1433d6423SLionel Sambuc 2433d6423SLionel Sambuc #include <minix/cpufeature.h> 3433d6423SLionel Sambuc 4433d6423SLionel Sambuc #include <assert.h> 5433d6423SLionel Sambuc #include "kernel/kernel.h" 6433d6423SLionel Sambuc #include "arch_proto.h" 7433d6423SLionel Sambuc 8433d6423SLionel Sambuc #include <string.h> 9433d6423SLionel Sambuc 10433d6423SLionel Sambuc /* These are set/computed in kernel.lds. */ 11433d6423SLionel Sambuc extern char _kern_vir_base, _kern_phys_base, _kern_size; 12433d6423SLionel Sambuc 13433d6423SLionel Sambuc /* Retrieve the absolute values to something we can use. */ 14433d6423SLionel Sambuc static phys_bytes kern_vir_start = (phys_bytes) &_kern_vir_base; 15433d6423SLionel Sambuc static phys_bytes kern_phys_start = (phys_bytes) &_kern_phys_base; 16433d6423SLionel Sambuc static phys_bytes kern_kernlen = (phys_bytes) &_kern_size; 17433d6423SLionel Sambuc 18433d6423SLionel Sambuc /* page directory we can use to map things */ 19433d6423SLionel Sambuc static u32_t pagedir[1024] __aligned(4096); 20433d6423SLionel Sambuc 21433d6423SLionel Sambuc void print_memmap(kinfo_t *cbi) 22433d6423SLionel Sambuc { 23433d6423SLionel Sambuc int m; 24433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 25433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 26433d6423SLionel Sambuc phys_bytes addr = cbi->memmap[m].mm_base_addr, endit = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 27433d6423SLionel Sambuc printf("%08lx-%08lx ",addr, endit); 28433d6423SLionel Sambuc } 29433d6423SLionel Sambuc printf("\nsize %08lx\n", cbi->mmap_size); 30433d6423SLionel Sambuc } 31433d6423SLionel Sambuc 32433d6423SLionel Sambuc void cut_memmap(kinfo_t *cbi, phys_bytes start, phys_bytes end) 33433d6423SLionel Sambuc { 34433d6423SLionel Sambuc int m; 35433d6423SLionel Sambuc phys_bytes o; 36433d6423SLionel Sambuc 37433d6423SLionel Sambuc if((o=start % I386_PAGE_SIZE)) 38433d6423SLionel Sambuc start -= o; 39433d6423SLionel Sambuc if((o=end % I386_PAGE_SIZE)) 40433d6423SLionel Sambuc end += I386_PAGE_SIZE - o; 41433d6423SLionel Sambuc 42433d6423SLionel Sambuc assert(kernel_may_alloc); 43433d6423SLionel Sambuc 44433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 45433d6423SLionel Sambuc phys_bytes substart = start, subend = end; 46433d6423SLionel Sambuc phys_bytes memaddr = cbi->memmap[m].mm_base_addr, 47433d6423SLionel Sambuc memend = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 48433d6423SLionel Sambuc 49433d6423SLionel Sambuc /* adjust cut range to be a subset of the free memory */ 50433d6423SLionel Sambuc if(substart < memaddr) substart = memaddr; 51433d6423SLionel Sambuc if(subend > memend) subend = memend; 52433d6423SLionel Sambuc if(substart >= subend) continue; 53433d6423SLionel Sambuc 54433d6423SLionel Sambuc /* if there is any overlap, forget this one and add 55433d6423SLionel Sambuc * 1-2 subranges back 56433d6423SLionel Sambuc */ 57433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = cbi->memmap[m].mm_length = 0; 58433d6423SLionel Sambuc if(substart > memaddr) 59433d6423SLionel Sambuc add_memmap(cbi, memaddr, substart-memaddr); 60433d6423SLionel Sambuc if(subend < memend) 61433d6423SLionel Sambuc add_memmap(cbi, subend, memend-subend); 62433d6423SLionel Sambuc } 63433d6423SLionel Sambuc } 64433d6423SLionel Sambuc 65433d6423SLionel Sambuc phys_bytes alloc_lowest(kinfo_t *cbi, phys_bytes len) 66433d6423SLionel Sambuc { 67433d6423SLionel Sambuc /* Allocate the lowest physical page we have. */ 68433d6423SLionel Sambuc int m; 69433d6423SLionel Sambuc #define EMPTY 0xffffffff 70433d6423SLionel Sambuc phys_bytes lowest = EMPTY; 71433d6423SLionel Sambuc assert(len > 0); 72433d6423SLionel Sambuc len = roundup(len, I386_PAGE_SIZE); 73433d6423SLionel Sambuc 74433d6423SLionel Sambuc assert(kernel_may_alloc); 75433d6423SLionel Sambuc 76433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 77433d6423SLionel Sambuc if(cbi->memmap[m].mm_length < len) continue; 78433d6423SLionel Sambuc if(cbi->memmap[m].mm_base_addr < lowest) lowest = cbi->memmap[m].mm_base_addr; 79433d6423SLionel Sambuc } 80433d6423SLionel Sambuc assert(lowest != EMPTY); 81433d6423SLionel Sambuc cut_memmap(cbi, lowest, len); 82433d6423SLionel Sambuc cbi->kernel_allocated_bytes_dynamic += len; 83433d6423SLionel Sambuc return lowest; 84433d6423SLionel Sambuc } 85433d6423SLionel Sambuc 86433d6423SLionel Sambuc void add_memmap(kinfo_t *cbi, u64_t addr, u64_t len) 87433d6423SLionel Sambuc { 88433d6423SLionel Sambuc int m; 89433d6423SLionel Sambuc #define LIMIT 0xFFFFF000 90433d6423SLionel Sambuc /* Truncate available memory at 4GB as the rest of minix 91433d6423SLionel Sambuc * currently can't deal with any bigger. 92433d6423SLionel Sambuc */ 93433d6423SLionel Sambuc if(addr > LIMIT) return; 94433d6423SLionel Sambuc if(addr + len > LIMIT) { 95433d6423SLionel Sambuc len -= (addr + len - LIMIT); 96433d6423SLionel Sambuc } 97433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 98433d6423SLionel Sambuc if(len == 0) return; 99433d6423SLionel Sambuc addr = roundup(addr, I386_PAGE_SIZE); 100433d6423SLionel Sambuc len = rounddown(len, I386_PAGE_SIZE); 101433d6423SLionel Sambuc 102433d6423SLionel Sambuc assert(kernel_may_alloc); 103433d6423SLionel Sambuc 104433d6423SLionel Sambuc for(m = 0; m < MAXMEMMAP; m++) { 105433d6423SLionel Sambuc phys_bytes highmark; 106433d6423SLionel Sambuc if(cbi->memmap[m].mm_length) continue; 107433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = addr; 108433d6423SLionel Sambuc cbi->memmap[m].mm_length = len; 109433d6423SLionel Sambuc cbi->memmap[m].mm_type = MULTIBOOT_MEMORY_AVAILABLE; 110433d6423SLionel Sambuc if(m >= cbi->mmap_size) 111433d6423SLionel Sambuc cbi->mmap_size = m+1; 112433d6423SLionel Sambuc highmark = addr + len; 113433d6423SLionel Sambuc if(highmark > cbi->mem_high_phys) { 114433d6423SLionel Sambuc cbi->mem_high_phys = highmark; 115433d6423SLionel Sambuc } 116433d6423SLionel Sambuc 117433d6423SLionel Sambuc return; 118433d6423SLionel Sambuc } 119433d6423SLionel Sambuc 120433d6423SLionel Sambuc panic("no available memmap slot"); 121433d6423SLionel Sambuc } 122433d6423SLionel Sambuc 123433d6423SLionel Sambuc u32_t *alloc_pagetable(phys_bytes *ph) 124433d6423SLionel Sambuc { 125433d6423SLionel Sambuc u32_t *ret; 126433d6423SLionel Sambuc #define PG_PAGETABLES 6 127433d6423SLionel Sambuc static u32_t pagetables[PG_PAGETABLES][1024] __aligned(4096); 128433d6423SLionel Sambuc static int pt_inuse = 0; 129433d6423SLionel Sambuc if(pt_inuse >= PG_PAGETABLES) panic("no more pagetables"); 130433d6423SLionel Sambuc assert(sizeof(pagetables[pt_inuse]) == I386_PAGE_SIZE); 131433d6423SLionel Sambuc ret = pagetables[pt_inuse++]; 132433d6423SLionel Sambuc *ph = vir2phys(ret); 133433d6423SLionel Sambuc return ret; 134433d6423SLionel Sambuc } 135433d6423SLionel Sambuc 136433d6423SLionel Sambuc #define PAGE_KB (I386_PAGE_SIZE / 1024) 137433d6423SLionel Sambuc 138433d6423SLionel Sambuc phys_bytes pg_alloc_page(kinfo_t *cbi) 139433d6423SLionel Sambuc { 140433d6423SLionel Sambuc int m; 141433d6423SLionel Sambuc multiboot_memory_map_t *mmap; 142433d6423SLionel Sambuc 143433d6423SLionel Sambuc assert(kernel_may_alloc); 144433d6423SLionel Sambuc 145433d6423SLionel Sambuc for(m = cbi->mmap_size-1; m >= 0; m--) { 146433d6423SLionel Sambuc mmap = &cbi->memmap[m]; 147433d6423SLionel Sambuc if(!mmap->mm_length) continue; 148433d6423SLionel Sambuc assert(mmap->mm_length > 0); 149433d6423SLionel Sambuc assert(!(mmap->mm_length % I386_PAGE_SIZE)); 150433d6423SLionel Sambuc assert(!(mmap->mm_base_addr % I386_PAGE_SIZE)); 151433d6423SLionel Sambuc 152433d6423SLionel Sambuc mmap->mm_length -= I386_PAGE_SIZE; 153433d6423SLionel Sambuc 154433d6423SLionel Sambuc cbi->kernel_allocated_bytes_dynamic += I386_PAGE_SIZE; 155433d6423SLionel Sambuc 156433d6423SLionel Sambuc return mmap->mm_base_addr + mmap->mm_length; 157433d6423SLionel Sambuc } 158433d6423SLionel Sambuc 159433d6423SLionel Sambuc panic("can't find free memory"); 160433d6423SLionel Sambuc } 161433d6423SLionel Sambuc 162433d6423SLionel Sambuc void pg_identity(kinfo_t *cbi) 163433d6423SLionel Sambuc { 164*0a6a1f1dSLionel Sambuc uint32_t i; 165433d6423SLionel Sambuc phys_bytes phys; 166433d6423SLionel Sambuc 167433d6423SLionel Sambuc /* We map memory that does not correspond to physical memory 168433d6423SLionel Sambuc * as non-cacheable. Make sure we know what it is. 169433d6423SLionel Sambuc */ 170433d6423SLionel Sambuc assert(cbi->mem_high_phys); 171433d6423SLionel Sambuc 172433d6423SLionel Sambuc /* Set up an identity mapping page directory */ 173433d6423SLionel Sambuc for(i = 0; i < I386_VM_DIR_ENTRIES; i++) { 174433d6423SLionel Sambuc u32_t flags = I386_VM_PRESENT | I386_VM_BIGPAGE 175433d6423SLionel Sambuc | I386_VM_USER 176433d6423SLionel Sambuc | I386_VM_WRITE; 177433d6423SLionel Sambuc phys = i * I386_BIG_PAGE_SIZE; 178433d6423SLionel Sambuc if((cbi->mem_high_phys & I386_VM_ADDR_MASK_4MB) 179433d6423SLionel Sambuc <= (phys & I386_VM_ADDR_MASK_4MB)) { 180433d6423SLionel Sambuc flags |= I386_VM_PWT | I386_VM_PCD; 181433d6423SLionel Sambuc } 182433d6423SLionel Sambuc pagedir[i] = phys | flags; 183433d6423SLionel Sambuc } 184433d6423SLionel Sambuc } 185433d6423SLionel Sambuc 186433d6423SLionel Sambuc int pg_mapkernel(void) 187433d6423SLionel Sambuc { 188433d6423SLionel Sambuc int pde; 189433d6423SLionel Sambuc u32_t mapped = 0, kern_phys = kern_phys_start; 190433d6423SLionel Sambuc 191433d6423SLionel Sambuc assert(!(kern_vir_start % I386_BIG_PAGE_SIZE)); 192433d6423SLionel Sambuc assert(!(kern_phys % I386_BIG_PAGE_SIZE)); 193433d6423SLionel Sambuc pde = kern_vir_start / I386_BIG_PAGE_SIZE; /* start pde */ 194433d6423SLionel Sambuc while(mapped < kern_kernlen) { 195433d6423SLionel Sambuc pagedir[pde] = kern_phys | I386_VM_PRESENT | 196433d6423SLionel Sambuc I386_VM_BIGPAGE | I386_VM_WRITE; 197433d6423SLionel Sambuc mapped += I386_BIG_PAGE_SIZE; 198433d6423SLionel Sambuc kern_phys += I386_BIG_PAGE_SIZE; 199433d6423SLionel Sambuc pde++; 200433d6423SLionel Sambuc } 201433d6423SLionel Sambuc return pde; /* free pde */ 202433d6423SLionel Sambuc } 203433d6423SLionel Sambuc 204433d6423SLionel Sambuc void vm_enable_paging(void) 205433d6423SLionel Sambuc { 206433d6423SLionel Sambuc u32_t cr0, cr4; 207433d6423SLionel Sambuc int pgeok; 208433d6423SLionel Sambuc 209433d6423SLionel Sambuc pgeok = _cpufeature(_CPUF_I386_PGE); 210433d6423SLionel Sambuc 211433d6423SLionel Sambuc cr0= read_cr0(); 212433d6423SLionel Sambuc cr4= read_cr4(); 213433d6423SLionel Sambuc 214433d6423SLionel Sambuc /* The boot loader should have put us in protected mode. */ 215433d6423SLionel Sambuc assert(cr0 & I386_CR0_PE); 216433d6423SLionel Sambuc 217433d6423SLionel Sambuc /* First clear PG and PGE flag, as PGE must be enabled after PG. */ 218433d6423SLionel Sambuc write_cr0(cr0 & ~I386_CR0_PG); 219433d6423SLionel Sambuc write_cr4(cr4 & ~(I386_CR4_PGE | I386_CR4_PSE)); 220433d6423SLionel Sambuc 221433d6423SLionel Sambuc cr0= read_cr0(); 222433d6423SLionel Sambuc cr4= read_cr4(); 223433d6423SLionel Sambuc 224433d6423SLionel Sambuc /* Our page table contains 4MB entries. */ 225433d6423SLionel Sambuc cr4 |= I386_CR4_PSE; 226433d6423SLionel Sambuc 227433d6423SLionel Sambuc write_cr4(cr4); 228433d6423SLionel Sambuc 229433d6423SLionel Sambuc /* First enable paging, then enable global page flag. */ 230433d6423SLionel Sambuc cr0 |= I386_CR0_PG; 231433d6423SLionel Sambuc write_cr0(cr0); 232433d6423SLionel Sambuc cr0 |= I386_CR0_WP; 233433d6423SLionel Sambuc write_cr0(cr0); 234433d6423SLionel Sambuc 235433d6423SLionel Sambuc /* May we enable these features? */ 236433d6423SLionel Sambuc if(pgeok) 237433d6423SLionel Sambuc cr4 |= I386_CR4_PGE; 238433d6423SLionel Sambuc 239433d6423SLionel Sambuc write_cr4(cr4); 240433d6423SLionel Sambuc } 241433d6423SLionel Sambuc 242433d6423SLionel Sambuc phys_bytes pg_load() 243433d6423SLionel Sambuc { 244433d6423SLionel Sambuc phys_bytes phpagedir = vir2phys(pagedir); 245433d6423SLionel Sambuc write_cr3(phpagedir); 246433d6423SLionel Sambuc return phpagedir; 247433d6423SLionel Sambuc } 248433d6423SLionel Sambuc 249433d6423SLionel Sambuc void pg_clear(void) 250433d6423SLionel Sambuc { 251433d6423SLionel Sambuc memset(pagedir, 0, sizeof(pagedir)); 252433d6423SLionel Sambuc } 253433d6423SLionel Sambuc 254433d6423SLionel Sambuc phys_bytes pg_rounddown(phys_bytes b) 255433d6423SLionel Sambuc { 256433d6423SLionel Sambuc phys_bytes o; 257433d6423SLionel Sambuc if(!(o = b % I386_PAGE_SIZE)) 258433d6423SLionel Sambuc return b; 259433d6423SLionel Sambuc return b - o; 260433d6423SLionel Sambuc } 261433d6423SLionel Sambuc 262433d6423SLionel Sambuc void pg_map(phys_bytes phys, vir_bytes vaddr, vir_bytes vaddr_end, 263433d6423SLionel Sambuc kinfo_t *cbi) 264433d6423SLionel Sambuc { 265433d6423SLionel Sambuc static int mapped_pde = -1; 266433d6423SLionel Sambuc static u32_t *pt = NULL; 267433d6423SLionel Sambuc int pde, pte; 268433d6423SLionel Sambuc 269433d6423SLionel Sambuc assert(kernel_may_alloc); 270433d6423SLionel Sambuc 271433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 272433d6423SLionel Sambuc assert(!(vaddr % I386_PAGE_SIZE)); 273433d6423SLionel Sambuc } else { 274433d6423SLionel Sambuc assert((vaddr % I386_PAGE_SIZE) == (phys % I386_PAGE_SIZE)); 275433d6423SLionel Sambuc vaddr = pg_rounddown(vaddr); 276433d6423SLionel Sambuc phys = pg_rounddown(phys); 277433d6423SLionel Sambuc } 278433d6423SLionel Sambuc assert(vaddr < kern_vir_start); 279433d6423SLionel Sambuc 280433d6423SLionel Sambuc while(vaddr < vaddr_end) { 281433d6423SLionel Sambuc phys_bytes source = phys; 282433d6423SLionel Sambuc assert(!(vaddr % I386_PAGE_SIZE)); 283433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 284433d6423SLionel Sambuc source = pg_alloc_page(cbi); 285433d6423SLionel Sambuc } else { 286433d6423SLionel Sambuc assert(!(phys % I386_PAGE_SIZE)); 287433d6423SLionel Sambuc } 288433d6423SLionel Sambuc assert(!(source % I386_PAGE_SIZE)); 289433d6423SLionel Sambuc pde = I386_VM_PDE(vaddr); 290433d6423SLionel Sambuc pte = I386_VM_PTE(vaddr); 291433d6423SLionel Sambuc if(mapped_pde < pde) { 292433d6423SLionel Sambuc phys_bytes ph; 293433d6423SLionel Sambuc pt = alloc_pagetable(&ph); 294433d6423SLionel Sambuc pagedir[pde] = (ph & I386_VM_ADDR_MASK) 295433d6423SLionel Sambuc | I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE; 296433d6423SLionel Sambuc mapped_pde = pde; 297433d6423SLionel Sambuc } 298433d6423SLionel Sambuc assert(pt); 299433d6423SLionel Sambuc pt[pte] = (source & I386_VM_ADDR_MASK) | 300433d6423SLionel Sambuc I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE; 301433d6423SLionel Sambuc vaddr += I386_PAGE_SIZE; 302433d6423SLionel Sambuc if(phys != PG_ALLOCATEME) 303433d6423SLionel Sambuc phys += I386_PAGE_SIZE; 304433d6423SLionel Sambuc } 305433d6423SLionel Sambuc } 306433d6423SLionel Sambuc 307433d6423SLionel Sambuc void pg_info(reg_t *pagedir_ph, u32_t **pagedir_v) 308433d6423SLionel Sambuc { 309433d6423SLionel Sambuc *pagedir_ph = vir2phys(pagedir); 310433d6423SLionel Sambuc *pagedir_v = pagedir; 311433d6423SLionel Sambuc } 312433d6423SLionel Sambuc 313