我正在创建一种玩具操作系统,并且正在尝试设置分页。引导加载程序首先将内核映射到上半部分,然后我尝试设置一个新的页面目录。
我有一些这样定义的结构:
typedef struct pd_entry pd_entry_t;
struct pd_entry
{
uint32_t present : 1;
uint32_t writable : 1;
uint32_t user : 1;
uint32_t writethru : 1;
uint32_t nocache : 1;
uint32_t accessed : 1;
uint32_t _reserved : 1;
uint32_t size_4mb : 1;
uint32_t global : 1;
uint32_t available : 3;
uint32_t frame : 20;
}__attribute__((packed));
typedef struct pt_entry pt_entry_t;
struct pt_entry
{
uint32_t present : 1;
uint32_t writable : 1;
uint32_t user : 1;
uint32_t writethru : 1;
uint32_t nocache : 1;
uint32_t accessed : 1;
uint32_t dirty : 1;
uint32_t attr_index : 1;
uint32_t global : 1;
uint32_t available : 3;
uint32_t frame : 20;
} __attribute__((packed));
typedef struct page_dir page_dir_t;
struct page_dir
{
pd_entry_t entries[TABLES_PER_DIR];
};
typedef struct page_table page_table_t;
struct page_table
{
pt_entry_t entries[PAGES_PER_TABLE];
};
我使用这段代码来设置条目:
// I store the page directory pointer in the last directory entry
uint32_t map_quick(uint32_t addr)
{
GET_PTE(QUICKMAP_ADDR)->frame = PAGE_ALIGN(addr) >> 12; // QUICKMAP_ADDR is 0xC0000000
paging_flush_tlb_entry(QUICKMAP_ADDR); // asm volatile ("invlpg (%0)" :: "r"(addr));
return QUICKMAP_ADDR;
}
pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt)
{
dir = (page_dir_t *)map_quick((uint32_t)dir);
return &dir->entries[PAGE_DIR_INDEX(virt)];
}
void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt)
{
pd_entry_t *pde = get_dir_entry(dir, virt);
pde->present = 1;
pde->writable = 1;
pde->user = 0;
pde->writethru = 0;
pde->nocache = 0;
pde->accessed = 0;
pde->size_4mb = 0;
pde->global = 0;
pde->frame = (uint32_t)pt >> 12;
kprintf("0x%x:0x%x ", (uint32_t)pt >> 12, pde->frame); // Just a 'printf' clone
}
但是,当set_dir_entry
被调用时,kprintf
打印0xA0:0x0
. 我不明白为什么pde->frame
没有设置?我没有收到任何页面错误或异常,目录条目总是等于0
无论我将其设置为什么。
编辑:这是代码kprintf
:
size_t kprintf(const char *str, ...)
{
if (!str) return 0;
va_list args;
va_start(args, str);
unsigned int i;
for (i = 0; i < strlen(str); i++)
{
if (str[i] == '%')
{
switch (str[i+1])
{
case 'c':
{
char c = va_arg(args, char);
kputc(c);
i++;
break;
}
case 's':
{
char *s = va_arg(args, char*);
kputs(s);
i++;
break;
}
case 'd':
case 'i':
{
int c = va_arg(args, int);
char s[32] = {0};
itoa_s(c, s, 10);
kputs(s);
i++;
break;
}
case 'X':
case 'x':
{
int c = va_arg(args, int);
char s[32] = {0};
itoa_s(c, s, 16);
kputs(s);
i++;
break;
}
}
}
else
{
kputc(str[i]);
}
}
va_end(args);
return (size_t)i;
}
void kputc(unsigned char c)
{
uint16_t attr = color << 8;
if (c == 0x8 && posx)
posx--;
else if (c == 0x9)
posx = (posx + 8) & ~(8-1);
else if (c == '\r')
posx = 0;
else if (c == '\n')
{
posx = 0;
posy++;
}
else if (c >= ' ')
{
uint16_t *loc = videomem + (posy*80 + posx);
*loc = c | attr;
posx++;
}
if (posx >= 80)
{
posx = 0;
posy++;
}
if (posy >= 25)
kscroll();
}
void kputs(const char *str)
{
if (!str) return;
unsigned int i;
for (i = 0; i < strlen(str); i++)
kputc(str[i]);
}
编辑 2:我不知道这是否有用,而且我真的不喜欢只是转储大量代码供人们阅读,但这是整个分页代码。不属于此代码的所有函数调用都可以独立运行。
虚拟.h
#ifndef _VIRTUAL_H_
#define _VIRTUAL_H_
#include <x86/paging.h>
#include <stdbool.h>
#define VIRTUAL_TO_PHYSICAL(x) ((uint32_t)(x) - 0xC0001000 + 0x00101000)
#define PHYSICAL_TO_VIRTUAL(x) ((uint32_t)(x) + 0xC0001000 - 0x00101000)
#define KERNEL_CODE_VADDRESS 0xC0001000
#define KERNEL_HEAP_VADDRESS 0xD0000000
void virtual_init();
void virtual_create_dir(page_dir_t **dir);
void virtual_destroy_dir(page_dir_t *dir);
void virtual_set_dir(page_dir_t *dir);
void virtual_map_page(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present);
void virtual_map_kernel(page_dir_t *dir);
#endif
虚拟的.c
#include <system/memory/physical.h>
#include <x86/idt.h>
#include <utils/kernio.h>
#include <utils/kernpanic.h>
#include <string.h>
#include "virtual.h"
#define QUICKMAP_ADDR 0xC0000000
#define GET_PDE(x) ((pd_entry_t *)(0xFFFFF000 + ((x) >> 22) * 4))
#define GET_PTE(x) ((pt_entry_t *)(0xFFC00000 + ((x) >> 12) * 4))
extern uint32_t __kernel_start, __kernel_end;
page_dir_t *kern_dir;
pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt);
void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt);
pt_entry_t* get_table_entry(page_dir_t *dir, uint32_t virt);
void set_table_entry(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present);
uint32_t map_quick(uint32_t addr);
void interrupt page_fault(registers_t *regs);
void virtual_init()
{
int_enable(14, page_fault);
virtual_create_dir(&kern_dir);
uint32_t phys, virt;
for (phys = 0; phys < 0x400000; phys += PAGE_SIZE)
virtual_map_page(kern_dir, phys, phys, true);
phys = VIRTUAL_TO_PHYSICAL(&__kernel_start);
virt = (uint32_t)&__kernel_start;
uint32_t end = VIRTUAL_TO_PHYSICAL(&__kernel_end) + physical_get_bitmap_size();
for (; phys < end; phys += PAGE_SIZE, virt += PAGE_SIZE)
virtual_map_page(kern_dir, phys, virt, true);
virtual_map_page(kern_dir, 0x0, QUICKMAP_ADDR, true);
virtual_set_dir(kern_dir);
}
void virtual_create_dir(page_dir_t **dir)
{
*dir = (page_dir_t *)physical_alloc_block();
page_dir_t *virt_dir = (page_dir_t *)map_quick((uint32_t)*dir);
memset(virt_dir, 0, sizeof(page_dir_t));
pd_entry_t *last_pde = &virt_dir->entries[TABLES_PER_DIR - 1];
last_pde->present = 1;
last_pde->writable = 1;
last_pde->frame = (uint32_t)*dir >> 12;
}
void virtual_destroy_dir(page_dir_t *dir)
{
page_dir_t *virt_dir = (page_dir_t *)map_quick((uint32_t)dir);
unsigned int i, j;
for (i = 1; i < KERNEL_CODE_VADDRESS / (PAGE_SIZE * TABLES_PER_DIR); i++)
{
pd_entry_t *pde = &virt_dir->entries[i];
page_table_t *table = (page_table_t *)(pde->frame << 12);
if (table != NULL && pde->present)
{
page_table_t *virt_table = (page_table_t *)map_quick((uint32_t)table);
for (j = 0; j < PAGES_PER_TABLE; j++)
{
pt_entry_t *pte = &virt_table->entries[j];
uint32_t phys = pte->frame << 12;
if (phys != NULL && pte->present)
physical_free_block((void *)phys);
}
physical_free_block(table);
}
}
physical_free_block(dir);
}
void virtual_set_dir(page_dir_t *dir)
{
paging_load_pdbr((uint32_t)dir);
paging_enable(true);
}
void virtual_map_page(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present)
{
set_table_entry(dir, phys, virt, present);
}
void virtual_map_kernel(page_dir_t *dir)
{
pd_entry_t *pde;
pde = get_dir_entry(kern_dir, 0x0);
set_dir_entry(dir, 0x0, (page_table_t *)(pde->frame << 12));
pde = get_dir_entry(kern_dir, KERNEL_CODE_VADDRESS);
set_dir_entry(dir, KERNEL_CODE_VADDRESS, (page_table_t *)(pde->frame << 12));
}
pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt)
{
dir = (page_dir_t *)map_quick((uint32_t)dir);
pd_entry_t *e = &dir->entries[PAGE_DIR_INDEX(virt)];
return &dir->entries[PAGE_DIR_INDEX(virt)];
}
void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt)
{
pd_entry_t *pde = get_dir_entry(dir, virt);
pde->present = 1;
pde->writable = 1;
pde->user = 0;
pde->writethru = 0;
pde->nocache = 0;
pde->accessed = 0;
pde->size_4mb = 0;
pde->global = 0;
pde->frame = (uint32_t)pt >> 12;
}
pt_entry_t* get_table_entry(page_dir_t *dir, uint32_t virt)
{
pd_entry_t *pde = get_dir_entry(dir, virt);
page_table_t *table = (page_table_t *)(pde->frame << 12);
if (table == NULL)
{
table = (page_table_t *)physical_alloc_block();
set_dir_entry(dir, virt, table);
unsigned int i;
for (i = 0; i < PAGES_PER_TABLE; i++)
set_table_entry(dir, 0x0, virt + (i * PAGE_SIZE), false);
}
table = (page_table_t *)map_quick((uint32_t)table);
return (pt_entry_t *)&table->entries[PAGE_TABLE_INDEX(virt)];
}
void set_table_entry(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present)
{
pt_entry_t *pte = get_table_entry(dir, PAGE_ALIGN(virt));
pte->present = present;
pte->writable = 1;
pte->user = 0;
pte->writethru = 0;
pte->nocache = 0;
pte->dirty = 0;
pte->attr_index = 0;
pte->global = 0;
pte->frame = PAGE_ALIGN(phys) >> 12;
}
uint32_t map_quick(uint32_t addr)
{
GET_PTE(QUICKMAP_ADDR)->frame = PAGE_ALIGN(addr) >> 12;
paging_flush_tlb_entry(QUICKMAP_ADDR);
return QUICKMAP_ADDR;
}
void interrupt page_fault(registers_t *regs)
{
uint32_t virt;
asm volatile ("movl %%cr2, %0" : "=r" (virt));
kprintf("\nError accessing address 0x%x", virt);
kpanic("Page Fault");
}