Почему у меня не работает страничная адресация в операционной системе x86_64?

Вопрос или проблема

Я хочу реализовать PML4 4K-страничную адресацию в своей ОС x86_64, поэтому сначала напишу следующие структуры

struct PML4{
    uint64_t p      :1;
    uint64_t rw     :1;
    uint64_t us     :1;
    uint64_t pwt    :1;
    uint64_t pcd    :1;
    uint64_t avl_1  :1;
    uint64_t rsvd_1 :3;
    uint64_t avl_2  :3;
    uint64_t tab_addr :28;
    uint64_t rsvd_2 :12;
    uint64_t avl_3  :7;
    uint64_t avl_4  :4;
    uint64_t xd     :1;
}__attribute__((packed));
typedef struct PML4 pml4_t;

struct PDPT{
    uint64_t p      :1;
    uint64_t rw     :1;
    uint64_t us     :1;
    uint64_t pwt    :1;
    uint64_t pcd    :1;
    uint64_t avl_1  :1;
    uint64_t rsvd_1 :3;
    uint64_t avl_2  :3;
    uint64_t tab_addr :28;
    uint64_t rsvd_2 :12;
    uint64_t avl_3  :7;
    uint64_t avl_4  :4;
    uint64_t xd     :1;
}__attribute__((packed));
typedef struct PDPT pdpt_t;

struct pd{
    uint64_t p      :1;
    uint64_t rw     :1;
    uint64_t us     :1;
    uint64_t pwt    :1;
    uint64_t pcd    :1;
    uint64_t avl_1  :1;
    uint64_t rsvd_1 :3;
    uint64_t avl_2  :3;
    uint64_t tab_addr :28;
    uint64_t rsvd_2 :12;
    uint64_t avl_3  :7;
    uint64_t avl_4  :4;
    uint64_t xd     :1;
}__attribute__((packed));
typedef struct pd pd_t;

struct pt{
    uint64_t p          :1;
    uint64_t rw         :1;
    uint64_t us         :1;
    uint64_t pwt        :1;
    uint64_t pcd        :1;
    uint64_t a          :1;
    uint64_t d          :1;
    uint64_t pat        :1;
    uint64_t g          :1;
    uint64_t avl_1      :3;
    uint64_t page_addr  :28;
    uint64_t rsvd       :12;
    uint64_t avl_2      :7;
    uint64_t avl_3      :4;
    uint64_t xd         :1;
}__attribute__((packed));
typedef struct pt pt_t;

и также загрузить таблицу pml4 в регистр cr3, используя следующую функцию load_cr3

section .text
global load_cr3

; Функция для установки CR3 на указанный адрес (передан из C в качестве аргумента)
load_cr3:
    mov rax, rdi               ; Загрузить первый аргумент (PML4_BASE_ADDRESS) в RAX
    mov cr3, rax               ; Установить CR3 на адрес в RAX
    ret

и попробовать инициализировать базовую страничную адресацию с помощью следующей функции init

pml4_t  pml4_table[512];
pdpt_t  pdpt_table[512];
pd_t    pd_table[512];
pt_t    pt_table[512];

void init_paging(){
    print("Инициализация страничной адресации...\n");

    // Настройка одной записи в качестве примера для отображения ядра верхней половины.
    pml4_table[511].p = 1;  // Используя последнюю запись PML4 для сопоставления верхней половины
    pml4_table[511].rw = 1;
    pml4_table[511].tab_addr = ((uint64_t)pdpt_table) >> 12;

    pdpt_table[511].p = 1;
    pdpt_table[511].rw = 1;
    pdpt_table[511].tab_addr = ((uint64_t)pd_table) >> 12;

    pd_table[511].p = 1;
    pd_table[511].rw = 1;
    pd_table[511].tab_addr = ((uint64_t)pt_table) >> 12;

    pt_table[0].p = 1;
    pt_table[0].rw = 1;
    pt_table[0].page_addr = kernel_phys_base >> 12; // Физический адрес ядра

    // Прежде чем мы включим страничную адресацию, мы должны зарегистрировать наш обработчик ошибок страниц.
    disable_interrupts();
    interrupt_install_handler(14, &page_fault_handler);
    enable_interrupts();

    load_cr3((uint64_t) &pml4_table);
    print("Страничная адресация инициализирована.\n");
}

Я знаю, что мой gdt и прерывания работают нормально. Но когда я пытаюсь использовать init_paging(); в ядре, это просто останавливает мое ядро, останавливая другую функциональность, не вызывая краха… Что здесь не так?

Ссылка на GitHub

Я пытался напечатать внутри функции init_paging() и увидел, что после вызова load_cr3 возникла проблема с загрузкой. Я также пытался

void test_paging(){
    uint64_t* test_address = (uint64_t*)0xFFFFFFFF80000000;  // Виртуальный адрес верхней половины
    *test_address = 0x12345678ABCDEF00;

    uint64_t* invalid_address = (uint64_t*)0xFFFFFFFF90000000;  // Неотображаемый адрес
    *invalid_address = 0x0;  // Это должно вызвать ошибку страницы
}

функция для отладки, но ничего не показала.

Ответ или решение

Ваше вопрос о проблемах с реализацией 4K пейджинга в вашей операционной системе на x86_64 архитектуре важен, и давайте подробно разберем возможные причины, по которым инициализация пейджинга может не работать как ожидается.

1. Корректность структуры таблиц

Первое, что стоит проверить — это корректность определения структур таблиц пейджинга. На первый взгляд, ваша реализация структур PML4, PDPT, PD и PT выглядит правильно. Тем не менее, стоит обратить особое внимание на следующее:

  • Размеры и выравнивание: Вы используете директиву __attribute__((packed)), что может привести к неправильному выравниванию структур. Убедитесь, что структура имеет необходимое выравнивание для использования процессором. Важно, чтобы размеры ваших структур были кратны 8 байтам.

  • Значения полей: При настройке PML4, PDPT, PD и PT убедитесь, что каждое поле устанавливается правильно, и не заняты ли неверные биты, особенно те, которые отвечают за разрешение доступа (p, rw, us).

2. Правильная инициализация таблиц

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

Обратите внимание на следующее:

  • Ваша таблица страниц (PT) указывает на физическую страницу, которая должна быть доступна. Убедитесь, что физическая страница, на которую указывает kernel_phys_base, реально существует и доступна.
  • Проверьте, действительно ли вы хотите использовать последнюю запись (511) для PML4 и иерархии таблиц. Часто используется меньший индекс, в зависимости от архитектуры.

3. Загрузка CR3

Ваше использование функции load_cr3 также требует проверки:

  • Убедитесь, что передаете правильный адрес. Ваша функция load_cr3 принимает указатель на pml4_table, но в самом начале инициализации вы, возможно, не корректно инициализировали этот адрес.
  • Используйте правильный адрес, который соответствует физической странице, содержащей PML4. Это требуется для корректной работы пейджинга.

4. Обработка прерываний и обработчика страниц

Вы перенаправляете обработку прерываний до инициализации пейджинга, это может не позволить вам обработать ошибки правильно:

  • Убедитесь, что ваш обработчик страниц (page_fault_handler) корректно реализован и может обработать как исправные, так и гарантированные ошибки. В противном случае, как Вы сами заметили, ваша система может "зависнуть" без обработки исключений.

5. Тестирование пейджинга

Ваш код для тестирования пейджинга служит хорошей идеей, однако учитывайте следующее:

  • При попытке записи по адресу 0xFFFFFFFF90000000 в действительности должен произойти fault, и ваша система должна обработать это. Если же этого не происходит, возможно, дело в некорректной инициализации вашего обработчика.

Заключение

Представленные моменты должны помочь исправить ваши проблемы с пейджингом в вашем проекте. Советуем следить за отладочной информацией и логами для выявления проблемы. Если после внесения исправлений проблема все еще сохраняется, возможно, потребуется провести глубокую отладку, используя отладочные инструменты, такие как QEMU или GDB, для отслеживания состояния системы после инициации пейджинга.

Оцените материал
Добавить комментарий

Капча загружается...