1*433d6423SLionel Sambuc 2*433d6423SLionel Sambuc #include <minix/cpufeature.h> 3*433d6423SLionel Sambuc 4*433d6423SLionel Sambuc #include <assert.h> 5*433d6423SLionel Sambuc #include "kernel/kernel.h" 6*433d6423SLionel Sambuc #include "arch_proto.h" 7*433d6423SLionel Sambuc 8*433d6423SLionel Sambuc #include <string.h> 9*433d6423SLionel Sambuc 10*433d6423SLionel Sambuc /* These are set/computed in kernel.lds. */ 11*433d6423SLionel Sambuc extern char _kern_vir_base, _kern_phys_base, _kern_size; 12*433d6423SLionel Sambuc 13*433d6423SLionel Sambuc /* Retrieve the absolute values to something we can use. */ 14*433d6423SLionel Sambuc static phys_bytes kern_vir_start = (phys_bytes) &_kern_vir_base; 15*433d6423SLionel Sambuc static phys_bytes kern_phys_start = (phys_bytes) &_kern_phys_base; 16*433d6423SLionel Sambuc static phys_bytes kern_kernlen = (phys_bytes) &_kern_size; 17*433d6423SLionel Sambuc 18*433d6423SLionel Sambuc /* page directory we can use to map things */ 19*433d6423SLionel Sambuc static u32_t pagedir[1024] __aligned(4096); 20*433d6423SLionel Sambuc 21*433d6423SLionel Sambuc void print_memmap(kinfo_t *cbi) 22*433d6423SLionel Sambuc { 23*433d6423SLionel Sambuc int m; 24*433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 25*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 26*433d6423SLionel Sambuc phys_bytes addr = cbi->memmap[m].mm_base_addr, endit = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 27*433d6423SLionel Sambuc printf("%08lx-%08lx ",addr, endit); 28*433d6423SLionel Sambuc } 29*433d6423SLionel Sambuc printf("\nsize %08lx\n", cbi->mmap_size); 30*433d6423SLionel Sambuc } 31*433d6423SLionel Sambuc 32*433d6423SLionel Sambuc void cut_memmap(kinfo_t *cbi, phys_bytes start, phys_bytes end) 33*433d6423SLionel Sambuc { 34*433d6423SLionel Sambuc int m; 35*433d6423SLionel Sambuc phys_bytes o; 36*433d6423SLionel Sambuc 37*433d6423SLionel Sambuc if((o=start % I386_PAGE_SIZE)) 38*433d6423SLionel Sambuc start -= o; 39*433d6423SLionel Sambuc if((o=end % I386_PAGE_SIZE)) 40*433d6423SLionel Sambuc end += I386_PAGE_SIZE - o; 41*433d6423SLionel Sambuc 42*433d6423SLionel Sambuc assert(kernel_may_alloc); 43*433d6423SLionel Sambuc 44*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 45*433d6423SLionel Sambuc phys_bytes substart = start, subend = end; 46*433d6423SLionel Sambuc phys_bytes memaddr = cbi->memmap[m].mm_base_addr, 47*433d6423SLionel Sambuc memend = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 48*433d6423SLionel Sambuc 49*433d6423SLionel Sambuc /* adjust cut range to be a subset of the free memory */ 50*433d6423SLionel Sambuc if(substart < memaddr) substart = memaddr; 51*433d6423SLionel Sambuc if(subend > memend) subend = memend; 52*433d6423SLionel Sambuc if(substart >= subend) continue; 53*433d6423SLionel Sambuc 54*433d6423SLionel Sambuc /* if there is any overlap, forget this one and add 55*433d6423SLionel Sambuc * 1-2 subranges back 56*433d6423SLionel Sambuc */ 57*433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = cbi->memmap[m].mm_length = 0; 58*433d6423SLionel Sambuc if(substart > memaddr) 59*433d6423SLionel Sambuc add_memmap(cbi, memaddr, substart-memaddr); 60*433d6423SLionel Sambuc if(subend < memend) 61*433d6423SLionel Sambuc add_memmap(cbi, subend, memend-subend); 62*433d6423SLionel Sambuc } 63*433d6423SLionel Sambuc } 64*433d6423SLionel Sambuc 65*433d6423SLionel Sambuc phys_bytes alloc_lowest(kinfo_t *cbi, phys_bytes len) 66*433d6423SLionel Sambuc { 67*433d6423SLionel Sambuc /* Allocate the lowest physical page we have. */ 68*433d6423SLionel Sambuc int m; 69*433d6423SLionel Sambuc #define EMPTY 0xffffffff 70*433d6423SLionel Sambuc phys_bytes lowest = EMPTY; 71*433d6423SLionel Sambuc assert(len > 0); 72*433d6423SLionel Sambuc len = roundup(len, I386_PAGE_SIZE); 73*433d6423SLionel Sambuc 74*433d6423SLionel Sambuc assert(kernel_may_alloc); 75*433d6423SLionel Sambuc 76*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 77*433d6423SLionel Sambuc if(cbi->memmap[m].mm_length < len) continue; 78*433d6423SLionel Sambuc if(cbi->memmap[m].mm_base_addr < lowest) lowest = cbi->memmap[m].mm_base_addr; 79*433d6423SLionel Sambuc } 80*433d6423SLionel Sambuc assert(lowest != EMPTY); 81*433d6423SLionel Sambuc cut_memmap(cbi, lowest, len); 82*433d6423SLionel Sambuc cbi->kernel_allocated_bytes_dynamic += len; 83*433d6423SLionel Sambuc return lowest; 84*433d6423SLionel Sambuc } 85*433d6423SLionel Sambuc 86*433d6423SLionel Sambuc void add_memmap(kinfo_t *cbi, u64_t addr, u64_t len) 87*433d6423SLionel Sambuc { 88*433d6423SLionel Sambuc int m; 89*433d6423SLionel Sambuc #define LIMIT 0xFFFFF000 90*433d6423SLionel Sambuc /* Truncate available memory at 4GB as the rest of minix 91*433d6423SLionel Sambuc * currently can't deal with any bigger. 92*433d6423SLionel Sambuc */ 93*433d6423SLionel Sambuc if(addr > LIMIT) return; 94*433d6423SLionel Sambuc if(addr + len > LIMIT) { 95*433d6423SLionel Sambuc len -= (addr + len - LIMIT); 96*433d6423SLionel Sambuc } 97*433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 98*433d6423SLionel Sambuc if(len == 0) return; 99*433d6423SLionel Sambuc addr = roundup(addr, I386_PAGE_SIZE); 100*433d6423SLionel Sambuc len = rounddown(len, I386_PAGE_SIZE); 101*433d6423SLionel Sambuc 102*433d6423SLionel Sambuc assert(kernel_may_alloc); 103*433d6423SLionel Sambuc 104*433d6423SLionel Sambuc for(m = 0; m < MAXMEMMAP; m++) { 105*433d6423SLionel Sambuc phys_bytes highmark; 106*433d6423SLionel Sambuc if(cbi->memmap[m].mm_length) continue; 107*433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = addr; 108*433d6423SLionel Sambuc cbi->memmap[m].mm_length = len; 109*433d6423SLionel Sambuc cbi->memmap[m].mm_type = MULTIBOOT_MEMORY_AVAILABLE; 110*433d6423SLionel Sambuc if(m >= cbi->mmap_size) 111*433d6423SLionel Sambuc cbi->mmap_size = m+1; 112*433d6423SLionel Sambuc highmark = addr + len; 113*433d6423SLionel Sambuc if(highmark > cbi->mem_high_phys) { 114*433d6423SLionel Sambuc cbi->mem_high_phys = highmark; 115*433d6423SLionel Sambuc } 116*433d6423SLionel Sambuc 117*433d6423SLionel Sambuc return; 118*433d6423SLionel Sambuc } 119*433d6423SLionel Sambuc 120*433d6423SLionel Sambuc panic("no available memmap slot"); 121*433d6423SLionel Sambuc } 122*433d6423SLionel Sambuc 123*433d6423SLionel Sambuc u32_t *alloc_pagetable(phys_bytes *ph) 124*433d6423SLionel Sambuc { 125*433d6423SLionel Sambuc u32_t *ret; 126*433d6423SLionel Sambuc #define PG_PAGETABLES 6 127*433d6423SLionel Sambuc static u32_t pagetables[PG_PAGETABLES][1024] __aligned(4096); 128*433d6423SLionel Sambuc static int pt_inuse = 0; 129*433d6423SLionel Sambuc if(pt_inuse >= PG_PAGETABLES) panic("no more pagetables"); 130*433d6423SLionel Sambuc assert(sizeof(pagetables[pt_inuse]) == I386_PAGE_SIZE); 131*433d6423SLionel Sambuc ret = pagetables[pt_inuse++]; 132*433d6423SLionel Sambuc *ph = vir2phys(ret); 133*433d6423SLionel Sambuc return ret; 134*433d6423SLionel Sambuc } 135*433d6423SLionel Sambuc 136*433d6423SLionel Sambuc #define PAGE_KB (I386_PAGE_SIZE / 1024) 137*433d6423SLionel Sambuc 138*433d6423SLionel Sambuc phys_bytes pg_alloc_page(kinfo_t *cbi) 139*433d6423SLionel Sambuc { 140*433d6423SLionel Sambuc int m; 141*433d6423SLionel Sambuc multiboot_memory_map_t *mmap; 142*433d6423SLionel Sambuc 143*433d6423SLionel Sambuc assert(kernel_may_alloc); 144*433d6423SLionel Sambuc 145*433d6423SLionel Sambuc for(m = cbi->mmap_size-1; m >= 0; m--) { 146*433d6423SLionel Sambuc mmap = &cbi->memmap[m]; 147*433d6423SLionel Sambuc if(!mmap->mm_length) continue; 148*433d6423SLionel Sambuc assert(mmap->mm_length > 0); 149*433d6423SLionel Sambuc assert(!(mmap->mm_length % I386_PAGE_SIZE)); 150*433d6423SLionel Sambuc assert(!(mmap->mm_base_addr % I386_PAGE_SIZE)); 151*433d6423SLionel Sambuc 152*433d6423SLionel Sambuc mmap->mm_length -= I386_PAGE_SIZE; 153*433d6423SLionel Sambuc 154*433d6423SLionel Sambuc cbi->kernel_allocated_bytes_dynamic += I386_PAGE_SIZE; 155*433d6423SLionel Sambuc 156*433d6423SLionel Sambuc return mmap->mm_base_addr + mmap->mm_length; 157*433d6423SLionel Sambuc } 158*433d6423SLionel Sambuc 159*433d6423SLionel Sambuc panic("can't find free memory"); 160*433d6423SLionel Sambuc } 161*433d6423SLionel Sambuc 162*433d6423SLionel Sambuc void pg_identity(kinfo_t *cbi) 163*433d6423SLionel Sambuc { 164*433d6423SLionel Sambuc int i; 165*433d6423SLionel Sambuc phys_bytes phys; 166*433d6423SLionel Sambuc 167*433d6423SLionel Sambuc /* We map memory that does not correspond to physical memory 168*433d6423SLionel Sambuc * as non-cacheable. Make sure we know what it is. 169*433d6423SLionel Sambuc */ 170*433d6423SLionel Sambuc assert(cbi->mem_high_phys); 171*433d6423SLionel Sambuc 172*433d6423SLionel Sambuc /* Set up an identity mapping page directory */ 173*433d6423SLionel Sambuc for(i = 0; i < I386_VM_DIR_ENTRIES; i++) { 174*433d6423SLionel Sambuc u32_t flags = I386_VM_PRESENT | I386_VM_BIGPAGE 175*433d6423SLionel Sambuc | I386_VM_USER 176*433d6423SLionel Sambuc | I386_VM_WRITE; 177*433d6423SLionel Sambuc phys = i * I386_BIG_PAGE_SIZE; 178*433d6423SLionel Sambuc if((cbi->mem_high_phys & I386_VM_ADDR_MASK_4MB) 179*433d6423SLionel Sambuc <= (phys & I386_VM_ADDR_MASK_4MB)) { 180*433d6423SLionel Sambuc flags |= I386_VM_PWT | I386_VM_PCD; 181*433d6423SLionel Sambuc } 182*433d6423SLionel Sambuc pagedir[i] = phys | flags; 183*433d6423SLionel Sambuc } 184*433d6423SLionel Sambuc } 185*433d6423SLionel Sambuc 186*433d6423SLionel Sambuc int pg_mapkernel(void) 187*433d6423SLionel Sambuc { 188*433d6423SLionel Sambuc int pde; 189*433d6423SLionel Sambuc u32_t mapped = 0, kern_phys = kern_phys_start; 190*433d6423SLionel Sambuc 191*433d6423SLionel Sambuc assert(!(kern_vir_start % I386_BIG_PAGE_SIZE)); 192*433d6423SLionel Sambuc assert(!(kern_phys % I386_BIG_PAGE_SIZE)); 193*433d6423SLionel Sambuc pde = kern_vir_start / I386_BIG_PAGE_SIZE; /* start pde */ 194*433d6423SLionel Sambuc while(mapped < kern_kernlen) { 195*433d6423SLionel Sambuc pagedir[pde] = kern_phys | I386_VM_PRESENT | 196*433d6423SLionel Sambuc I386_VM_BIGPAGE | I386_VM_WRITE; 197*433d6423SLionel Sambuc mapped += I386_BIG_PAGE_SIZE; 198*433d6423SLionel Sambuc kern_phys += I386_BIG_PAGE_SIZE; 199*433d6423SLionel Sambuc pde++; 200*433d6423SLionel Sambuc } 201*433d6423SLionel Sambuc return pde; /* free pde */ 202*433d6423SLionel Sambuc } 203*433d6423SLionel Sambuc 204*433d6423SLionel Sambuc void vm_enable_paging(void) 205*433d6423SLionel Sambuc { 206*433d6423SLionel Sambuc u32_t cr0, cr4; 207*433d6423SLionel Sambuc int pgeok; 208*433d6423SLionel Sambuc 209*433d6423SLionel Sambuc pgeok = _cpufeature(_CPUF_I386_PGE); 210*433d6423SLionel Sambuc 211*433d6423SLionel Sambuc cr0= read_cr0(); 212*433d6423SLionel Sambuc cr4= read_cr4(); 213*433d6423SLionel Sambuc 214*433d6423SLionel Sambuc /* The boot loader should have put us in protected mode. */ 215*433d6423SLionel Sambuc assert(cr0 & I386_CR0_PE); 216*433d6423SLionel Sambuc 217*433d6423SLionel Sambuc /* First clear PG and PGE flag, as PGE must be enabled after PG. */ 218*433d6423SLionel Sambuc write_cr0(cr0 & ~I386_CR0_PG); 219*433d6423SLionel Sambuc write_cr4(cr4 & ~(I386_CR4_PGE | I386_CR4_PSE)); 220*433d6423SLionel Sambuc 221*433d6423SLionel Sambuc cr0= read_cr0(); 222*433d6423SLionel Sambuc cr4= read_cr4(); 223*433d6423SLionel Sambuc 224*433d6423SLionel Sambuc /* Our page table contains 4MB entries. */ 225*433d6423SLionel Sambuc cr4 |= I386_CR4_PSE; 226*433d6423SLionel Sambuc 227*433d6423SLionel Sambuc write_cr4(cr4); 228*433d6423SLionel Sambuc 229*433d6423SLionel Sambuc /* First enable paging, then enable global page flag. */ 230*433d6423SLionel Sambuc cr0 |= I386_CR0_PG; 231*433d6423SLionel Sambuc write_cr0(cr0); 232*433d6423SLionel Sambuc cr0 |= I386_CR0_WP; 233*433d6423SLionel Sambuc write_cr0(cr0); 234*433d6423SLionel Sambuc 235*433d6423SLionel Sambuc /* May we enable these features? */ 236*433d6423SLionel Sambuc if(pgeok) 237*433d6423SLionel Sambuc cr4 |= I386_CR4_PGE; 238*433d6423SLionel Sambuc 239*433d6423SLionel Sambuc write_cr4(cr4); 240*433d6423SLionel Sambuc } 241*433d6423SLionel Sambuc 242*433d6423SLionel Sambuc phys_bytes pg_load() 243*433d6423SLionel Sambuc { 244*433d6423SLionel Sambuc phys_bytes phpagedir = vir2phys(pagedir); 245*433d6423SLionel Sambuc write_cr3(phpagedir); 246*433d6423SLionel Sambuc return phpagedir; 247*433d6423SLionel Sambuc } 248*433d6423SLionel Sambuc 249*433d6423SLionel Sambuc void pg_clear(void) 250*433d6423SLionel Sambuc { 251*433d6423SLionel Sambuc memset(pagedir, 0, sizeof(pagedir)); 252*433d6423SLionel Sambuc } 253*433d6423SLionel Sambuc 254*433d6423SLionel Sambuc phys_bytes pg_rounddown(phys_bytes b) 255*433d6423SLionel Sambuc { 256*433d6423SLionel Sambuc phys_bytes o; 257*433d6423SLionel Sambuc if(!(o = b % I386_PAGE_SIZE)) 258*433d6423SLionel Sambuc return b; 259*433d6423SLionel Sambuc return b - o; 260*433d6423SLionel Sambuc } 261*433d6423SLionel Sambuc 262*433d6423SLionel Sambuc void pg_map(phys_bytes phys, vir_bytes vaddr, vir_bytes vaddr_end, 263*433d6423SLionel Sambuc kinfo_t *cbi) 264*433d6423SLionel Sambuc { 265*433d6423SLionel Sambuc static int mapped_pde = -1; 266*433d6423SLionel Sambuc static u32_t *pt = NULL; 267*433d6423SLionel Sambuc int pde, pte; 268*433d6423SLionel Sambuc 269*433d6423SLionel Sambuc assert(kernel_may_alloc); 270*433d6423SLionel Sambuc 271*433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 272*433d6423SLionel Sambuc assert(!(vaddr % I386_PAGE_SIZE)); 273*433d6423SLionel Sambuc } else { 274*433d6423SLionel Sambuc assert((vaddr % I386_PAGE_SIZE) == (phys % I386_PAGE_SIZE)); 275*433d6423SLionel Sambuc vaddr = pg_rounddown(vaddr); 276*433d6423SLionel Sambuc phys = pg_rounddown(phys); 277*433d6423SLionel Sambuc } 278*433d6423SLionel Sambuc assert(vaddr < kern_vir_start); 279*433d6423SLionel Sambuc 280*433d6423SLionel Sambuc while(vaddr < vaddr_end) { 281*433d6423SLionel Sambuc phys_bytes source = phys; 282*433d6423SLionel Sambuc assert(!(vaddr % I386_PAGE_SIZE)); 283*433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 284*433d6423SLionel Sambuc source = pg_alloc_page(cbi); 285*433d6423SLionel Sambuc } else { 286*433d6423SLionel Sambuc assert(!(phys % I386_PAGE_SIZE)); 287*433d6423SLionel Sambuc } 288*433d6423SLionel Sambuc assert(!(source % I386_PAGE_SIZE)); 289*433d6423SLionel Sambuc pde = I386_VM_PDE(vaddr); 290*433d6423SLionel Sambuc pte = I386_VM_PTE(vaddr); 291*433d6423SLionel Sambuc if(mapped_pde < pde) { 292*433d6423SLionel Sambuc phys_bytes ph; 293*433d6423SLionel Sambuc pt = alloc_pagetable(&ph); 294*433d6423SLionel Sambuc pagedir[pde] = (ph & I386_VM_ADDR_MASK) 295*433d6423SLionel Sambuc | I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE; 296*433d6423SLionel Sambuc mapped_pde = pde; 297*433d6423SLionel Sambuc } 298*433d6423SLionel Sambuc assert(pt); 299*433d6423SLionel Sambuc pt[pte] = (source & I386_VM_ADDR_MASK) | 300*433d6423SLionel Sambuc I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE; 301*433d6423SLionel Sambuc vaddr += I386_PAGE_SIZE; 302*433d6423SLionel Sambuc if(phys != PG_ALLOCATEME) 303*433d6423SLionel Sambuc phys += I386_PAGE_SIZE; 304*433d6423SLionel Sambuc } 305*433d6423SLionel Sambuc } 306*433d6423SLionel Sambuc 307*433d6423SLionel Sambuc void pg_info(reg_t *pagedir_ph, u32_t **pagedir_v) 308*433d6423SLionel Sambuc { 309*433d6423SLionel Sambuc *pagedir_ph = vir2phys(pagedir); 310*433d6423SLionel Sambuc *pagedir_v = pagedir; 311*433d6423SLionel Sambuc } 312*433d6423SLionel Sambuc 313