Я пытаюсь реализовать свою собственную ОС, и теперь я пытаюсь реализовать механизм подкачки.
Я создал каталог страниц и создал отображение идентификаторов кода ядра. Однако после сохранения физического адреса таблицы первой страницы и включения подкачки мой код кажется потерянным, то есть виртуальный адрес не сопоставлен с физическим адресом или неверный. Я могу видеть это поведение в 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()
.
Любая помощь будет оценена!
Спасибо заранее!