긴 이야기 짧게

긴 이야기 짧게

배경: 가상화를 위해 QEMU를 사용하고 있습니다.xv6-riscvWSL2 위에 있습니다. 저는 Linux 명령을 연상시키는 일종의 깔끔한 운영 체제 종료 프로세스를 만들려고 합니다 exit. 현재 나는 qemu를 종료하기 위해 Ctrl-를 사용하고 있지만 a x운영 체제 내에서 프로그래밍 방식으로 이 작업을 수행할 수 있기를 바랍니다.

나는 Linux exit명령이 현재 쉘을 닫는다는 것을 알고 있습니다. 즉, 활성 쉘이 남아 있는지 감지하는 커널 공간 방법과 QEMU가 닫히고 있음을 알리는 방법이 필요하다는 것을 의미합니다. 재컴파일하기 위해 커널에 대한 전체 액세스 권한이 있지만 외부 C 라이브러리가 없기 때문에 사용할 수 없습니다. 제가 개발한 솔루션은 기본적으로 처음부터 시작해야 합니다.

그래서 내 질문은 다음과 같습니다RISC-V QEMU 인스턴스에 종료 중임을 알리기 위해 운영 체제는 무엇을 해야 합니까? 운영 체제에서 QEMU 자체를 종료하는 방법은 무엇입니까?두 번째 점은 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);

간단히 말해서, 이 코드 조각은 레지스터 맵의 0번째 인덱스가 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에 추가한 다음 이 응용 프로그램을 사용하여 운영 체제를 종료합니다.

내가 말했듯이, 당신은 볼 수 있습니다이번에 제출하세요완전한 구현을 위해.

관련 정보