OS dev: тройная ошибка при попытке включения подкачки - PullRequest
0 голосов
/ 16 февраля 2020

Я создаю простую ОС для учебных целей, и я (в настоящее время; я следовал различным учебникам ранее и что-то настраивал самостоятельно) следую этому учебнику для включения подкачки страниц. Я использую QEMU вместо Bochs в качестве моего эмулятора.

Если я не буду использовать подкачку, все будет работать нормально (даже тот самый базовый c kmalloc(), который я реализовал), но как только я установлю PG бит в регистре cr0 (т. е. включить подкачку страниц), все вылетает и QEMU перезагружается: я подозреваю, что некоторые из имеющихся у меня структур (т. е. каталог страниц, таблицы страниц и т. д.) не созданы или не загружены должным образом, но у меня нет возможности проверить.

Я уже давно пытаюсь решить эту проблему, но не нашел решения. Кто-нибудь может увидеть, где моя ошибка?

Здесь вы можете найти мой полный код: https://github.com/davidedellagiustina/ScratchOS (commit 83b5c8c). Код подкачки находится в src/cpu/paging.*.

Редактировать: Настройка каталога страниц super-basi c, точно следуя этому учебнику , приводит к созданию рабочего кода. Основываясь на этом простом примере, я пытаюсь построить более сложные структуры (например, page_t, page_table_t, page_directory_t), чтобы понять ошибку.

Ответы [ 2 ]

3 голосов
/ 16 февраля 2020

В общем:

  • указатели должны быть только для виртуальных адресов (и никогда не должны использоваться для физических адресов)

  • физические адреса должны возможно, вы используете typedef (например, typedef uint32_t phys_address_t), чтобы позже (когда вы хотите поддерживать PAE / Physical Address Extensions) вы могли изменить тип (например, вместо этого использовать typedef uint64_t phys_address_t), не нарушая все. Это также означает, что вы получаете предупреждения / ошибки во время компиляции, когда вы делаете глупые ошибки (например, используя виртуальный адрес / указатель, где вам нужен физический адрес / целое число без знака).

  • почти все ядро должно использовать указатели / виртуальные адреса для всего. Физические адреса используются только некоторыми драйверами устройств (для мастеринга шины / DMA) и для самого управления физической памятью (для выделения физических страниц для таблиц страниц и т. Д .; перед отображением их в виртуальное адресное пространство). Это включает в себя управление памятью высокого уровня («kmallo c ()» должно возвращать указатель void *, а не физический адрес).

  • во время загрузки, есть небольшой период времени когда ни один из нормальных кодов ядра не может работать, потому что он использует виртуальные адреса, и подкачка еще не была инициализирована. Чтобы минимизировать размер этого периода времени (и дублирование кода, вызванное наличием двух версий функций - одной для «до инициализации подкачки» и другой для «после инициализации подкачки»), вы хотите инициализировать подкачку как можно скорее; либо с выделенным фрагментом кода запуска на ассемблере, который выполняется перед «main ()» (возможно, с использованием «статически выделенной во время компиляции» памяти в разделе «.bss» ядра для каталога страниц и таблиц страниц), либо в загрузке сам загрузчик (который чище и мощнее / гибче). Такие вещи, как установка допустимого стека ядра и инициализация (физической, виртуальной, а затем кучи) управления памятью, могут / должны подождать до инициализации подкачки.

  • для отображения идентификаторов; вам потребуется только 2 цикла (один для создания записей каталога страниц, а другой для создания всех записей таблицы страниц), где оба цикла могут быть такими (только с разными начальными значениями в eax, ecx и edi) :

    .nextEntry:
        stosd
        add eax,0x00001000
        loop .nextEntry
    
  • отображение идентичности не велико. Обычно вы хотите, чтобы ядро ​​располагало высоким виртуальным адресом (например, 0xC0000000) с областью «намеренно не используемой для перехвата указателей NULL» в 0x0000000, а пользовательское пространство (процессы и т. Д. c) использует обычные виртуальные адреса между ними (например, возможно, начиная с виртуального адреса 0x00400000). Это делает его раздражающим для кода, который инициализирует пейджинг и скрипт компоновщика ядра (именно поэтому лучше инициализировать пейджинг в загрузчике и избежать путаницы в ядре). Для этого случая; вам нужно будет временно идентифицировать карту одной страницы (страницу, содержащую окончательный «mov cr0», который включает подкачку страниц, и jmp kernel_entry, который передает управление коду / ядру по более высокому адресу), и вы захотите удалить эту временную идентификацию Отображаемая страница после запуска основного ядра.

  • вам нужно будет "очень хорошо ознакомиться" с возможностями отладки вашего эмулятора. Qemu имеет журнал, который может предоставить очень полезные подсказки, и включает в себя встроенный монитор, который предлагает различные команды (см. https://en.wikibooks.org/wiki/QEMU/Monitor). Вы должны иметь возможность заменить «mov cr0» (который включает подкачку страниц) бесконечным l oop (.die: jmp die), а затем использовать монитор, чтобы остановить эмулятор после того, как он достигнет бесконечного l oop, и проверить все (содержимое cr3, содержимое физической памяти) и выясните, что не так с записями каталога страниц или таблицы страниц (и выполните аналогичные действия сразу после включения подкачки, чтобы проверить виртуальное адресное пространство, прежде чем ваш код что-либо с ним сделает). Qemu также позволяет подключать удаленный отладчик (GDB).

0 голосов
/ 17 февраля 2020

Я обнаружил, что мне не хватает всех флагов в записях каталога страниц (особенно в режимах чтения / записи и режима ядра), поскольку я помещал туда только адрес таблицы страниц. Я оставлю свой репозиторий в открытом доступе c и продолжу разработку с этого момента, если кому-то понадобится в будущем.

Редактировать: Кроме того, я забыл инициализировать все страниц (с адресом и битом присутствия) при создании новой таблицы страниц.

...