#include "types.h"#include "vm2.h"#include "lowlevel.h"/*Ok, so the memory system is a bit weird, due to not wanting to simply map all of physical ram into the kernel space.The top 12mb of the address space are reserved for the VM:top 4mb: recursive mapping of page directory.next 4mb: mapping of 'guest' directory - used for cloning etc.next 4mb: mapping of random pages, in order to copy data between programs.The hope is we might be able to get the kernel to only occupy 32mb at the top of the address space, and not put limits on the amount of memory accessible to user mode. (more than that is going to be used by hardware anyway, and I'm notdoing paging to disk).*/// PhysMem free_memory;typedef struct table_entry{ u32 present : 1; // Page present in memory u32 rw : 1; // Read-only if clear, readwrite if set u32 user : 1; // Supervisor level only if clear u32 accessed : 1; // Has the page been accessed since last refresh? u32 dirty : 1; // Has the page been written to since last refresh? u32 unused : 7; // Amalgamation of unused and reserved bits u32 frame : 20; // Frame address (shifted right 12 bits)} table_entry;typedef struct { u32 present:1; // whether it is in memory. u32 writable:1; // 1 if it's writable. u32 user:1; // 1 if it's a user page, 0 for supervisor. u32 write_through:1; // don't bother u32 cache_disabled:1; // don't bother u32 accessed:1; // whether the 4mb block has been accessed since clearing this flag. u32 dirty:1; // whether it's been written to. u32 superpage:1; // 1 for 4mb, 2 otherwise. u32 global:1; u32 available:3; // must think of something to put here (reference count?) u32 frame:20; // top 10 bit of address in physical ram.} dir_entry;static table_entry table_entry_set(u32 phys_addr, bool user, bool write, bool exec);// static u32 dir_entry_get_base_address(dir_entry d);static dir_entry dir_entry_set(u32 phys_addr, bool user, bool write);static void vm_invalidate_addr(void *virt);static dir_entry* vm_get_dir_entry(u32 virt);static table_entry *vm_get_page_table(u32 virt);table_entry *vm_ensure_page_table_priv(u32 virt, bool user);void vm_ensure_page_table(u32 virt,bool user) { vm_ensure_page_table_priv(virt,user);}void vm_map(u32 phys, void* virt, bool user, bool write, bool exec);bool vm_verify_range(void* start, u32 len, bool user, bool write, bool exec);void vm_destroy_address_space(void);u32 vm_virt_to_phys(u32 virt);void vm_switch_address_space(AddressSpace physical_address);AddressSpace vm_clone(void);u32 vm_unmap(u32 virt); /** * Gets where this points in physical ram */// u32 dir_entry_get_base_address(dir_entry d) {// return d.frame<<12;// } dir_entry dir_entry_set(u32 phys_addr,bool user,bool write) { dir_entry d; zero(d); d.frame=phys_addr>>12; d.present=1; d.writable=write?1:0; d.user=user?1:0; return d; }// first PDE of kernel address space.#define KERNEL_PDE_START 768// 1111 1111 1100 0000 0000 | 0000 0000 0000 /* The top 12 mb of the address space are reserved. top 4mb: recursive mapping of current page directory. next 4mb: mapping of foreign page dir. next 4mb: map a page table (a 4mb chunk) from the foreign task. */ // the page directory for the current address space.static dir_entry* current_page_dir= (dir_entry*)0xFFFFF000;// the page tables for the current address space.static table_entry* current_page_tables = (table_entry*)0xFFC00000;// the foreign directory entry mapped.static dir_entry* other_page_dir = (dir_entry*)0xFFFFE000;// the page tables for the foreign address space.static table_entry* other_page_tables = (table_entry*)0xFF800000;static void* other_memory_area = (void*)0xff400000;// static dir_entry* spare_directory=(dir_entry*) 0xFF600000;// static table_entry* spare_page=(table_entry*) 0xFF600000;// static table_entry* spare_page_data=(table_entry*) 0xFF601000;// static dir_entry* other_page_dir2 = (dir_entry*)0xFFBFF000;static void vm_destroy_page_table(u32 i) { table_entry*pt=vm_get_page_table(i<<22); for(int te=0;te<1024;++te) { if(pt[te].present) { vm_phys_free_page(pt[te].frame<<12); zero(pt[te]); } } vm_phys_free_page(current_page_dir[i].frame<<12);}static table_entry table_entry_set(u32 phys_addr,bool user,bool write,bool exec) { table_entry p; zero(p); p.present=1; p.rw=write?1:0; p.user=user?1:0; p.frame=phys_addr>>12; return p;}void vm_map_anon(void*addr,bool user,bool write,bool exec) { u32 phys = vm_phys_get_page(); vm_map(phys,addr,user,write,exec);}void vm_destroy_address_space() { for(int i=0;i<KERNEL_PDE_START;++i) { // go up to the kernel's base address. if(current_page_dir[i].present) { if(current_page_dir[i].superpage) PANIC("WE CAN'T HANDLE SUPERPAGES!"); // kill off the page table. vm_destroy_page_table(i); zero(current_page_dir[i]); } } // the 'spare' mappings. if(current_page_dir[1022].present) { // this doesn't want to be done, as the page table mapped is the // page directory of some other process.... } //directory_entry&pde=current_page_dir[1022];}u32 vm_virt_to_phys(u32 virt) { u32 pte = (virt>>12) & 0x3ff; return vm_get_page_table(virt)[pte].frame<<12;}void vm_switch_address_space(AddressSpace physical_address) { u32 curr=(current_page_dir[1023].frame<<12); if(curr!=physical_address) { asm volatile("mov %0, %%cr3":: "r"(physical_address)); }}void vm_invalidate_addr(void* virt) { asm volatile("invlpg %0"::"m" (*(char *)virt));}/*u32 src,dest; dpt = dest>>22; // destination page table index. dp = (dest >> 12)&01777; // the page number in the table di = dest&0xfff; // start index into page. if(!other_page_dir[dpt].present) fail. vm_map(other_page_dir[dpt].frame<<12,spare_page,0,1,1); if(!spare_page[dp].present) fail. vm_map(spare_page[dp],spare_page_data,0,1,1); table_entry* other=other_page_tables+(pt<<10); table_entry* current=page_tables+(pt<<10); vm_map(otherp,(u32)spare_page,0,1,1);*/void* map_foreign_page(u32 dest) { u32 dpt = dest>>22; // destination page table index. u32 dp = (dest >> 12)&01777; // the page number in the table u32 di = dest&0xfff; // start index into page. if(!other_page_dir[dpt].present) return 0; table_entry* other=other_page_tables+(dpt<<10); if(!other[dp].present) return 0; current_page_dir[1021]=other_page_dir[dpt]; return (void*) (dp<<12|di);}static void vm_copy_page_table(u32 pt) { table_entry* other=other_page_tables+(pt<<10); table_entry* current=current_page_tables+(pt<<10); current_page_dir[1021]=other_page_dir[pt]; // map it into other_memory_area for(int i=0;i<1024;++i) { other[i]=current[i]; if(current[i].present) { // get a new page and allocate it. u32 newp=vm_phys_get_page(); other[i].frame=newp>>12; // where in this address space the other page is mapped. u32 other_virt_address = (u32)other_memory_area+(pt*4096); // the address we're copying from. u32 current_virt_address = pt<<22|i<<12; // invalidate tlb. vm_invalidate_addr((void*)other_virt_address);// copy the data over. memcpy((void*)other_virt_address,(void*)current_virt_address,4096); } }}void map_foreign_pgdir(AddressSpace npd) { current_page_dir[1022]=dir_entry_set(npd,0,1); vm_invalidate_addr(other_page_dir+1023);}/*void* map_foreign_page(u32 addr) { u32 dpt = addr>>22; // destination page table index. u32 dp = (addr >> 12)&01777; // the page number in the table if(!other_page_dir[dpt].present) return 0; vm_map(other_page_dir[dpt].frame<<12,spare_page,0,1,1); if(!spare_page[dp].present) return 0; vm_map(spare_page[dp].frame<<12,spare_page_data,0,1,1);return spare_page_data;}*/AddressSpace vm_clone() { u32 npd=vm_phys_get_page(); map_foreign_pgdir(npd); memset(other_page_dir,0,1<<22); // zero it. // flush tlb vm_invalidate_addr(other_page_dir+1023); // set the recursive mapping other_page_dir[1023]=dir_entry_set(npd,0,1); // get access to the change (the page tables.) vm_invalidate_addr(other_page_tables+(1023<<10)); for(int i=0;i<KERNEL_PDE_START;++i) { if(current_page_dir[i].present) { u32 npt=vm_phys_get_page(); other_page_dir[i]=current_page_dir[i]; other_page_dir[i].frame=npt>>12; other_page_dir[i].present=1; flush_tlb(); vm_copy_page_table(i); } else { zero(other_page_dir[i]); } } for(int i=KERNEL_PDE_START;i<1020;++i) { other_page_dir[i]=current_page_dir[i]; } return npd;}// gets the page directory entry associated with the virtual address addr.dir_entry* vm_get_dir_entry(u32 virt) { return current_page_dir+(virt>>22);}static table_entry* vm_get_page_table(u32 virt) { //u32 offset = virt&0xfff; u32 pde = virt>>22; //u32 pte = (virt>>12) & 0x3ff; return current_page_tables+(pde<<10);}// make sure there's a page table at the relevant address.table_entry* vm_ensure_page_table_priv(u32 virt,bool user) { dir_entry* entry=vm_get_dir_entry(virt); if(!entry->present) { u32 pt=vm_phys_get_page(); *entry=dir_entry_set(pt,user,1); table_entry*t=vm_get_page_table(virt); vm_invalidate_addr(t); memset(t,0,4096); return t; } return vm_get_page_table(virt);}// map phys at virt with the relevant protections.void vm_map(u32 phys,void* virtu,bool user,bool write,bool exec) { u32 virt=(u32)virtu; table_entry*pt=vm_ensure_page_table_priv(virt,user); pt[(virt>>12)&0x3ff]= table_entry_set(phys,user,write,exec); vm_invalidate_addr((void*)virt);}#define PAGE_ALIGN(x) ((x)&0xFFFFE000)bool vm_verify_range(void* _start,u32 len,bool user,bool write,bool exec) { u32 start = (u32)_start; u32 curr=PAGE_ALIGN(start); u32 end=PAGE_ALIGN(start+len); if(end<curr) return true; while(curr<=end) { dir_entry dirent=*vm_get_dir_entry(curr);// con->printf("Verifying page directory at %x\n",curr); // check the page directory permissions first. if(!dirent.present) return true; if(user && !dirent.user) return true; if(write && !dirent.writable) return true; table_entry* pt = vm_get_page_table(curr); u32 pdend=((curr>>22)+1)<<22; while(curr<=pdend && curr <= end) {// con->printf("Table entry: %x\n",pt[(curr>>12)&0x3ff]); table_entry te=(pt[(curr>>12)&0x3ff]); if(!te.present) return true; if(user && !te.user) return true; if(write && !te.rw) return true; curr+=0x1000; } } return false;}u32 vm_unmap(u32 virt) { table_entry*pt = vm_get_page_table(virt)+(virt&0xfff); u32 frame = pt->frame; pt->present=0; return frame>>12;}// these are at the heart of the IPC.// copy between address spaces.MUST_CHECK int vm_copy_range_to(AddressSpace foreign,void* src,void*dest,u32 len) { // first make sure the area is valid in this address space. if(vm_verify_range(dest,len,0,0,0)) { // not valid return 1; } map_foreign_pgdir(foreign); // this has the address the page ended up on. void* addr = map_foreign_page((u32)src); if(!addr) return 1; if((u32)other_page_tables<=(u32)addr+len) { // need to span multiple page tables. return 1; } // now do the copy. memcpy(dest,addr,len); return 0;}// these are at the heart of the IPC.// copy between address spaces.int vm_copy_range_from(AddressSpace foreign,void* src,void*dest,u32 len) { // first make sure the area is valid in this address space. if(vm_verify_range(src,len,0,0,0)) { // not valid return 1; } map_foreign_pgdir(foreign); // this has the address the page ended up on. void* addr = map_foreign_page((u32)dest); if(!addr) return 1; if((u32)other_page_tables<=(u32)addr+len) { // need to span multiple page tables. return 1; } // now do the copy. memcpy(addr,src,len); return 0;}