長い話を短く

長い話を短く

背景:仮想化にQEMUを使用しています。xv6-riscvWSL2の上にあります。私はLinuxコマンドを連想させるようなきちんとしたオペレーティングシステムのシャットダウンプロセスを作成しようとしていますexit。私は現在qemuを終了するためにCtrl-を使用していますが、a xオペレーティングシステム内でプログラム的にこれを行うことができることを願っています。

私はLinuxexitコマンドが現在シェルを閉じていることを知っています。つまり、アクティブシェルが残っているかどうかを検出するカーネル空間方法と、QEMUが閉じていることを知らせる方法が必要であることを意味します。再コンパイルするためにカーネルへのフルアクセス権がありますが、外部Cライブラリがないため使用できません。私が開発したソリューションは基本的に最初から始める必要があります。

だから私の質問は次のようになりますRISC-V QEMUインスタンスにシャットダウンしていることを知らせるために、オペレーティングシステムは何をすべきですか?オペレーティングシステムでQEMU自体をシャットダウンする方法は?2番目の点は、CLIのMakefileでQEMUをインスタンス化し、QEMUが何もせずにRISC-Vをエミュレートするのではなく、オペレーティングシステムがシャットダウンした後に端末にアクセスできるようにしたいので、特に重要です。

注:この質問は次の質問とは異なります。もう一つこれは、ホストが起動したACPIシャットダウンではなく、ゲストOSが起動したシャットダウンについて質問し、QEMUとOSの内部についてより深く理解するためです。

ベストアンサー1

長い話を短く

スーパーバイザーモードで:

(*(volatile uint32 *) 0x100000) = 0x5555;

また、アドレス0x100000をマップするようにカーネルページテーブルを変更する必要があります。あなたは見ることができます今回提出してください完全な実装のため。

長い答え

残念ながら、QEMU RISC-V エミュレータのドキュメントは不十分です。だから私たちはコードを掘り下げる必要があります。私たちはxv6で知っていますファイルの生成xv6を仮想化するには、QEMUの共通仮想プラットフォーム(virt)を使用します。

これvirt.cQEMUソースコードのファイルには、QEMUユニバーサル仮想プラットフォームの実装が含まれています。そのファイル内で検索すると、poweroff次のようになります。これコードセグメント:

name = g_strdup_printf("/poweroff");
qemu_fdt_add_subnode(ms->fdt, name);
qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff");
qemu_fdt_setprop_cell(ms->fdt, name, "regmap", test_phandle);
qemu_fdt_setprop_cell(ms->fdt, name, "offset", 0x0);
qemu_fdt_setprop_cell(ms->fdt, name, "value", FINISHER_PASS);
g_free(name);

簡単に言えば、このコードスニペットは、レジスタマップのゼロ番目のインデックスがFINISHER_PASS書き込まれると、QEMUにQEMUを終了するように指示します。test_phandle上記の行を見ると、FINISHER_RESET作成中にQEMUをリセットできることがわかります。この行の前の数行は、私たちが書く必要があるゲストのメモリマップされた場所を示していますFINISHER_PASS

qemu_fdt_setprop_cells(ms->fdt, name, "reg",
    0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size);

VIRT_TEST同じファイル内で検索すると、次のような結果が得られます。これワイヤー:

[VIRT_TEST] =         {   0x100000,        0x1000 },

したがって、私たちが書くべき場所はFINISHER_PASS0x100000です。また、プロジェクト全体で sum と を検索すると、そのFINISHER_PASS値を表示できます。FINISHER_RESETこのファイル:

enum {
    FINISHER_FAIL = 0x3333,
    FINISHER_PASS = 0x5555,
    FINISHER_RESET = 0x7777
};

簡単に言えば、我々は現在のオペレーティングシステムを終了するために0x100000アドレスに0x5555を書く必要があることを知っています。同じアドレスに0x7777を書くとOSを再起動できると思うかもしれませんが、なぜかそうすることはできませんね…理由を見つけたらぜひ教えてください!

それでは、xv6を終了するシステムコールを実装しましょう。まず、仮想アドレス0x100000が物理アドレス0x100000にマッピングされるように、カーネルのページテーブルを変更する必要があります(ASLRが実装されている場合は、他の仮想アドレスもマッピングできます)。私はand関数に次の行を追加することでvm.cこれをしましたkvmmake

kvmmap(kpgtbl, 0x100000, 0x100000, PGSIZE, PTE_R | PTE_W);

QEMUによると、領域サイズは0x1000バイトなので、ページサイズを使用します。源泉

次に、オペレーティングシステムにシステムコールを追加するだけです。これが私がする方法です:

// Power off QEMU
static uint64
sys_poweroff(void)
{
  printf("Powering off...\n");
  (*(volatile uint32 *) 0x100000) = 0x5555;
  panic("sys_poweroff");
}

このシステムコールをシステムコールテーブルに追加すると、起動します。

その後、システムコールを使用する単純なユーザープログラムを作成できます。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
  poweroff();
  exit(1); // never reached
}

このユーザープログラムをMakefileに追加し、このアプリケーションを使用してオペレーティングシステムを終了します。

私が言ったように、あなたは見ることができます今回提出してください完全な実装のため。

おすすめ記事