1*433d6423SLionel Sambuc #include <minix/cpufeature.h> 2*433d6423SLionel Sambuc 3*433d6423SLionel Sambuc #include <minix/type.h> 4*433d6423SLionel Sambuc #include <assert.h> 5*433d6423SLionel Sambuc #include "kernel/kernel.h" 6*433d6423SLionel Sambuc #include "arch_proto.h" 7*433d6423SLionel Sambuc #include <machine/cpu.h> 8*433d6423SLionel Sambuc #include <arm/armreg.h> 9*433d6423SLionel Sambuc 10*433d6423SLionel Sambuc #include <string.h> 11*433d6423SLionel Sambuc #include <minix/type.h> 12*433d6423SLionel Sambuc 13*433d6423SLionel Sambuc /* These are set/computed in kernel.lds. */ 14*433d6423SLionel Sambuc extern char _kern_vir_base, _kern_phys_base, _kern_size; 15*433d6423SLionel Sambuc 16*433d6423SLionel Sambuc /* Retrieve the absolute values to something we can use. */ 17*433d6423SLionel Sambuc static phys_bytes kern_vir_start = (phys_bytes) &_kern_vir_base; 18*433d6423SLionel Sambuc static phys_bytes kern_phys_start = (phys_bytes) &_kern_phys_base; 19*433d6423SLionel Sambuc static phys_bytes kern_kernlen = (phys_bytes) &_kern_size; 20*433d6423SLionel Sambuc 21*433d6423SLionel Sambuc /* page directory we can use to map things */ 22*433d6423SLionel Sambuc static u32_t pagedir[4096] __aligned(16384); 23*433d6423SLionel Sambuc 24*433d6423SLionel Sambuc void print_memmap(kinfo_t *cbi) 25*433d6423SLionel Sambuc { 26*433d6423SLionel Sambuc int m; 27*433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 28*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 29*433d6423SLionel Sambuc phys_bytes addr = cbi->memmap[m].mm_base_addr, endit = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 30*433d6423SLionel Sambuc printf("%08lx-%08lx ",addr, endit); 31*433d6423SLionel Sambuc } 32*433d6423SLionel Sambuc printf("\nsize %08lx\n", cbi->mmap_size); 33*433d6423SLionel Sambuc } 34*433d6423SLionel Sambuc 35*433d6423SLionel Sambuc void cut_memmap(kinfo_t *cbi, phys_bytes start, phys_bytes end) 36*433d6423SLionel Sambuc { 37*433d6423SLionel Sambuc int m; 38*433d6423SLionel Sambuc phys_bytes o; 39*433d6423SLionel Sambuc 40*433d6423SLionel Sambuc if((o=start % ARM_PAGE_SIZE)) 41*433d6423SLionel Sambuc start -= o; 42*433d6423SLionel Sambuc if((o=end % ARM_PAGE_SIZE)) 43*433d6423SLionel Sambuc end += ARM_PAGE_SIZE - o; 44*433d6423SLionel Sambuc 45*433d6423SLionel Sambuc assert(kernel_may_alloc); 46*433d6423SLionel Sambuc 47*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 48*433d6423SLionel Sambuc phys_bytes substart = start, subend = end; 49*433d6423SLionel Sambuc phys_bytes memaddr = cbi->memmap[m].mm_base_addr, 50*433d6423SLionel Sambuc memend = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length; 51*433d6423SLionel Sambuc 52*433d6423SLionel Sambuc /* adjust cut range to be a subset of the free memory */ 53*433d6423SLionel Sambuc if(substart < memaddr) substart = memaddr; 54*433d6423SLionel Sambuc if(subend > memend) subend = memend; 55*433d6423SLionel Sambuc if(substart >= subend) continue; 56*433d6423SLionel Sambuc 57*433d6423SLionel Sambuc /* if there is any overlap, forget this one and add 58*433d6423SLionel Sambuc * 1-2 subranges back 59*433d6423SLionel Sambuc */ 60*433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = cbi->memmap[m].mm_length = 0; 61*433d6423SLionel Sambuc if(substart > memaddr) 62*433d6423SLionel Sambuc add_memmap(cbi, memaddr, substart-memaddr); 63*433d6423SLionel Sambuc if(subend < memend) 64*433d6423SLionel Sambuc add_memmap(cbi, subend, memend-subend); 65*433d6423SLionel Sambuc } 66*433d6423SLionel Sambuc } 67*433d6423SLionel Sambuc 68*433d6423SLionel Sambuc void add_memmap(kinfo_t *cbi, u64_t addr, u64_t len) 69*433d6423SLionel Sambuc { 70*433d6423SLionel Sambuc int m; 71*433d6423SLionel Sambuc #define LIMIT 0xFFFFF000 72*433d6423SLionel Sambuc /* Truncate available memory at 4GB as the rest of minix 73*433d6423SLionel Sambuc * currently can't deal with any bigger. 74*433d6423SLionel Sambuc */ 75*433d6423SLionel Sambuc if(addr > LIMIT) { 76*433d6423SLionel Sambuc return; 77*433d6423SLionel Sambuc } 78*433d6423SLionel Sambuc 79*433d6423SLionel Sambuc if(addr + len > LIMIT) { 80*433d6423SLionel Sambuc len -= (addr + len - LIMIT); 81*433d6423SLionel Sambuc } 82*433d6423SLionel Sambuc 83*433d6423SLionel Sambuc assert(cbi->mmap_size < MAXMEMMAP); 84*433d6423SLionel Sambuc if(len == 0) { 85*433d6423SLionel Sambuc return; 86*433d6423SLionel Sambuc } 87*433d6423SLionel Sambuc addr = roundup(addr, ARM_PAGE_SIZE); 88*433d6423SLionel Sambuc len = rounddown(len, ARM_PAGE_SIZE); 89*433d6423SLionel Sambuc 90*433d6423SLionel Sambuc assert(kernel_may_alloc); 91*433d6423SLionel Sambuc 92*433d6423SLionel Sambuc for(m = 0; m < MAXMEMMAP; m++) { 93*433d6423SLionel Sambuc phys_bytes highmark; 94*433d6423SLionel Sambuc if(cbi->memmap[m].mm_length) { 95*433d6423SLionel Sambuc continue; 96*433d6423SLionel Sambuc } 97*433d6423SLionel Sambuc cbi->memmap[m].mm_base_addr = addr; 98*433d6423SLionel Sambuc cbi->memmap[m].mm_length = len; 99*433d6423SLionel Sambuc cbi->memmap[m].type = MULTIBOOT_MEMORY_AVAILABLE; 100*433d6423SLionel Sambuc if(m >= cbi->mmap_size) { 101*433d6423SLionel Sambuc cbi->mmap_size = m+1; 102*433d6423SLionel Sambuc } 103*433d6423SLionel Sambuc highmark = addr + len; 104*433d6423SLionel Sambuc if(highmark > cbi->mem_high_phys) { 105*433d6423SLionel Sambuc cbi->mem_high_phys = highmark; 106*433d6423SLionel Sambuc } 107*433d6423SLionel Sambuc return; 108*433d6423SLionel Sambuc } 109*433d6423SLionel Sambuc 110*433d6423SLionel Sambuc panic("no available memmap slot"); 111*433d6423SLionel Sambuc } 112*433d6423SLionel Sambuc 113*433d6423SLionel Sambuc u32_t *alloc_pagetable(phys_bytes *ph) 114*433d6423SLionel Sambuc { 115*433d6423SLionel Sambuc u32_t *ret; 116*433d6423SLionel Sambuc #define PG_PAGETABLES 24 117*433d6423SLionel Sambuc static u32_t pagetables[PG_PAGETABLES][256] __aligned(1024); 118*433d6423SLionel Sambuc static int pt_inuse = 0; 119*433d6423SLionel Sambuc if(pt_inuse >= PG_PAGETABLES) { 120*433d6423SLionel Sambuc panic("no more pagetables"); 121*433d6423SLionel Sambuc } 122*433d6423SLionel Sambuc assert(sizeof(pagetables[pt_inuse]) == 1024); 123*433d6423SLionel Sambuc ret = pagetables[pt_inuse++]; 124*433d6423SLionel Sambuc *ph = vir2phys(ret); 125*433d6423SLionel Sambuc return ret; 126*433d6423SLionel Sambuc } 127*433d6423SLionel Sambuc 128*433d6423SLionel Sambuc #define PAGE_KB (ARM_PAGE_SIZE / 1024) 129*433d6423SLionel Sambuc 130*433d6423SLionel Sambuc phys_bytes pg_alloc_page(kinfo_t *cbi) 131*433d6423SLionel Sambuc { 132*433d6423SLionel Sambuc int m; 133*433d6423SLionel Sambuc multiboot_memory_map_t *mmap; 134*433d6423SLionel Sambuc 135*433d6423SLionel Sambuc assert(kernel_may_alloc); 136*433d6423SLionel Sambuc 137*433d6423SLionel Sambuc for(m = 0; m < cbi->mmap_size; m++) { 138*433d6423SLionel Sambuc mmap = &cbi->memmap[m]; 139*433d6423SLionel Sambuc if(!mmap->mm_length) { 140*433d6423SLionel Sambuc continue; 141*433d6423SLionel Sambuc } 142*433d6423SLionel Sambuc assert(mmap->mm_length > 0); 143*433d6423SLionel Sambuc assert(!(mmap->mm_length % ARM_PAGE_SIZE)); 144*433d6423SLionel Sambuc assert(!(mmap->mm_base_addr % ARM_PAGE_SIZE)); 145*433d6423SLionel Sambuc 146*433d6423SLionel Sambuc u32_t addr = mmap->mm_base_addr; 147*433d6423SLionel Sambuc mmap->mm_base_addr += ARM_PAGE_SIZE; 148*433d6423SLionel Sambuc mmap->mm_length -= ARM_PAGE_SIZE; 149*433d6423SLionel Sambuc 150*433d6423SLionel Sambuc cbi->kernel_allocated_bytes_dynamic += ARM_PAGE_SIZE; 151*433d6423SLionel Sambuc 152*433d6423SLionel Sambuc return addr; 153*433d6423SLionel Sambuc } 154*433d6423SLionel Sambuc 155*433d6423SLionel Sambuc panic("can't find free memory"); 156*433d6423SLionel Sambuc } 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambuc void pg_identity(kinfo_t *cbi) 159*433d6423SLionel Sambuc { 160*433d6423SLionel Sambuc int i; 161*433d6423SLionel Sambuc phys_bytes phys; 162*433d6423SLionel Sambuc 163*433d6423SLionel Sambuc /* We map memory that does not correspond to physical memory 164*433d6423SLionel Sambuc * as non-cacheable. Make sure we know what it is. 165*433d6423SLionel Sambuc */ 166*433d6423SLionel Sambuc assert(cbi->mem_high_phys); 167*433d6423SLionel Sambuc 168*433d6423SLionel Sambuc /* Set up an identity mapping page directory */ 169*433d6423SLionel Sambuc for(i = 0; i < ARM_VM_DIR_ENTRIES; i++) { 170*433d6423SLionel Sambuc u32_t flags = ARM_VM_SECTION 171*433d6423SLionel Sambuc | ARM_VM_SECTION_USER 172*433d6423SLionel Sambuc | ARM_VM_SECTION_DOMAIN; 173*433d6423SLionel Sambuc 174*433d6423SLionel Sambuc phys = i * ARM_SECTION_SIZE; 175*433d6423SLionel Sambuc /* mark mormal memory as cacheable. TODO: fix hard coded values */ 176*433d6423SLionel Sambuc if (phys >= PHYS_MEM_BEGIN && phys <= PHYS_MEM_END) { 177*433d6423SLionel Sambuc pagedir[i] = phys | flags | ARM_VM_SECTION_CACHED; 178*433d6423SLionel Sambuc } else { 179*433d6423SLionel Sambuc pagedir[i] = phys | flags | ARM_VM_SECTION_DEVICE; 180*433d6423SLionel Sambuc } 181*433d6423SLionel Sambuc } 182*433d6423SLionel Sambuc } 183*433d6423SLionel Sambuc 184*433d6423SLionel Sambuc int pg_mapkernel(void) 185*433d6423SLionel Sambuc { 186*433d6423SLionel Sambuc int pde; 187*433d6423SLionel Sambuc u32_t mapped = 0, kern_phys = kern_phys_start; 188*433d6423SLionel Sambuc 189*433d6423SLionel Sambuc assert(!(kern_vir_start % ARM_SECTION_SIZE)); 190*433d6423SLionel Sambuc assert(!(kern_phys_start % ARM_SECTION_SIZE)); 191*433d6423SLionel Sambuc pde = kern_vir_start / ARM_SECTION_SIZE; /* start pde */ 192*433d6423SLionel Sambuc while(mapped < kern_kernlen) { 193*433d6423SLionel Sambuc pagedir[pde] = (kern_phys & ARM_VM_SECTION_MASK) 194*433d6423SLionel Sambuc | ARM_VM_SECTION 195*433d6423SLionel Sambuc | ARM_VM_SECTION_SUPER 196*433d6423SLionel Sambuc | ARM_VM_SECTION_DOMAIN 197*433d6423SLionel Sambuc | ARM_VM_SECTION_CACHED; 198*433d6423SLionel Sambuc mapped += ARM_SECTION_SIZE; 199*433d6423SLionel Sambuc kern_phys += ARM_SECTION_SIZE; 200*433d6423SLionel Sambuc pde++; 201*433d6423SLionel Sambuc } 202*433d6423SLionel Sambuc return pde; /* free pde */ 203*433d6423SLionel Sambuc } 204*433d6423SLionel Sambuc 205*433d6423SLionel Sambuc void vm_enable_paging(void) 206*433d6423SLionel Sambuc { 207*433d6423SLionel Sambuc u32_t sctlr; 208*433d6423SLionel Sambuc u32_t actlr; 209*433d6423SLionel Sambuc 210*433d6423SLionel Sambuc write_ttbcr(0); 211*433d6423SLionel Sambuc 212*433d6423SLionel Sambuc /* Set all Domains to Client */ 213*433d6423SLionel Sambuc write_dacr(0x55555555); 214*433d6423SLionel Sambuc 215*433d6423SLionel Sambuc sctlr = read_sctlr(); 216*433d6423SLionel Sambuc 217*433d6423SLionel Sambuc /* Enable MMU */ 218*433d6423SLionel Sambuc sctlr |= CPU_CONTROL_MMU_ENABLE; 219*433d6423SLionel Sambuc 220*433d6423SLionel Sambuc /* TRE set to zero (default reset value): TEX[2:0] are used, plus C and B bits.*/ 221*433d6423SLionel Sambuc sctlr &= ~CPU_CONTROL_TR_ENABLE; 222*433d6423SLionel Sambuc 223*433d6423SLionel Sambuc /* AFE set to zero (default reset value): not using simplified model. */ 224*433d6423SLionel Sambuc sctlr &= ~CPU_CONTROL_AF_ENABLE; 225*433d6423SLionel Sambuc 226*433d6423SLionel Sambuc /* Enable instruction ,data cache and branch prediction */ 227*433d6423SLionel Sambuc sctlr |= CPU_CONTROL_DC_ENABLE; 228*433d6423SLionel Sambuc sctlr |= CPU_CONTROL_IC_ENABLE; 229*433d6423SLionel Sambuc sctlr |= CPU_CONTROL_BPRD_ENABLE; 230*433d6423SLionel Sambuc 231*433d6423SLionel Sambuc /* Enable barriers */ 232*433d6423SLionel Sambuc sctlr |= CPU_CONTROL_32BD_ENABLE; 233*433d6423SLionel Sambuc 234*433d6423SLionel Sambuc /* Enable L2 cache (cortex-a8) */ 235*433d6423SLionel Sambuc #define CORTEX_A8_L2EN (0x02) 236*433d6423SLionel Sambuc actlr = read_actlr(); 237*433d6423SLionel Sambuc actlr |= CORTEX_A8_L2EN; 238*433d6423SLionel Sambuc write_actlr(actlr); 239*433d6423SLionel Sambuc 240*433d6423SLionel Sambuc write_sctlr(sctlr); 241*433d6423SLionel Sambuc } 242*433d6423SLionel Sambuc 243*433d6423SLionel Sambuc phys_bytes pg_load() 244*433d6423SLionel Sambuc { 245*433d6423SLionel Sambuc phys_bytes phpagedir = vir2phys(pagedir); 246*433d6423SLionel Sambuc write_ttbr0(phpagedir); 247*433d6423SLionel Sambuc return phpagedir; 248*433d6423SLionel Sambuc } 249*433d6423SLionel Sambuc 250*433d6423SLionel Sambuc void pg_clear(void) 251*433d6423SLionel Sambuc { 252*433d6423SLionel Sambuc memset(pagedir, 0, sizeof(pagedir)); 253*433d6423SLionel Sambuc } 254*433d6423SLionel Sambuc 255*433d6423SLionel Sambuc phys_bytes pg_rounddown(phys_bytes b) 256*433d6423SLionel Sambuc { 257*433d6423SLionel Sambuc phys_bytes o; 258*433d6423SLionel Sambuc if(!(o = b % ARM_PAGE_SIZE)) { 259*433d6423SLionel Sambuc return b; 260*433d6423SLionel Sambuc } 261*433d6423SLionel Sambuc return b - o; 262*433d6423SLionel Sambuc } 263*433d6423SLionel Sambuc 264*433d6423SLionel Sambuc void pg_map(phys_bytes phys, vir_bytes vaddr, vir_bytes vaddr_end, 265*433d6423SLionel Sambuc kinfo_t *cbi) 266*433d6423SLionel Sambuc { 267*433d6423SLionel Sambuc static int mapped_pde = -1; 268*433d6423SLionel Sambuc static u32_t *pt = NULL; 269*433d6423SLionel Sambuc int pde, pte; 270*433d6423SLionel Sambuc 271*433d6423SLionel Sambuc assert(kernel_may_alloc); 272*433d6423SLionel Sambuc 273*433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 274*433d6423SLionel Sambuc assert(!(vaddr % ARM_PAGE_SIZE)); 275*433d6423SLionel Sambuc } else { 276*433d6423SLionel Sambuc assert((vaddr % ARM_PAGE_SIZE) == (phys % ARM_PAGE_SIZE)); 277*433d6423SLionel Sambuc vaddr = pg_rounddown(vaddr); 278*433d6423SLionel Sambuc phys = pg_rounddown(phys); 279*433d6423SLionel Sambuc } 280*433d6423SLionel Sambuc assert(vaddr < kern_vir_start); 281*433d6423SLionel Sambuc 282*433d6423SLionel Sambuc while(vaddr < vaddr_end) { 283*433d6423SLionel Sambuc phys_bytes source = phys; 284*433d6423SLionel Sambuc assert(!(vaddr % ARM_PAGE_SIZE)); 285*433d6423SLionel Sambuc if(phys == PG_ALLOCATEME) { 286*433d6423SLionel Sambuc source = pg_alloc_page(cbi); 287*433d6423SLionel Sambuc } else { 288*433d6423SLionel Sambuc assert(!(phys % ARM_PAGE_SIZE)); 289*433d6423SLionel Sambuc } 290*433d6423SLionel Sambuc assert(!(source % ARM_PAGE_SIZE)); 291*433d6423SLionel Sambuc pde = ARM_VM_PDE(vaddr); 292*433d6423SLionel Sambuc pte = ARM_VM_PTE(vaddr); 293*433d6423SLionel Sambuc if(mapped_pde < pde) { 294*433d6423SLionel Sambuc phys_bytes ph; 295*433d6423SLionel Sambuc pt = alloc_pagetable(&ph); 296*433d6423SLionel Sambuc pagedir[pde] = (ph & ARM_VM_PDE_MASK) 297*433d6423SLionel Sambuc | ARM_VM_PAGEDIR 298*433d6423SLionel Sambuc | ARM_VM_PDE_DOMAIN; 299*433d6423SLionel Sambuc mapped_pde = pde; 300*433d6423SLionel Sambuc } 301*433d6423SLionel Sambuc assert(pt); 302*433d6423SLionel Sambuc pt[pte] = (source & ARM_VM_PTE_MASK) 303*433d6423SLionel Sambuc | ARM_VM_PAGETABLE 304*433d6423SLionel Sambuc | ARM_VM_PTE_CACHED 305*433d6423SLionel Sambuc | ARM_VM_PTE_USER; 306*433d6423SLionel Sambuc vaddr += ARM_PAGE_SIZE; 307*433d6423SLionel Sambuc if(phys != PG_ALLOCATEME) { 308*433d6423SLionel Sambuc phys += ARM_PAGE_SIZE; 309*433d6423SLionel Sambuc } 310*433d6423SLionel Sambuc } 311*433d6423SLionel Sambuc } 312*433d6423SLionel Sambuc 313*433d6423SLionel Sambuc void pg_info(reg_t *pagedir_ph, u32_t **pagedir_v) 314*433d6423SLionel Sambuc { 315*433d6423SLionel Sambuc *pagedir_ph = vir2phys(pagedir); 316*433d6423SLionel Sambuc *pagedir_v = pagedir; 317*433d6423SLionel Sambuc } 318