身份映射在启用分页后不起作用



我尝试实现我自己的操作系统,现在我尝试实现分页机制
我创建了一个页面目录,并创建了内核代码的标识映射。然而,在存储了第一个页面表的物理地址并启用分页之后,我的代码似乎丢失了——也就是说,虚拟地址没有映射到物理地址,或者映射到错误的物理地址。我可以在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()返回kmalloc将在下一次运行时分配的地址
memory_set()是我对memset()的实现。

任何帮助都将不胜感激
提前感谢!

我解决了

问题出在struct page_t上。该结构使用位字段,因此当我在该结构的frame成员中放入一个值时,该值被右移了12位
问题是,当我在alloc_frame()中设置frame时,我已经自己移动了值,所以页面指向了错误的帧
我认为页面被正确映射到框架的原因是GDB向我显示了原始值,而不是实际在结构中的值
这一定是因为位字段的缘故。

最新更新