Страничная адресация intel x86
В процессе настройки страничной адресации столкнулся с проблемами.
Для их описания я буду использовать этот код на Си:
uint32_t pde[1024] __attribute__((aligned(4096)));
uint32_t first_pte[1024] __attribute__((aligned(4096)));
static void paging_set_page_directory(uint32_t pde_addr)
{
asm volatile ("mov %0, %%cr3"::"r"(pde_addr));
uint32_t cr0;
asm volatile ("mov %%cr0, %0": "=r"(cr0));
cr0 |= 0x80000000;
asm volatile ("mov %0, %%cr0":: "r"(cr0));
}
static void paging_init()
{
memset(&pde, 0, sizeof(pde));
memset(&first_pte, 0, sizeof(first_pte));
for(uint32_t i = 0; i < 1024; i++)
first_pte[i] = (i * 0x1000) | 1; //present, readonly, supervisor...
pde[0] = ((uint32_t)&first_pte) | 1; //present, readonly, supervisor...
paging_set_page_directory((uint32_t)&pde);
}
Все тесты работоспособности я проводил на виртуальной машине VirtualBox. Во всех случаях под ОШИБКОЙ я подразумеваю критическую ошибку VirtualBox.
Почему:
- Я не могу использовать каталог без страниц. Т.е. при таком изменении цикла
for(uint32_t i = 0; i < 0; i++)
возникает ошибка. Я немного поэкспериментировал и выяснил, что ошибка возникает при i < 1024/4 + 10. - Я могу писать в память после установки каталога, хотя страницы (и таблицы) помечены атрибутом readonly. Добавление этой строки
*(uint32_t*)(0x3) = 77;
после paging_init() не вызывает никакой реакции системы (я ожидал page fault). - Я не могу сдвинуть страницы на фрейм, т.е. изменение строки на
first_pte[i] = (0x1000*(i + 1)) | 1;
приводит к ошибке.
Я только встал на путь низкоуровневого программирования, поэтому буду рад любым советам и ответам. Спасибо!
Ответы (1 шт):
В каталоге должны быть определены как минимум те страницы, которые использует программа в данный момент. Предполагаю, что для примера выше это страницы, содержащие стек и код.
Страницы должны тождественно отражать фреймы:
[0] -> 0x00000000, [1] -> 0x00001000
и т.д. Иначе при обращении к ранее определённому адресу, например,0x00000000
, можно обратиться, например, к0x00001000
, и получить непредсказуемый результат.Привилегия
supervisor
позволяет игнорировать состояние флагаrw
. Чтобы отключить это поведение, нужно установить флагRW
в регистреCR0
:cr0 |= (1 << 31) | (1 << 16);
.