quinn-os/memory.c

737 lines
17 KiB
C

#include "multiboot.h"
#include "string.h"
#include "memory.h"
extern struct task_t *current_task;
#ifdef MEM_DEBUG
char *mem_alloc_pid(int pid);
struct mem_frame_t {
unsigned int page;
int pid;
}__attribute__((packed));
struct mem_frame_t *pageframes;
#else
#define mem_alloc_pid(x) mem_alloc()
unsigned char *mem_bitmap = (unsigned char *)0;
unsigned int mem_bitmap_size;
#endif
unsigned char *start_free_mem;
unsigned char *end_free_mem;
unsigned int *page_directory;
unsigned char *kernel_ds_at;
unsigned char *pci_mem_at;
unsigned int framebuffer_start;
unsigned int framebuffer_len;
int free_page_count;
extern unsigned char *videoram;
extern unsigned char vidmode;
unsigned int pages_above_kernel;
#define PAGE_SIZE 4096
#define PAGE_MASK (~(0xffffffff << 12))
char *mem_free_last_called_by;
static __inline__ unsigned long round_up_to_page(unsigned long addr) {
if ((addr & PAGE_MASK) != 0) {
addr &= ~(PAGE_MASK);
addr += PAGE_SIZE;
}
return addr;
}
extern unsigned char *endkernel;
void init_mem(multiboot_info_t *mbd) {
int available = 0;
int total = 0;
unsigned int mem_end;
int i;
mem_free_last_called_by = "Init MEM";
pages_above_kernel = 0;
multiboot_memory_map_t *mmap = (multiboot_memory_map_t *)mbd->mmap_addr;
while (mmap < (multiboot_memory_map_t *)(mbd->mmap_addr + mbd->mmap_length)) {
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) {
if (mmap->addr == 0x00100000) {
pages_above_kernel = mmap->len / PAGE_SIZE;
end_free_mem = (unsigned char *)(mmap->addr + mmap->len);
}
available += mmap->len;
}
total += mmap->len;
mmap = (multiboot_memory_map_t *)((unsigned int)mmap + mmap->size + sizeof(unsigned int));
}
mem_end = (unsigned int)&endkernel;
multiboot_module_t *mod;
if (mbd->mods_count > 0) {
mod = (multiboot_module_t *)mbd->mods_addr;
mem_end = mod->mod_end;
}
mem_end = round_up_to_page(mem_end);
pages_above_kernel -= (mem_end / PAGE_SIZE);
#ifdef MEM_DEBUG
pageframes = (struct mem_frame_t *)mem_end;
int pages_in_frames = round_up_to_page((pages_above_kernel * PAGE_SIZE) / sizeof(struct mem_frame_t)) / PAGE_SIZE;
free_page_count = (pages_above_kernel - pages_in_frames);
start_free_mem = (unsigned char *)round_up_to_page(mem_end + (pages_in_frames * PAGE_SIZE));
for (i=0;i<free_page_count;i++) {
pageframes[i].page = start_free_mem + i * PAGE_SIZE;
pageframes[i].pid = -1;
}
#else
mem_bitmap = (unsigned char *)mem_end;
mem_bitmap_size = pages_above_kernel / 8;
start_free_mem = (unsigned char *)round_up_to_page(mem_end + mem_bitmap_size);
free_page_count = ((unsigned int)end_free_mem - (unsigned int)start_free_mem) / PAGE_SIZE;
for (i=0;i<mem_bitmap_size;i++) {
mem_bitmap[i] = 0;
}
#endif
mem_reserve(0x00f00000, 256); // ISA HOLE
kernel_ds_at = (unsigned char *)0;
pci_mem_at = (unsigned char *)0x20000000;
}
extern unsigned char initialized;
void mem_clear_page_dir(unsigned int *user_page_directory) {
int i, j;
unsigned int *fake_dir = (unsigned int *)0x29002000;
unsigned int *fake_tab = (unsigned int *)0x29005000;
mem_map_page((unsigned int)user_page_directory, (unsigned int)fake_dir, 3);
for (i=0x100;i<0x340;i++) {
if (fake_dir[i] & 0x1 != 0) {
mem_map_page(fake_dir[i] & 0xfffff000, fake_tab, 7);
for (j=0;j<1023;j++) {
if (fake_tab[j] & 0x1) {
mem_free((char *)(fake_tab[j] & 0xfffff000), "mem_clear_page_dir");
fake_tab[j] = 2;
}
}
mem_free((char *)(fake_dir[i] & 0xFFFFF000), "mem_clear_page_dir");
fake_dir[i] = 0 | 6;
}
}
}
void mem_free_page_dir(unsigned int *user_page_directory) {
mem_clear_page_dir(user_page_directory);
mem_free((char *)user_page_directory, "mem_free_page_dir");
}
int mem_cpy_pages(struct task_t *old_task, struct task_t *new_task) {
int virt;
int i;
unsigned int *fake_tab = (unsigned int *)0x29003000;
if (old_task->user_pages != (void *)0) {
new_task->user_pages = (unsigned int *)malloc(sizeof(unsigned int) * old_task->user_pages_cnt);
if (!new_task->user_pages) {
return 0;
}
new_task->user_pages_at = old_task->user_pages_at;
new_task->user_pages_cnt = old_task->user_pages_cnt;
for (i=0;i<old_task->user_pages_cnt;i++) {
virt = i * 0x1000 + 0x40000000;
new_task->user_pages[i] = (unsigned int)mem_alloc_pid(new_task->pid);
mem_map_page(new_task->user_pages[i], (unsigned int)fake_tab, 7);
memcpy((char *)fake_tab, (char *)virt, 0x1000);
mem_map_page_in(new_task->user_pages[i], virt, new_task->cr3, 7);
}
} else {
new_task->user_pages_at = 0x40000000;
new_task->user_pages_cnt = 0;
}
// copy user stack
for (i=0;i<USER_STACK_SIZE/0x1000;i++) {
new_task->user_stack_pages[i] = (unsigned int)mem_alloc_pid(new_task->pid);
virt = 0xf0200000 + i * 0x1000;
if (old_task->user_stack_pages[i] != 0) {
mem_map_page(new_task->user_stack_pages[i], (unsigned int)fake_tab, 7);
memcpy((char *)fake_tab, (char *)virt, 0x1000);
}
mem_map_page_in(new_task->user_stack_pages[i], virt, new_task->cr3, 7);
}
// copy environment
virt = 0xf0000000;
for (i=0;i<64;i++) {
virt = 0xf0000000 + (i * 0x1000);
new_task->user_env_pages[i] = (unsigned int)mem_alloc_pid(new_task->pid);
if (old_task->user_env_pages[i] != 0) {
mem_map_page(new_task->user_env_pages[i], (unsigned int)fake_tab, 7);
memcpy((char *)fake_tab, (char *)virt, 0x1000);
}
mem_map_page_in(new_task->user_env_pages[i], virt, new_task->cr3, 7);
}
}
void mem_clear_user_pages() {
int i;
unsigned int *pd_map = (unsigned int *)0xfffff000;
for (i=0;i<current_task->user_pages_cnt;i++) {
unsigned int virt = i * 0x1000 + 0x40000000;
unsigned int table_entry = (virt >> 12) & 0x03ff;
unsigned int dir_entry = (virt >> 22);
unsigned int *pt_map = (unsigned int *)0xffc00000 + (0x400 * dir_entry);
mem_free((char *)(pt_map[table_entry] & 0xfffff000), "mem_clear_user_pages");
pt_map[table_entry] = 0 | 6;
}
/*
for (i=0;i<current_task->user_pages_cnt;i++) {
unsigned int virt = i * 0x1000 + 0x40000000;
unsigned int dir_entry = (virt >> 22);
if (pd_map[dir_entry] & 0x1) {
mem_free((char *)(pd_map[dir_entry] & 0xFFFFF000), "mem_clear_user_pages");
pd_map[dir_entry] = 0 | 6;
}
}
*/
ivld_cr3();
}
#ifdef MEM_DEBUG
void mem_reserve(char *blk, int pages) {
unsigned int i = ((unsigned int)blk - (unsigned int)start_free_mem) / PAGE_SIZE;
int j;
for (j=i;j<i+pages;j++) {
pageframes[i].pid = 0;
}
}
char *mem_alloc_pid(int pid) {
int i;
for (i=0;i<free_page_count;i++) {
if (pageframes[i].pid == -1) {
pageframes[i].pid = pid;
return (char *)pageframes[i].page;
}
}
return (void *)0;
}
char *mem_alloc(void) {
int i;
for (i=0;i<free_page_count;i++) {
if (pageframes[i].pid == -1) {
pageframes[i].pid = current_task->pid;
return (char *)pageframes[i].page;
}
}
return (void *)0;
}
void mem_free(char *blk, char *caller) {
struct mem_frame_t *page = &pageframes[((unsigned int)blk - (unsigned int)start_free_mem) / PAGE_SIZE];
if (page->page == (unsigned int)blk) {
if (page->pid != current_task->pid && strcmp(caller, "sched_free_task") != 0){
kprintf("Page owned by %d, freed by %d %s\n", page->pid, current_task->pid, caller);
}
page->pid = -1;
} else {
kprintf("ERROR IN MEM MANAGER\n");
}
}
#else
void mem_reserve(char *blk, int pages) {
unsigned int i = ((unsigned int)blk - (unsigned int)start_free_mem) / PAGE_SIZE;
unsigned long mask, offset, index;
unsigned int j;
for (j=i;j<i+pages;j++) {
mask = 1;
index = j / 8;
offset = j % 8;
mem_bitmap[index] |= (mask << offset);
}
}
char *mem_alloc(void) {
return mem_alloc_pages(1);
}
// allocates a single physical page
char *mem_alloc_pages(int count) {
char * blk;
unsigned long mask, offset, index, i, j;
int consecutive = 0;
unsigned long start;
for (i=0;i<free_page_count;i++) {
mask = 1;
index = i / 8;
offset = i % 8;
if (!(mem_bitmap[index] & (mask << offset))) {
mem_bitmap[index] |= (mask << offset);
if (consecutive == 0) {
start = (unsigned long)start_free_mem + (i * PAGE_SIZE);
}
consecutive++;
if (consecutive == count) {
return (char *)start;
}
} else {
for (j=0;j<consecutive;j++) {
mem_free(start + (i * PAGE_SIZE), "mem_alloc");
}
consecutive = 0;
}
}
return (char *)0;
}
// frees a single physical page
void mem_free(char *blk, char *caller) {
unsigned long mask, offset, index;
mem_free_last_called_by = caller;
unsigned long addr = (unsigned long)blk;
if ((unsigned int)blk & 0x00000FFF) {
kprintf("Bad Free 0x%p %s\n", addr, caller);
return;
}
addr -= (unsigned long)start_free_mem;
if (addr < 0 || addr > end_free_mem) {
kprintf("Bad Free 0x%p %s\n", addr, caller);
return;
}
index = (addr / PAGE_SIZE) / 8;
offset = (addr / PAGE_SIZE) % 8;
if (!(mem_bitmap[index] & (1 << offset))) {
kprintf("Double Free 0x%p %s\n", blk, caller);
return;
}
mask = ~(1<<offset);
mem_bitmap[index] &= mask;
}
#endif
extern void console_pagemap();
void init_paging(void) {
unsigned int i;
unsigned int *page_table;
page_directory = (unsigned int *)mem_alloc();
// map page directory into itself
page_directory[1023] = (unsigned int)page_directory | 3;
for (i=0;i<1023;i++) {
page_directory[i] = (unsigned int)0 | 2;
}
// identity map lower 4 MB
page_table = (unsigned int *)mem_alloc();
memset((char *)page_table, 0, 4096);
page_directory[0] = (unsigned int)page_table | 3;
unsigned int address = 0;
while (address <= (unsigned int)start_free_mem) {
if (!(page_directory[address / 4096 / 1024] & 0x1)) {
page_directory[address / 4096 / 1024] = (unsigned int)mem_alloc() | 3;
}
page_table = page_directory[address / 4096 / 1024] & 0xfffff000;
page_table[address / 4096 % 1024] = address | 3;
address += PAGE_SIZE;
}
for (i=0x80;i<0xA4;i++) {
page_directory[i] = (unsigned int)mem_alloc() | 3;
}
for (i=0x340;i<0x380;i++) {
page_directory[i] = (unsigned int)mem_alloc() | 3;
}
// enable paging
asm volatile("mov %0, %%cr3":: "b"(page_directory));
unsigned int cr0;
asm volatile("mov %%cr0, %0": "=b"(cr0));
cr0 |= 0x80000000;
asm volatile("mov %0, %%cr0":: "b"(cr0));
}
unsigned int *mem_new_pagedirectory(struct task_t *new_task) {
unsigned int *new_page_directory;
//unsigned int *new_page_table;
unsigned int *old_page_dir = (unsigned int *)0xfffff000;
//unsigned int *old_page_table = (unsigned int *)0xffc00000;
unsigned int *fake_dir = (unsigned int *)0x29000000;
//unsigned int *fake_tab = (unsigned int *)0x29001000;
unsigned int i;
new_page_directory = (unsigned int *)mem_alloc_pid(new_task->pid);
mem_map_page((unsigned int)new_page_directory, (unsigned int)fake_dir, 3);
// map page directory into itself
fake_dir[1023] = (unsigned int)new_page_directory | 3;
for (i=0;i<1023;i++) {
fake_dir[i] = 0 | 6;
}
unsigned int table_end = (unsigned int)start_free_mem / 4096 / 1024 + 1;
for (i=0;i<table_end;i++)
fake_dir[i] = old_page_dir[i];
for (i=0x80;i<0xA4;i++) {
fake_dir[i] = old_page_dir[i];
}
for (i=0x340;i<0x380;i++) {
fake_dir[i] = old_page_dir[i];
}
// map kernel stack areas
for (i=0xC4;i<0xC8;i++) {
fake_dir[i] = old_page_dir[i];
}
// map framebuffers
if (vidmode == 1) {
for (i=0x380;i<0x380 + (framebuffer_len / 0x400000) + 1 ;i++) {
fake_dir[i] = old_page_dir[i];
}
}
return new_page_directory;
}
unsigned char *mem_map_page_in(unsigned int phys, unsigned int virt, unsigned int cr3, int flags) {
unsigned int *fake_dir = (unsigned int *)0x29004000;
unsigned int *fake_tab = (unsigned int *)0x29001000;
unsigned int dir_entry = (virt >> 22);
unsigned int table_entry = (virt >> 12) & 0x03ff;
int i;
mem_map_page((unsigned int)cr3, (unsigned int)fake_dir, flags);
if ((fake_dir[dir_entry] & 1) == 0) {
fake_dir[dir_entry] = (unsigned int)mem_alloc() | flags;
mem_map_page((unsigned int)fake_dir[dir_entry] & 0xfffff000, (unsigned int)fake_tab, flags);
for (i=0;i<1023;i++) {
fake_tab[i] = 0x2;
}
fake_tab[1023] = fake_tab;
} else {
mem_map_page((unsigned int)fake_dir[dir_entry] & 0xfffff000, (unsigned int)fake_tab, flags);
}
fake_tab[table_entry] = phys | flags;
}
extern void ivld_tlb(unsigned int virt);
extern void ivld_cr3();
unsigned char *mem_map_page(unsigned int phys, unsigned int virt, int flags) {
unsigned int dir_entry = (virt >> 22);
unsigned int table_entry = (virt >> 12) & 0x03ff;
int i;
unsigned int *pd_map = (unsigned int *)0xfffff000;
unsigned int *pt_map = (unsigned int *)0xffc00000 + (0x400 * dir_entry);
if ((pd_map[dir_entry] & 1) == 0) {
// table doesnt exist, create it.
pd_map[dir_entry] = (unsigned int)mem_alloc() | flags;
ivld_tlb(virt);
for (i=0;i<1023;i++) {
pt_map[i] = 0x02;
}
pt_map[1023] = pt_map;
}
pt_map[table_entry] = phys | flags;
ivld_tlb(virt);
return (unsigned char *)virt;
}
unsigned char *mem_map_framebuffer(unsigned int phys, unsigned int fb_length) {
unsigned int virt = 0xe0000000;
int i, j;
unsigned int start = phys & 0xFFFFF000;
framebuffer_start = start;
framebuffer_len = round_up_to_page(fb_length);
for (i=start;i<start+fb_length;i+= 0x1000) {
unsigned int dir_entry = (virt >> 22);
unsigned int table_entry = (virt >> 12) & 0x03ff;
unsigned int *pd_map = (unsigned int *)0xfffff000;
unsigned int *pt_map = (unsigned int *)0xffc00000 + (0x400 * dir_entry);
if ((pd_map[dir_entry] & 1) == 0) {
// table doesnt exist, create it.
pd_map[dir_entry] = (unsigned int)mem_alloc() | 3;
ivld_tlb(virt);
for (j=0;j<1023;j++) {
pt_map[j] = 0x2;
}
}
pt_map[table_entry] = i | 3;
ivld_tlb(virt);
virt += 0x1000;
}
return (char *)0xe0000000;
}
int mem_map_user_page(unsigned int virt) {
unsigned int dir_entry = (virt >> 22);
unsigned int table_entry = (virt >> 12) & 0x03ff;
unsigned int *pd_map = (unsigned int *)0xfffff000;
unsigned int *pt_map = (unsigned int *)0xffc00000 + (0x400 * dir_entry);
int i;
if (virt > current_task->user_pages_at) {
return 0;
}
if ((pd_map[dir_entry] & 1) == 0) {
// table doesnt exist, create it.
pd_map[dir_entry] = (unsigned int)mem_alloc() | 7;
ivld_tlb(virt);
for (i=0;i<1023;i++) {
pt_map[i] = 0x2;
}
}
current_task->user_pages_cnt++;
if (current_task->user_pages == (void *)0) {
current_task->user_pages = (unsigned int *)malloc(current_task->user_pages_cnt * sizeof(unsigned int));
} else {
current_task->user_pages = (unsigned int *)realloc(current_task->user_pages, current_task->user_pages_cnt * sizeof(unsigned int));
}
current_task->user_pages[current_task->user_pages_cnt - 1] = (unsigned int)mem_alloc();
pt_map[table_entry] = (unsigned int)current_task->user_pages[current_task->user_pages_cnt - 1] | 7;
ivld_tlb(virt);
memset((unsigned char *)(virt & 0xFFFFF000), 0, 0x1000);
return 1;
}
int mem_map_krnl_page(unsigned int virt) {
unsigned int dir_entry = (virt >> 22);
unsigned int table_entry = (virt >> 12) & 0x03ff;
unsigned int *pt_map = (unsigned int *)0xffc00000 + (0x400 * dir_entry);
if (virt > (unsigned int)kernel_ds_at) {
return 0;
}
pt_map[table_entry] = (unsigned int)mem_alloc() | 3;
ivld_tlb(virt);
return 1;
}
#define KERNEL_DS_START 0xd0000000
unsigned int mem_pci_sbrk(unsigned int amount) {
unsigned int mem = (unsigned int)pci_mem_at;
if (pci_mem_at < 0x29000000) {
pci_mem_at += round_up_to_page(amount);
return mem;
}
return 0;
}
unsigned int mem_usr_sbrk(int amount) {
unsigned int previous_ds;
previous_ds = current_task->user_pages_at;
if (amount <= 0) {
return previous_ds;
}
current_task->user_pages_at += amount;
return previous_ds;
}
void *sbrk(int amount) {
unsigned char *previous_ds;
if (kernel_ds_at == 0) {
kernel_ds_at = (unsigned char *)KERNEL_DS_START;
}
if (amount <= 0) {
if (initialized) {
kprintf("trying to free kernel memory\n");
}
return previous_ds;
}
if (kernel_ds_at + amount >=0xe0000000) {
kprintf("Out of kernel memory :(\n");
return 0;
}
previous_ds = kernel_ds_at;
kernel_ds_at += amount;
return previous_ds;
}
int brk(void *end_data_segment) {
unsigned char *req_brk = end_data_segment;
if (req_brk > (unsigned char *)0xd0000000 && req_brk <= (unsigned char *)0xe0000000) {
kernel_ds_at = req_brk;
return 0;
}
return -1;
}
char *db_malloc_caller;
void *dbmalloc(int size, char *caller) {
db_malloc_caller = caller;
return malloc(size);
}
void *dbrealloc(char *ptr, int size, char *caller) {
db_malloc_caller = caller;
return realloc(ptr, size);
}
void dbfree(char *ptr, char *caller) {
db_malloc_caller = caller;
free(ptr);
}
#ifdef MEM_DEBUG
unsigned int mem_get_info(struct mem_info *info) {
info->total = free_page_count * PAGE_SIZE;
info->used = 0;
int i;
for (i=0;i<free_page_count;i++) {
if (pageframes[i].pid != -1) {
info->used += PAGE_SIZE;
}
}
return 1;
}
#else
unsigned int mem_get_info(struct mem_info *info) {
info->total = free_page_count * PAGE_SIZE;
info->used = 0;
unsigned long mask, offset, index, i;
for (i=0;i<free_page_count;i++) {
mask = 1;
index = i / 8;
offset = i % 8;
if (mem_bitmap[index] & (mask << offset)) {
info->used += PAGE_SIZE;
}
}
return 1;
}
#endif