Почему QEMU возвращает неправильные адреса при заполнении старшей половины PML4?

avatar
user123
9 августа 2021 в 05:02
113
1
0

Я пишу небольшую ОС x86-64, которую загружаю с UEFI. Я пытаюсь сделать ядро ​​более высокой половиной ядра, переместив исполняемый файл ядра на 0x800000000000. Этот адрес должен находиться на полпути к PML4. По сути, я должен заполнить запись 256 PML4, чтобы обратиться к этой высшей половине. Я пытался это сделать, но мой код был тройным. Поскольку я тестирую ядро ​​на QEMU и отлаживаю его с помощью GDB, я использовал monitor info mem в GDB, чтобы увидеть сопоставление виртуальных и физических адресов. Он вернул следующее:

(gdb) monitor info mem
0000000000000000-0000000000400000 0000000000400000 -rw
ffff800000000000-ffff800000c00000 0000000000c00000 -rw

Вместо сопоставления 0x800000000000 было сопоставлено ffff800000000000. Вероятно, поэтому код выдает тройную ошибку, когда я перехожу к более высокой половине. Вот небольшой пример кода, который у меня есть:

typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef unsigned long UINT64;

struct GDT{
    UINT64 nullDescriptor;
    
    UINT16 codeLimit;
    UINT16 codeBaseLow;
    UINT8 codeBaseMid;
    UINT8 codeFlags;
    UINT8 codeLimitMid;
    UINT8 codeBaseHigh;
    
    UINT16 dataLimit;
    UINT16 dataBaseLow;
    UINT8 dataBaseMid;
    UINT8 dataFlags;
    UINT8 dataLimitMid;
    UINT8 dataBaseHigh;
}__attribute__((packed));

struct GDTR{
    UINT16 size;
    GDT* address;
}__attribute__((packed));

void main(){
    //Identity mapping for the first 4MB
    UINT64* pml4Ptr = (UINT64*)0x200000;
    *pml4Ptr = 0x20101b;
    
    UINT64* pdpPtr = (UINT64*)0x201000;
    *pdpPtr = 0x20201b;
    
    UINT64* pdPtr = (UINT64*)0x202000;
    *pdPtr = 0x20301b;
    *(pdPtr + 1) = 0x20401b;
    
    UINT64* ptPtr = (UINT64*)0x203000;
    UINT64 physAddr = 0x1b;
    for (UINT32 i = 0; i < 2 * 512; i++){
        *(ptPtr + i) = physAddr;
        physAddr += 0x1000;
    }
    
    //Kernel mapping for the higher half
    *(pml4Ptr + 256) = 0x20601b; //When this is less then 256 I get the right addresses
    
    pdpPtr = (UINT64*)0x206000;
    *pdpPtr = 0x20701b;
    
    pdPtr = (UINT64*)0x207000;
    *pdPtr = 0x20801b;
    *(pdPtr + 1) = 0x20901b;
    *(pdPtr + 2) = 0x20a01b;
    *(pdPtr + 3) = 0x20b01b;
    *(pdPtr + 4) = 0x20c01b;
    *(pdPtr + 5) = 0x20d01b;
    
    ptPtr = (UINT64*)0x208000;
    physAddr = 0x1b;
    for (UINT32 i = 0; i < 6 * 512; i++){
        *(ptPtr + i) = physAddr;
        physAddr += 0x1000;
    }

    asm volatile(
    "movq $0x200018, %rax\n\t"
    "mov %rax, %cr3\n\t"
    "movq $0x375000, %rsp\n\t"
    );
        
    GDT gdt = {
        .nullDescriptor = 0,
        
        .codeLimit = 0x0000,
        .codeBaseLow = 0,
        .codeBaseMid = 0,
        .codeFlags = 0x9a,
        .codeLimitMid = 0xaf,
        .codeBaseHigh = 0,
        
        .dataLimit = 0x0000,
        .dataBaseLow = 0,
        .dataBaseMid = 0,
        .dataFlags = 0x92,
        .dataLimitMid = 0x00,
        .dataBaseHigh = 0
    };
    
    GDT* gdtAddr = &gdt;
    GDTR gdtr = { 23, gdtAddr };
    GDTR* gdtrAddr = &gdtr;
    
    asm volatile("lgdt (%0)" : : "r"(gdtrAddr));
    
    asm volatile(
    "sub $16, %rsp\n\t"
    "movq $8, 8(%rsp)\n\t"
    "movabsq $fun, %rax\n\t"
    "mov %rax, (%rsp)\n\t"
    "lretq\n\t"
    "fun:\n\t"
    "movq $0x10, %rax\n\t"
    "mov %ax, %ss\n\t"
    "mov %ax, %es\n\t"
    "mov %ax, %ds\n\t"
    "mov %ax, %gs\n\t"
    "mov %ax, %fs\n\t"
    "hlt"
    );
}

У меня есть указатель на адрес PML4, затем я делаю *(pml4Ptr + 256) = 0x20601b;. Это должно сопоставить запись 256 PML4 с 0x206000 без кэширования. Остальная часть небольшого фрагмента кода в разделе отображения ядра должна отображать 12 МБ данных в старшей половине виртуального адреса, начиная с физического адреса 0. Вместо этого я получаю адреса выше, что кажется странным.

Если я установлю запись 255 PML4 с тем же кодом (*(pml4Ptr + 255) = 0x20601b;), я получу следующий результат:

(gdb) monitor info mem
0000000000000000-0000000000400000 0000000000400000 -rw
00007f8000000000-00007f8000c00000 0000000000c00000 -rw

Я действительно получаю правильные адреса!? Есть ли известная ошибка, из-за которой QEMU неправильно обращается к PML4, когда вы заполняете старшую половину, или есть что-то, что я упустил из виду в своем коде?

Я также просмотрел таблицы страниц напрямую (поскольку они размещаются в статических позициях в ОЗУ). Я получаю следующий результат:

user@user-System-Product-Name:~$ hexdump -C result.bin
00000000  3b 10 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |;. .............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000800  1b 60 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |.` .............|
00000810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000  3b 20 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |;  .............|
00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00002000  1b 30 20 00 00 00 00 00  3b 40 20 00 00 00 00 00  |.0 .....;@ .....|
00002010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00003000  1b 00 00 00 00 00 00 00  1b 10 00 00 00 00 00 00  |................|
00003010  1b 20 00 00 00 00 00 00  1b 30 00 00 00 00 00 00  |. .......0......|
00003020  1b 40 00 00 00 00 00 00  1b 50 00 00 00 00 00 00  |.@.......P......|
00003030  1b 60 00 00 00 00 00 00  1b 70 00 00 00 00 00 00  |.`.......p......|
00003040  1b 80 00 00 00 00 00 00  1b 90 00 00 00 00 00 00  |................|
00003050  1b a0 00 00 00 00 00 00  1b b0 00 00 00 00 00 00  |................|
00003060  1b c0 00 00 00 00 00 00  1b d0 00 00 00 00 00 00  |................|
00003070  1b e0 00 00 00 00 00 00  1b f0 00 00 00 00 00 00  |................|
00003080  1b 00 01 00 00 00 00 00  1b 10 01 00 00 00 00 00  |................|
00003090  1b 20 01 00 00 00 00 00  1b 30 01 00 00 00 00 00  |. .......0......|
000030a0  1b 40 01 00 00 00 00 00  1b 50 01 00 00 00 00 00  |.@.......P......|
000030b0  1b 60 01 00 00 00 00 00  1b 70 01 00 00 00 00 00  |.`.......p......|
000030c0  1b 80 01 00 00 00 00 00  1b 90 01 00 00 00 00 00  |................|
000030d0  1b a0 01 00 00 00 00 00  1b b0 01 00 00 00 00 00  |................|
000030e0  1b c0 01 00 00 00 00 00  1b d0 01 00 00 00 00 00  |................|
000030f0  1b e0 01 00 00 00 00 00  1b f0 01 00 00 00 00 00  |................|
00003100  1b 00 02 00 00 00 00 00  1b 10 02 00 00 00 00 00  |................|
00003110  1b 20 02 00 00 00 00 00  1b 30 02 00 00 00 00 00  |. .......0......|
00003120  1b 40 02 00 00 00 00 00  1b 50 02 00 00 00 00 00  |.@.......P......|
00003130  1b 60 02 00 00 00 00 00  1b 70 02 00 00 00 00 00  |.`.......p......|
00003140  1b 80 02 00 00 00 00 00  1b 90 02 00 00 00 00 00  |................|
00003150  1b a0 02 00 00 00 00 00  1b b0 02 00 00 00 00 00  |................|
00003160  1b c0 02 00 00 00 00 00  1b d0 02 00 00 00 00 00  |................|
00003170  1b e0 02 00 00 00 00 00  1b f0 02 00 00 00 00 00  |................|
00003180  1b 00 03 00 00 00 00 00  1b 10 03 00 00 00 00 00  |................|
00003190  1b 20 03 00 00 00 00 00  1b 30 03 00 00 00 00 00  |. .......0......|
000031a0  1b 40 03 00 00 00 00 00  1b 50 03 00 00 00 00 00  |.@.......P......|
000031b0  1b 60 03 00 00 00 00 00  1b 70 03 00 00 00 00 00  |.`.......p......|
000031c0  1b 80 03 00 00 00 00 00  1b 90 03 00 00 00 00 00  |................|
000031d0  1b a0 03 00 00 00 00 00  1b b0 03 00 00 00 00 00  |................|
000031e0  1b c0 03 00 00 00 00 00  1b d0 03 00 00 00 00 00  |................|
000031f0  1b e0 03 00 00 00 00 00  1b f0 03 00 00 00 00 00  |................|
00003200  1b 00 04 00 00 00 00 00  1b 10 04 00 00 00 00 00  |................|
00003210  1b 20 04 00 00 00 00 00  1b 30 04 00 00 00 00 00  |. .......0......|
00003220  1b 40 04 00 00 00 00 00  1b 50 04 00 00 00 00 00  |.@.......P......|
00003230  1b 60 04 00 00 00 00 00  1b 70 04 00 00 00 00 00  |.`.......p......|
00003240  1b 80 04 00 00 00 00 00  1b 90 04 00 00 00 00 00  |................|
00003250  1b a0 04 00 00 00 00 00  1b b0 04 00 00 00 00 00  |................|
00003260  1b c0 04 00 00 00 00 00  1b d0 04 00 00 00 00 00  |................|
00003270  1b e0 04 00 00 00 00 00  1b f0 04 00 00 00 00 00  |................|
00003280  1b 00 05 00 00 00 00 00  1b 10 05 00 00 00 00 00  |................|
...
...
00004fa0  1b 40 3f 00 00 00 00 00  1b 50 3f 00 00 00 00 00  |.@?......P?.....|
00004fb0  1b 60 3f 00 00 00 00 00  1b 70 3f 00 00 00 00 00  |.`?......p?.....|
00004fc0  1b 80 3f 00 00 00 00 00  1b 90 3f 00 00 00 00 00  |..?.......?.....|
00004fd0  1b a0 3f 00 00 00 00 00  1b b0 3f 00 00 00 00 00  |..?.......?.....|
00004fe0  1b c0 3f 00 00 00 00 00  1b d0 3f 00 00 00 00 00  |..?.......?.....|
00004ff0  1b e0 3f 00 00 00 00 00  1b f0 3f 00 00 00 00 00  |..?.......?.....|
00005000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00006000  1b 70 20 00 00 00 00 00  00 00 00 00 00 00 00 00  |.p .............|
00006010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00007000  1b 80 20 00 00 00 00 00  1b 90 20 00 00 00 00 00  |.. ....... .....|
00007010  1b a0 20 00 00 00 00 00  1b b0 20 00 00 00 00 00  |.. ....... .....|
00007020  1b c0 20 00 00 00 00 00  1b d0 20 00 00 00 00 00  |.. ....... .....|
00007030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00008000  1b 00 00 00 00 00 00 00  1b 10 00 00 00 00 00 00  |................|
00008010  1b 20 00 00 00 00 00 00  1b 30 00 00 00 00 00 00  |. .......0......|
00008020  1b 40 00 00 00 00 00 00  1b 50 00 00 00 00 00 00  |.@.......P......|
00008030  1b 60 00 00 00 00 00 00  1b 70 00 00 00 00 00 00  |.`.......p......|
00008040  1b 80 00 00 00 00 00 00  1b 90 00 00 00 00 00 00  |................|
00008050  1b a0 00 00 00 00 00 00  1b b0 00 00 00 00 00 00  |................|
00008060  1b c0 00 00 00 00 00 00  1b d0 00 00 00 00 00 00  |................|
00008070  1b e0 00 00 00 00 00 00  1b f0 00 00 00 00 00 00  |................|
00008080  1b 00 01 00 00 00 00 00  1b 10 01 00 00 00 00 00  |................|
00008090  1b 20 01 00 00 00 00 00  1b 30 01 00 00 00 00 00  |. .......0......|
000080a0  1b 40 01 00 00 00 00 00  1b 50 01 00 00 00 00 00  |.@.......P......|
000080b0  1b 60 01 00 00 00 00 00  1b 70 01 00 00 00 00 00  |.`.......p......|
000080c0  1b 80 01 00 00 00 00 00  1b 90 01 00 00 00 00 00  |................|
000080d0  1b a0 01 00 00 00 00 00  1b b0 01 00 00 00 00 00  |................|
000080e0  1b c0 01 00 00 00 00 00  1b d0 01 00 00 00 00 00  |................|
000080f0  1b e0 01 00 00 00 00 00  1b f0 01 00 00 00 00 00  |................|
00008100  1b 00 02 00 00 00 00 00  1b 10 02 00 00 00 00 00  |................|
00008110  1b 20 02 00 00 00 00 00  1b 30 02 00 00 00 00 00  |. .......0......|
00008120  1b 40 02 00 00 00 00 00  1b 50 02 00 00 00 00 00  |.@.......P......|
00008130  1b 60 02 00 00 00 00 00  1b 70 02 00 00 00 00 00  |.`.......p......|
00008140  1b 80 02 00 00 00 00 00  1b 90 02 00 00 00 00 00  |................|
00008150  1b a0 02 00 00 00 00 00  1b b0 02 00 00 00 00 00  |................|
00008160  1b c0 02 00 00 00 00 00  1b d0 02 00 00 00 00 00  |................|
00008170  1b e0 02 00 00 00 00 00  1b f0 02 00 00 00 00 00  |................|
00008180  1b 00 03 00 00 00 00 00  1b 10 03 00 00 00 00 00  |................|
00008190  1b 20 03 00 00 00 00 00  1b 30 03 00 00 00 00 00  |. .......0......|
000081a0  1b 40 03 00 00 00 00 00  1b 50 03 00 00 00 00 00  |.@.......P......|
000081b0  1b 60 03 00 00 00 00 00  1b 70 03 00 00 00 00 00  |.`.......p......|
000081c0  1b 80 03 00 00 00 00 00  1b 90 03 00 00 00 00 00  |................|
000081d0  1b a0 03 00 00 00 00 00  1b b0 03 00 00 00 00 00  |................|
000081e0  1b c0 03 00 00 00 00 00  1b d0 03 00 00 00 00 00  |................|
000081f0  1b e0 03 00 00 00 00 00  1b f0 03 00 00 00 00 00  |................|
00008200  1b 00 04 00 00 00 00 00  1b 10 04 00 00 00 00 00  |................|
00008210  1b 20 04 00 00 00 00 00  1b 30 04 00 00 00 00 00  |. .......0......|
00008220  1b 40 04 00 00 00 00 00  1b 50 04 00 00 00 00 00  |.@.......P......|
00008230  1b 60 04 00 00 00 00 00  1b 70 04 00 00 00 00 00  |.`.......p......|
00008240  1b 80 04 00 00 00 00 00  1b 90 04 00 00 00 00 00  |................|
00008250  1b a0 04 00 00 00 00 00  1b b0 04 00 00 00 00 00  |................|
00008260  1b c0 04 00 00 00 00 00  1b d0 04 00 00 00 00 00  |................|
00008270  1b e0 04 00 00 00 00 00  1b f0 04 00 00 00 00 00  |................|
00008280  1b 00 05 00 00 00 00 00  1b 10 05 00 00 00 00 00  |................|
00008290  1b 20 05 00 00 00 00 00  1b 30 05 00 00 00 00 00  |. .......0......|
000082a0  1b 40 05 00 00 00 00 00  1b 50 05 00 00 00 00 00  |.@.......P......|
000082b0  1b 60 05 00 00 00 00 00  1b 70 05 00 00 00 00 00  |.`.......p......|
000082c0  1b 80 05 00 00 00 00 00  1b 90 05 00 00 00 00 00  |................|
000082d0  1b a0 05 00 00 00 00 00  1b b0 05 00 00 00 00 00  |................|
000082e0  1b c0 05 00 00 00 00 00  1b d0 05 00 00 00 00 00  |................|
000082f0  1b e0 05 00 00 00 00 00  1b f0 05 00 00 00 00 00  |................|
00008300  1b 00 06 00 00 00 00 00  1b 10 06 00 00 00 00 00  |................|
00008310  1b 20 06 00 00 00 00 00  1b 30 06 00 00 00 00 00  |. .......0......|
00008320  1b 40 06 00 00 00 00 00  1b 50 06 00 00 00 00 00  |.@.......P......|
00008330  1b 60 06 00 00 00 00 00  1b 70 06 00 00 00 00 00  |.`.......p......|
00008340  1b 80 06 00 00 00 00 00  1b 90 06 00 00 00 00 00  |................|
00008350  1b a0 06 00 00 00 00 00  1b b0 06 00 00 00 00 00  |................|
00008360  1b c0 06 00 00 00 00 00  1b d0 06 00 00 00 00 00  |................|
00008370  1b e0 06 00 00 00 00 00  1b f0 06 00 00 00 00 00  |................|
00008380  1b 00 07 00 00 00 00 00  1b 10 07 00 00 00 00 00  |................|
00008390  1b 20 07 00 00 00 00 00  1b 30 07 00 00 00 00 00  |. .......0......|
000083a0  1b 40 07 00 00 00 00 00  1b 50 07 00 00 00 00 00  |.@.......P......|
000083b0  1b 60 07 00 00 00 00 00  1b 70 07 00 00 00 00 00  |.`.......p......|
000083c0  1b 80 07 00 00 00 00 00  1b 90 07 00 00 00 00 00  |................|
000083d0  1b a0 07 00 00 00 00 00  1b b0 07 00 00 00 00 00  |................|
000083e0  1b c0 07 00 00 00 00 00  1b d0 07 00 00 00 00 00  |................|

Дамп кажется правильным. Он начинается с адреса 0x200000. Это означает, что адрес 0 в дампе — это 0x200000, адрес 0x1000 — это 0x201000 и т. д.

Я компилирую код с помощью этого скрипта:

g++ -fomit-frame-pointer --static -ffreestanding -nostdlib -mgeneral-regs-only -mno-red-zone -c -m64 Startup/Source/Main.cpp -oStartup/Object/Main.o
ld -entry main --oformat elf64-x86-64 --no-dynamic-linker -static -nostdlib -Ttext-segment=300000 Startup/Object/Main.o -ostartup.elf

Может ли кто-нибудь определить какие-либо проблемы с кодом или что-то, что я упустил из виду?

Источник

Ответы (1)

avatar
Peter Cordes
9 августа 2021 в 05:36
1

0x800000000000 не является каноническим адресом, поэтому для него нет места в макете каталога страниц. (И попытки разыменовать его приведут к ошибке #GP вместо запуска промаха TLB => обход страницы.)

У вас есть PML4, поэтому ваши 16 старших битов виртуального адреса должны быть копиями бита #47. то есть указатель должен быть представлен как 48-битное значение, расширенное до 64-битного знака.
то есть ((int64_t)addr << 16) >> 16 == addr должно быть истинным.
(Со сдвигом вправо с использованием sar арифметического сдвига вправо).
(канонический адрес x86-64?).

Старшая половина полезного (канонического) диапазона фактически начинается с младшая половина, 00007f8000000000.

Каноническая форма адреса и арифметика указателя включает ASCII-диаграмму дыры в виртуальном адресном пространстве.

Вы правы относительно того, где находится верхняя часть младшей половины, но вы забыли подписать-расширить 0x800000000000 до 64-бит, чтобы достичь нижней части старшей половины. Указатели по-прежнему номинально 64-битные, а не просто усеченные до 48-битных. Вот почему такие адреса, как ffff800000000000, вообще могут существовать.


Если у вас включен PML5 (например, оборудование Ice Lake или программная эмуляция этой функции), дополнительный уровень каталогов страниц даст вам до 57 бит виртуального адресного пространства, что позволит адресовать 0x800000000000.

См. также x86-64: канонические адреса и реальный доступный диапазон

user123
9 августа 2021 в 06:17
1

Большое спасибо. Я никогда не слышал об этом. Если я правильно понимаю, таблицы страниц правильные. QEMU просто читает таблицы страниц и подписывает, правильно расширяя результат, чего я не делал.