152 lines
4.8 KiB
C
152 lines
4.8 KiB
C
|
#include "../include/memory.h"
|
||
|
#include "../include/vga.h"
|
||
|
#include "../include/io.h"
|
||
|
|
||
|
page_directory_t *kernel_directory = 0; // 内核用页目录
|
||
|
page_directory_t *current_directory = 0; // 当前页目录
|
||
|
|
||
|
uint32_t *frames;
|
||
|
uint32_t nframes;
|
||
|
|
||
|
extern uint32_t placement_address;
|
||
|
extern void *program_break, *program_break_end;
|
||
|
|
||
|
static void set_frame(uint32_t frame_addr) {
|
||
|
uint32_t frame = frame_addr / 0x1000;
|
||
|
uint32_t idx = INDEX_FROM_BIT(frame);
|
||
|
uint32_t off = OFFSET_FROM_BIT(frame);
|
||
|
frames[idx] |= (0x1 << off);
|
||
|
}
|
||
|
|
||
|
static void clear_frame(uint32_t frame_addr) {
|
||
|
uint32_t frame = frame_addr / 0x1000;
|
||
|
uint32_t idx = INDEX_FROM_BIT(frame);
|
||
|
uint32_t off = OFFSET_FROM_BIT(frame);
|
||
|
frames[idx] &= ~(0x1 << off);
|
||
|
}
|
||
|
|
||
|
static uint32_t test_frame(uint32_t frame_addr) {
|
||
|
uint32_t frame = frame_addr / 0x1000;
|
||
|
uint32_t idx = INDEX_FROM_BIT(frame);
|
||
|
uint32_t off = OFFSET_FROM_BIT(frame);
|
||
|
return (frames[idx] & (0x1 << off));
|
||
|
}
|
||
|
|
||
|
uint32_t first_frame() {
|
||
|
for (int i = 0; i < INDEX_FROM_BIT(nframes); i++) {
|
||
|
if (frames[i] != 0xffffffff) {
|
||
|
for (int j = 0; j < 32; j++) {
|
||
|
uint32_t toTest = 0x1 << j;
|
||
|
if (!(frames[i] & toTest)) {
|
||
|
return i * 4 * 8 + j;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return (uint32_t) -1;
|
||
|
}
|
||
|
|
||
|
void alloc_frame(page_t *page, int is_kernel, int is_writable) {
|
||
|
if (page->frame) return;
|
||
|
else {
|
||
|
uint32_t idx = first_frame();
|
||
|
if (idx == (uint32_t) -1) {
|
||
|
printf("FRAMES_FREE_ERROR: Cannot free frames!\n");
|
||
|
asm("cli");
|
||
|
for (;;)io_hlt();
|
||
|
}
|
||
|
set_frame(idx * 0x1000);
|
||
|
page->present = 1; // 现在这个页存在了
|
||
|
page->rw = is_writable ? 1 : 0; // 是否可写由is_writable决定
|
||
|
page->user = is_kernel ? 0 : 1; // 是否为用户态由is_kernel决定
|
||
|
page->frame = idx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void free_frame(page_t *page) {
|
||
|
uint32_t frame = page->frame;
|
||
|
if (!frame) return;
|
||
|
else {
|
||
|
clear_frame(frame);
|
||
|
page->frame = 0x0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void switch_page_directory(page_directory_t *dir) {
|
||
|
current_directory = dir;
|
||
|
asm volatile("mov %0, %%cr3" : : "r"(&dir->tablesPhysical));
|
||
|
uint32_t cr0;
|
||
|
asm volatile("mov %%cr0, %0" : "=r"(cr0));
|
||
|
cr0 |= 0x80000000;
|
||
|
asm volatile("mov %0, %%cr0" : : "r"(cr0));
|
||
|
}
|
||
|
|
||
|
page_t *get_page(uint32_t address, int make, page_directory_t *dir) {
|
||
|
address /= 0x1000;
|
||
|
uint32_t table_idx = address / 1024;
|
||
|
if (dir->tables[table_idx]) return &dir->tables[table_idx]->pages[address % 1024];
|
||
|
else if (make) {
|
||
|
uint32_t tmp;
|
||
|
dir->tables[table_idx] = (page_table_t *) kmalloc_ap(sizeof(page_table_t), &tmp);
|
||
|
memset(dir->tables[table_idx], 0, 0x1000);
|
||
|
dir->tablesPhysical[table_idx] = tmp | 0x7;
|
||
|
return &dir->tables[table_idx]->pages[address % 1024];
|
||
|
} else return 0;
|
||
|
}
|
||
|
|
||
|
void page_fault(registers_t *regs) {
|
||
|
asm("cli");
|
||
|
uint32_t faulting_address;
|
||
|
asm volatile("mov %%cr2, %0" : "=r" (faulting_address)); //
|
||
|
|
||
|
int present = !(regs->err_code & 0x1); // 页不存在
|
||
|
int rw = regs->err_code & 0x2; // 只读页被写入
|
||
|
int us = regs->err_code & 0x4; // 用户态写入内核页
|
||
|
int reserved = regs->err_code & 0x8; // 写入CPU保留位
|
||
|
int id = regs->err_code & 0x10; // 由取指引起
|
||
|
|
||
|
printf("[ERROR]: Page fault |");
|
||
|
if (present)
|
||
|
printf("Type: present;\n\taddress: %x ", faulting_address);
|
||
|
else if (rw)
|
||
|
printf("Type: read-only;\n\taddress: %x", faulting_address);
|
||
|
else if (us)
|
||
|
printf("Type: user-mode;\n\taddres: %x", faulting_address);
|
||
|
else if (reserved)
|
||
|
printf("Type: reserved;\n\taddress: %x", faulting_address);
|
||
|
else if (id)
|
||
|
printf("Type: decode address;\n\taddress: %x", faulting_address);
|
||
|
|
||
|
|
||
|
for (;;) io_hlt();
|
||
|
}
|
||
|
|
||
|
void init_page() {
|
||
|
uint32_t mem_end_page = 0xFFFFFFFF; // 4GB Page
|
||
|
|
||
|
nframes = mem_end_page / 0x1000;
|
||
|
frames = (uint32_t *) kmalloc(INDEX_FROM_BIT(nframes));
|
||
|
memset(frames, 0, INDEX_FROM_BIT(nframes));
|
||
|
|
||
|
kernel_directory = (page_directory_t *) kmalloc_a(sizeof(page_directory_t)); //kmalloc: 无分页情况自动在内核后方分配 | 有分页从内核堆分配
|
||
|
|
||
|
memset(kernel_directory, 0, sizeof(page_directory_t));
|
||
|
current_directory = kernel_directory;
|
||
|
int i = 0;
|
||
|
while (i < placement_address) {
|
||
|
// 内核部分对ring3而言可读不可写 | 无偏移页表映射
|
||
|
alloc_frame(get_page(i, 1, kernel_directory), 0, 0);
|
||
|
i += 0x1000;
|
||
|
}
|
||
|
|
||
|
for (int i = KHEAP_START; i < KHEAP_START + KHEAP_INITIAL_SIZE; i++) {
|
||
|
alloc_frame(get_page(i, 1, kernel_directory), 0, 0);
|
||
|
}
|
||
|
|
||
|
register_interrupt_handler(14, page_fault);
|
||
|
switch_page_directory(kernel_directory);
|
||
|
|
||
|
program_break = (void *) KHEAP_START;
|
||
|
program_break_end = (void *) (KHEAP_START + KHEAP_INITIAL_SIZE);
|
||
|
}
|