Как я могу реализовать пейджинг и найти адрес физической памяти, зная виртуальный адрес

Я хочу реализовать инициализацию подкачки. Ссылаясь на некоторые ссылки на вики osdev: https://wiki.osdev.org/Paging , https://wiki.osdev.org/Setting_Up_Paging , моя собственная версия сильно отличается. Потому что, когда мы смотрим на каталог страниц , они говорят, что 12 бит для флага, а остальные для адреса таблицы страниц, поэтому я попробовал что-то вроде этого:

void init_paging() {
    unsigned int i = 0;

    unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000)));

    for (i = 0; i < 0x400; i++) __PAGE_DIRECTORY__[i] = PAGE_PRESENT(0) | PAGE_READ_WRITE;

    for (i = 0; i < 0x400; i++) __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__  << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    _EnablingPaging_();
} 

эта функция помогает мне узнать физический адрес, зная виртуальный адрес:

void *get_phyaddr(void *virtualaddr) {
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;

unsigned long *pd = (unsigned long *)__PAGE_DIRECTORY__[pdindex];

unsigned long *pt = (unsigned long *)pd[ptindex];

return (void *)(pt + ((unsigned int)virtualaddr & 0xFFF));

}

Я не в том направлении?

Или все тот же?

Всего 1 ответ


Предполагая, что вы пытаетесь идентифицировать первые 4 Мбайт физического адресного пространства:

a) для unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000))); это локальная переменная (например, скорее всего, помещенная в стек); и оно не сохранится после возврата функции (например, используемое ею пространство стека будет перезаписано другими функциями позже), что приведет к повреждению таблицы страниц. Это вряд ли закончится хорошо.

б) Для __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; вы меняете i дважды, один раз с * 0x1000 (что совпадает с << 12 ) и снова с << 12 . Это слишком много, и это должно быть больше похоже на __FIRST_PAGE_TABLE__[i] = (i << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; __FIRST_PAGE_TABLE__[i] = (i << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; ,

c) Для __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE; адрес уже является адресом (а не «номером страницы», который необходимо __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__) | PAGE_PRESENT(1) | PAGE_READ_WRITE; ), поэтому он должен быть похож на __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__) | PAGE_PRESENT(1) | PAGE_READ_WRITE; __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__) | PAGE_PRESENT(1) | PAGE_READ_WRITE; ,

За гранью этого; Я бы очень предпочел лучшее использование типов. В частности, вам, вероятно, следует привыкнуть использовать uint32_t (или uint64_t , или ваш собственный typedef ) для физических адресов, чтобы убедиться, что вы случайно не перепутаете виртуальный адрес с физическим адресом (и убедитесь, что компилятор жалуется на неправильный набери когда ошибаешься); потому что (хотя это не очень важно сейчас, потому что вы отображаете личность), это станет важным "скоро". Я также рекомендовал бы использовать uint32_t для записей таблицы страниц и записей каталога страниц, потому что они должны быть 32-битными, а не «каким бы ни был размер, какой компилятор считает int должен быть» (обратите внимание, что это различие в том, как вы думаете о коде, что более важно, чем то, что на самом деле делает компилятор, или значение int равно 32 битам).


Есть идеи?

10000