Отображение идентичности не работает после включения подкачки - PullRequest
0 голосов
/ 21 апреля 2020

Я пытаюсь реализовать свою собственную ОС, и теперь я пытаюсь реализовать механизм подкачки.
Я создал каталог страниц и создал отображение идентификаторов кода ядра. Однако после сохранения физического адреса таблицы первой страницы и включения подкачки мой код кажется потерянным, то есть виртуальный адрес не сопоставлен с физическим адресом или неверный. Я могу видеть это поведение в GDB: мои коды операций кода превращаются в add [eax], al.

Мои структуры:

#define MAX_PAGES_PER_TABLE 1024
#define MAX_TABLES_PER_DIR 1024
typedef struct {
    uint32_t present    : 1;   // Page present in memory
    uint32_t rw         : 1;   // Read-only if clear, readwrite if set
    uint32_t user       : 1;   // Supervisor level only if clear
    uint32_t accessed   : 1;   // Has the page been accessed since last refresh?
    uint32_t dirty      : 1;   // Has the page been written to since last refresh?
    uint32_t unused     : 7;   // Amalgamation of unused and reserved bits
    uint32_t frame      : 20;  // Frame address (shifted right 12 bits)
} page_t;

typedef struct {
    page_t pages[MAX_PAGES_PER_TABLE];
} page_table_t;

typedef struct {
    page_table_t* page_tables[MAX_TABLES_PER_DIR]; // Array of pointers to pagetables.
    /*
       Array of pointers to the pagetables above, but gives their *physical*
       location, for loading into the CR3 register.
    */
    uint32_t page_tables_physical[MAX_TABLES_PER_DIR];
    /*
       The physical address of page_tables_physical. This comes into play
       when we get our kernel heap allocated and the directory
       may be in a different location in virtual memory.
    */
    uint32_t physical_address;
} page_directory_t;

Вот мой разрешающий код подкачки:

page_directory_t* current_page_dir;

void switch_page_directory(page_directory_t* new_page_directory)
{
    current_page_dir = new_page_directory;
    // let the cpu know the physical address of the page tables
    asm volatile("mov %0, %%cr3" : : "r"(&new_page_directory->page_tables_physical));
    uint32_t cr0;
    asm volatile("mov %%cr0, %0" : "=r"(cr0));
    cr0 |= 0x80000000; // set the PG flag of cr0 - enable paging
    asm volatile("mov %0, %%cr0" : : "r" (cr0));
}

Я дважды проверил, что каталог страниц выглядит нормально, но, возможно, я ошибаюсь, вот код идентификации:

void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
    if (page->frame != 0) {
        // there is already a frame associated with the page
        return;
    }
    uint32_t first_free_frame_address = first_not_set(frames);
    if (first_free_frame_address == frames.len + 1) {
        panic("no free pages");
    }

    set_bit(first_free_frame_address, frames);
    page->frame = first_free_frame_address * ALIGNMENT;
    page->present = 1;
    page->rw = is_writeable ? 1: 0;
    page->user = is_kernel ? 0 : 1;
}

page_t* get_page(uint32_t address, int create, page_directory_t* dir)
{
    // when dividing the address by alignment, the index of the page is received
    uint32_t address_index = address / ALIGNMENT;
    // according to the index of the address,
    // the index of the table, and the index of the page in the table are calculated
    uint32_t table_index = address_index / MAX_PAGES_PER_TABLE;
    uint32_t page_index = address_index % MAX_PAGES_PER_TABLE;
    if (dir->page_tables[table_index]) {
        // page table exists
        return &dir->page_tables[table_index]->pages[page_index];
    } else if (create) {
        // page table doesn't exist - creating it
        uint32_t physical_address;
        dir->page_tables[table_index] = (page_table_t*) kmalloc_internal(sizeof(page_table_t), 1, &physical_address);
        // set a first entry in the table
        memory_set((uint8_t*)dir->page_tables[table_index], 0, ALIGNMENT);
        // give the first page, the attributes: Present, Read/Write, Kernel page
        physical_address |= 0x3;
        // save the physical address of the table
        dir->page_tables_physical[table_index] = physical_address;
        return &dir->page_tables[table_index]->pages[page_index];
    } else {
        //page table doesn't exist - page cannot be retrieved
        return 0;
    }
}

void initialize_paging()
{
    initialize_frames();
    page_directory_t* page_dir = (page_directory_t*) kmalloc(sizeof(page_directory_t));
    memory_set((uint8_t*) page_dir, 0, sizeof(page_directory_t));
    // set the physical addresses of the page tables to 0 with attributes:
    // kernel tables, rw, not present
    int j;
    uint8_t* tmp = (uint8_t*) page_dir->page_tables_physical;
    for (j = 0; j < 1024; j++) {
        memory_set(tmp, 2, 1);
        tmp++;
        memory_set(tmp, 0, 3);
        tmp += 3;
    }
    current_page_dir = page_dir;

    // We need to identity map (phys addr = virt addr) from
    // 0x0 to the end of used memory, so we can access this
    // transparently, as if paging wasn't enabled.
    // NOTE that we use a while loop here deliberately.
    // inside the loop body we actually change free_physical_address
    // by calling kmalloc(). A while loop causes this to be
    // computed on-the-fly rather than once at the start.
    uint32_t free_physical_address = get_current_physicall_address();
    uint32_t i = 0;
    while (i <= free_physical_address)
    {
        // Kernel code is readable but not writeable from userspace.
        alloc_frame(get_page(i, 1, page_dir), 1, 1);
        i += ALIGNMENT;
        // get_page() may allocate more space for new tables
        free_physical_address = get_current_physicall_address();
    }

Это большой код, поэтому я подведу итог:
alloc_frame() находит следующий свободный кадр - используя набор битов (здесь я не помещаю код набора битов, потому что он работает правильно.
get_page(), возвращает запись страницы, соответствующую данному адресу, и создает таблицу страниц, если не не существует.
initialize_paging() создает каталог страницы и выполняет сопоставление идентификаторов.

Следующие функции отсутствуют, но это их краткое содержание:
kmalloc() выделяет память, выровненную по 4096.
get_current_physicall_address() возвращает адрес, который kmallo c выделит при следующем запуске.
memory_set() - моя реализация memset().

Любая помощь будет оценена!
Спасибо заранее!

1 Ответ

2 голосов
/ 22 апреля 2020

Я решил это!

Проблема была с struct page_t. В структуре используются битовые поля, поэтому, когда я помещаю значение в frame член структуры, это значение сдвигается вправо на 12 бит.
Проблема заключалась в том, что когда я устанавливал frame в alloc_frame(), я Я уже сдвинул значение, поэтому страницы указывали на неправильные кадры.
Причина, по которой я думал, что страницы были правильно сопоставлены с кадрами, заключалась в том, что GDB показывал мне исходное значение, а не то, которое на самом деле находится в структуре.
Это должно было произойти из-за битовых полей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...