Тройные ошибки QEMU при включении пейджинга

Я внедряю свое собственное ядро, и я застрял. Я пытаюсь загрузить свое ядро ​​​​в виртуальные адреса старшей половины. Я решил проблему идентификационных адресов, сопоставив идентификаторы с низким 1M ОЗУ. Я создал раздел init, который перемещен на физический адрес ядра, чтобы позаботиться об инициализации подкачки. Виртуальное смещение моего ядра равно 0xc0000000. Это мой скрипт компоновщика:

OUTPUT_FORMAT(elf32-i386)
ENTRY(start)

KERNEL_VIRTUAL_OFFSET = 0xC0000000;

SECTIONS
{
    . = 1M;
    kernel_start = .;

    start_init = .;
   .init ALIGN(4K) :
                          { *(.multiboot);
                            *(.init);
                            *(.tables);
                          }
    end_init = .;

    . += KERNEL_VIRTUAL_OFFSET;

    kernel_high_half_start = .;
   .text ALIGN(4K) :  AT(ADDR(.text) - KERNEL_VIRTUAL_OFFSET)
                         {*(.text) }
   .data ALIGN(4K) :  AT(ADDR(.data) - KERNEL_VIRTUAL_OFFSET)
                         { *(.data) }
   .rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_VIRTUAL_OFFSET)
                         { *(.rodata) }
   .bss  ALIGN(4K) :  AT(ADDR(.bss) - KERNEL_VIRTUAL_OFFSET)
                         { *(.bss)  }
    kernel_high_half_end = .;

    kernel_end = . - KERNEL_VIRTUAL_OFFSET;
}

Вот моя точка входа. Я использую GRUB в качестве загрузчика. Он успешно загружается и переходит в мою точку входа из-за раздела init:

    its 32
    section .multiboot
    ;grub bootloader header
            align 4
            dd 0x1BADB002            ;magic
            dd 0x00                  ;flags
            dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero

    ; Declarations
    global start
    extern kmain
    extern paging_init
    extern kernel_page_directory

    section .init

    enable_paging:
      mov eax, kernel_page_directory
      mov cr3, eax
      mov eax, cr0
      or eax, 0x80000000
      mov cr0, eax  ; ***** PAGING ENABLED HERE *****
      ret

    start:
      cli           ;block interrupts
      mov esp, init_stack
      call paging_init
      call enable_paging

      ;mov eax, 0xb8000
      ;mov byte[eax], 'h'
      ;mov byte[eax+1], 0x7

      ; Now high half kernel is mapped to the page directory
      mov esp, stack_space  ;set stack pointer
      push ebx ; grub boot info
      call kmain

    loop:
      hlt           ;halt the CPU
      jmp loop

    resb 4096; 4KB small stack for my init section.
    init_stack:

    section .bss
    resb 8192 ;8KB for stack
    stack_space:

Вот мой код, который заполняет таблицу страниц и каталог страниц ядра. Как видите, весь код слинкован в секцию init, чтобы избежать проблем с перемещением:

page_table_t kernel_page_directory[PAGE_DIR_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

page_pointer_t kernel_page_tables[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

page_pointer_t identity_page_table[PAGE_TABLE_SIZE]
__attribute__((aligned(PAGE_SIZE))) __attribute__((section(".tables"))) = {0};

/* Identity map the low 1M
 * In early boot stage.
 */
static void __attribute__((section(".init"))) map_identity()
{
   //map bios
   unsigned int current_page = 0;
   for(int i = 0; i < BIOS_PAGE_TABLE_ENTRIES; i++, current_page += PAGE_SIZE)
   {
      identity_page_table[i] = (current_page) | 0x3;
   }

   //map init
   current_page = INIT_START;
   for(int i = INIT_START >> 12 & 0x3FF;
       i < ((INIT_START >> 12 & 0x3FF) + (INIT_SIZE / PAGE_SIZE));
       i++, current_page += PAGE_SIZE)
   {
      identity_page_table[i] = (current_page) | 0x3;
   }

   kernel_page_directory[0] = ((unsigned long)(identity_page_table)) | 0x3;
}

/* Map the kernel memory to its page directory,
 * **in early boot stage.
 * We don't need to map the init section, we don't need it anymore.
 */

__attribute__((section(".init"))) static void map_kernel_memory()
{
   //Identity map the init section
   //Start at 1MB i.e. its page aligned.
   unsigned int start_index = 256;
   unsigned long current_page = KERNEL_START;

   for(int i = start_index;
       i < start_index + (KERNEL_SIZE / PAGE_SIZE) + 1;
       i++, current_page += PAGE_SIZE)
   {
       kernel_page_tables[i] = current_page | 0x3;
   }

   kernel_page_directory[KERNEL_DIRECTORY_ENTRY] = ((unsigned long)kernel_page_tables) | 0x3;
}

__attribute__((section(".init"))) void paging_init()
{
   map_identity();

   map_kernel_memory();
}

Я пытался указать точную инструкцию по сборке, но из-за этого у меня ядро ​​работает некорректно, и я думаю, что это из-за mov cr0, eax при включении подкачки. CR3 действительно содержит адрес kernel_page_directory или с 0x3. Как только я включаю подкачку, QEMU перестает отвечать и система постоянно перезагружается. Экран очищается, а затем повторно печатается. Есть идеи, почему это происходит? Как я могу это исправить?


person Delights    schedule 02.07.2016    source источник
comment
Что может помочь, так это предоставить минимальный полный проверяемый пример. Вы не предоставили весь свой файл C (он неполный) и не предоставили определения, которые используете. Я могу догадаться, что это за некоторые из них, но мы не знаем, правильно ли они определены или нет. Предоставьте достаточно кода, чтобы каждый мог самостоятельно скомпилировать/собрать/связать его. Также полезно — какие на самом деле команды вы используете для компиляции/сборки/связывания? Это тоже часто бывает ценным в подобном вопросе.   -  person Michael Petch    schedule 02.07.2016
comment
@MichaelPetch После ужасной ночи отладки я пришел к решению. Проблема заключалась в том, что я использовал GDT GRUB вместо своего собственного. Я предполагаю, что один из линейных адресов, на который я ссылался, не был отображен в моем каталоге страниц, и это вызвало ошибку страницы или неопределенное поведение.   -  person Delights    schedule 03.07.2016
comment
В спецификации мультизагрузки в значительной степени говорится об этом. Соответствующая цитата: GDTR Несмотря на то, что регистры сегментов настроены, как описано выше, «GDTR» может быть недействительным, поэтому образ ОС не должен загружать какие-либо регистры сегментов (даже просто перезагружая те же самые регистры). значений!) пока не установит собственный GDT.   -  person Michael Petch    schedule 04.07.2016
comment
@Delights Привет, я пытаюсь решить аналогичную проблему, но тоже застреваю. Есть ли у вас где-нибудь (Github/Gitlab) код вашей ОС, чтобы заглянуть внутрь и найти вдохновение для решения проблем, которые у меня есть? Ссылка или .tar будут оценены.... Заранее спасибо   -  person Martin Kopecký    schedule 01.08.2020


Ответы (1)


Является ли адрес вашего каталога страниц выровненным по страницам? Размер каждой страницы (фрейма) 4 КБ. Я предлагаю создать структуру для каталога страниц следующим образом:

typedef struct page_directory{
    page_table_t *tables[1024];
    size_t tablesPhysical[1024]; // Physical address of page tables
    size_t physicalAddr;         // Physical address of `tablesPhysical'
} page_directory_t;

Итак, ваш адрес каталога должен быть кратен 4 КБ (0x1000). Вам может помочь Учебник Джеймса Моллоя.

person Travor Liu    schedule 08.03.2018
comment
Он использует __attribute__((aligned(PAGE_SIZE))) . Из остального кода можно сделать вывод, что PAGE_SIZE равен 4096. Таким образом, атрибут выравнивания, который он использует, выравнивается по границе 4 КБ. В комментариях он упоминает, что нашел свою проблему, и она была связана с использованием GDT GRUB, а не с тем, что он сделал. - person Michael Petch; 08.03.2018