1*ac31a7b8SGabe Black /* 2*ac31a7b8SGabe Black * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 3*ac31a7b8SGabe Black * Use of this source code is governed by a BSD-style license that can be 4*ac31a7b8SGabe Black * found in the LICENSE file. 5*ac31a7b8SGabe Black * 6*ac31a7b8SGabe Black * Alternatively, this software may be distributed under the terms of the 7*ac31a7b8SGabe Black * GNU General Public License ("GPL") version 2 as published by the Free 8*ac31a7b8SGabe Black * Software Foundation. 9*ac31a7b8SGabe Black */ 10*ac31a7b8SGabe Black 11*ac31a7b8SGabe Black #include <common.h> 12*ac31a7b8SGabe Black #include <physmem.h> 13*ac31a7b8SGabe Black #include <linux/compiler.h> 14*ac31a7b8SGabe Black 15*ac31a7b8SGabe Black /* Large pages are 2MB. */ 16*ac31a7b8SGabe Black #define LARGE_PAGE_SIZE ((1 << 20) * 2) 17*ac31a7b8SGabe Black 18*ac31a7b8SGabe Black /* 19*ac31a7b8SGabe Black * Paging data structures. 20*ac31a7b8SGabe Black */ 21*ac31a7b8SGabe Black 22*ac31a7b8SGabe Black struct pdpe { 23*ac31a7b8SGabe Black uint64_t p:1; 24*ac31a7b8SGabe Black uint64_t mbz_0:2; 25*ac31a7b8SGabe Black uint64_t pwt:1; 26*ac31a7b8SGabe Black uint64_t pcd:1; 27*ac31a7b8SGabe Black uint64_t mbz_1:4; 28*ac31a7b8SGabe Black uint64_t avl:3; 29*ac31a7b8SGabe Black uint64_t base:40; 30*ac31a7b8SGabe Black uint64_t mbz_2:12; 31*ac31a7b8SGabe Black }; 32*ac31a7b8SGabe Black 33*ac31a7b8SGabe Black typedef struct pdpe pdpt_t[512]; 34*ac31a7b8SGabe Black 35*ac31a7b8SGabe Black struct pde { 36*ac31a7b8SGabe Black uint64_t p:1; /* present */ 37*ac31a7b8SGabe Black uint64_t rw:1; /* read/write */ 38*ac31a7b8SGabe Black uint64_t us:1; /* user/supervisor */ 39*ac31a7b8SGabe Black uint64_t pwt:1; /* page-level writethrough */ 40*ac31a7b8SGabe Black uint64_t pcd:1; /* page-level cache disable */ 41*ac31a7b8SGabe Black uint64_t a:1; /* accessed */ 42*ac31a7b8SGabe Black uint64_t d:1; /* dirty */ 43*ac31a7b8SGabe Black uint64_t ps:1; /* page size */ 44*ac31a7b8SGabe Black uint64_t g:1; /* global page */ 45*ac31a7b8SGabe Black uint64_t avl:3; /* available to software */ 46*ac31a7b8SGabe Black uint64_t pat:1; /* page-attribute table */ 47*ac31a7b8SGabe Black uint64_t mbz_0:8; /* must be zero */ 48*ac31a7b8SGabe Black uint64_t base:31; /* base address */ 49*ac31a7b8SGabe Black }; 50*ac31a7b8SGabe Black 51*ac31a7b8SGabe Black typedef struct pde pdt_t[512]; 52*ac31a7b8SGabe Black 53*ac31a7b8SGabe Black static pdpt_t pdpt __aligned(4096); 54*ac31a7b8SGabe Black static pdt_t pdts[4] __aligned(4096); 55*ac31a7b8SGabe Black 56*ac31a7b8SGabe Black /* 57*ac31a7b8SGabe Black * Map a virtual address to a physical address and optionally invalidate any 58*ac31a7b8SGabe Black * old mapping. 59*ac31a7b8SGabe Black * 60*ac31a7b8SGabe Black * @param virt The virtual address to use. 61*ac31a7b8SGabe Black * @param phys The physical address to use. 62*ac31a7b8SGabe Black * @param invlpg Whether to use invlpg to clear any old mappings. 63*ac31a7b8SGabe Black */ 64*ac31a7b8SGabe Black static void x86_phys_map_page(uintptr_t virt, phys_addr_t phys, int invlpg) 65*ac31a7b8SGabe Black { 66*ac31a7b8SGabe Black /* Extract the two bit PDPT index and the 9 bit PDT index. */ 67*ac31a7b8SGabe Black uintptr_t pdpt_idx = (virt >> 30) & 0x3; 68*ac31a7b8SGabe Black uintptr_t pdt_idx = (virt >> 21) & 0x1ff; 69*ac31a7b8SGabe Black 70*ac31a7b8SGabe Black /* Set up a handy pointer to the appropriate PDE. */ 71*ac31a7b8SGabe Black struct pde *pde = &(pdts[pdpt_idx][pdt_idx]); 72*ac31a7b8SGabe Black 73*ac31a7b8SGabe Black memset(pde, 0, sizeof(struct pde)); 74*ac31a7b8SGabe Black pde->p = 1; 75*ac31a7b8SGabe Black pde->rw = 1; 76*ac31a7b8SGabe Black pde->us = 1; 77*ac31a7b8SGabe Black pde->ps = 1; 78*ac31a7b8SGabe Black pde->base = phys >> 21; 79*ac31a7b8SGabe Black 80*ac31a7b8SGabe Black if (invlpg) { 81*ac31a7b8SGabe Black /* Flush any stale mapping out of the TLBs. */ 82*ac31a7b8SGabe Black __asm__ __volatile__( 83*ac31a7b8SGabe Black "invlpg %0\n\t" 84*ac31a7b8SGabe Black : 85*ac31a7b8SGabe Black : "m" (*(uint8_t *)virt) 86*ac31a7b8SGabe Black ); 87*ac31a7b8SGabe Black } 88*ac31a7b8SGabe Black } 89*ac31a7b8SGabe Black 90*ac31a7b8SGabe Black /* Identity map the lower 4GB and turn on paging with PAE. */ 91*ac31a7b8SGabe Black static void x86_phys_enter_paging(void) 92*ac31a7b8SGabe Black { 93*ac31a7b8SGabe Black phys_addr_t page_addr; 94*ac31a7b8SGabe Black unsigned i; 95*ac31a7b8SGabe Black 96*ac31a7b8SGabe Black /* Zero out the page tables. */ 97*ac31a7b8SGabe Black memset(pdpt, 0, sizeof(pdpt)); 98*ac31a7b8SGabe Black memset(pdts, 0, sizeof(pdts)); 99*ac31a7b8SGabe Black 100*ac31a7b8SGabe Black /* Set up the PDPT. */ 101*ac31a7b8SGabe Black for (i = 0; i < ARRAY_SIZE(pdts); i++) { 102*ac31a7b8SGabe Black pdpt[i].p = 1; 103*ac31a7b8SGabe Black pdpt[i].base = ((uintptr_t)&pdts[i]) >> 12; 104*ac31a7b8SGabe Black } 105*ac31a7b8SGabe Black 106*ac31a7b8SGabe Black /* Identity map everything up to 4GB. */ 107*ac31a7b8SGabe Black for (page_addr = 0; page_addr < (1ULL << 32); 108*ac31a7b8SGabe Black page_addr += LARGE_PAGE_SIZE) { 109*ac31a7b8SGabe Black /* There's no reason to invalidate the TLB with paging off. */ 110*ac31a7b8SGabe Black x86_phys_map_page(page_addr, page_addr, 0); 111*ac31a7b8SGabe Black } 112*ac31a7b8SGabe Black 113*ac31a7b8SGabe Black /* Turn on paging */ 114*ac31a7b8SGabe Black __asm__ __volatile__( 115*ac31a7b8SGabe Black /* Load the page table address */ 116*ac31a7b8SGabe Black "movl %0, %%cr3\n\t" 117*ac31a7b8SGabe Black /* Enable pae */ 118*ac31a7b8SGabe Black "movl %%cr4, %%eax\n\t" 119*ac31a7b8SGabe Black "orl $0x00000020, %%eax\n\t" 120*ac31a7b8SGabe Black "movl %%eax, %%cr4\n\t" 121*ac31a7b8SGabe Black /* Enable paging */ 122*ac31a7b8SGabe Black "movl %%cr0, %%eax\n\t" 123*ac31a7b8SGabe Black "orl $0x80000000, %%eax\n\t" 124*ac31a7b8SGabe Black "movl %%eax, %%cr0\n\t" 125*ac31a7b8SGabe Black : 126*ac31a7b8SGabe Black : "r" (pdpt) 127*ac31a7b8SGabe Black : "eax" 128*ac31a7b8SGabe Black ); 129*ac31a7b8SGabe Black } 130*ac31a7b8SGabe Black 131*ac31a7b8SGabe Black /* Disable paging and PAE mode. */ 132*ac31a7b8SGabe Black static void x86_phys_exit_paging(void) 133*ac31a7b8SGabe Black { 134*ac31a7b8SGabe Black /* Turn off paging */ 135*ac31a7b8SGabe Black __asm__ __volatile__ ( 136*ac31a7b8SGabe Black /* Disable paging */ 137*ac31a7b8SGabe Black "movl %%cr0, %%eax\n\t" 138*ac31a7b8SGabe Black "andl $0x7fffffff, %%eax\n\t" 139*ac31a7b8SGabe Black "movl %%eax, %%cr0\n\t" 140*ac31a7b8SGabe Black /* Disable pae */ 141*ac31a7b8SGabe Black "movl %%cr4, %%eax\n\t" 142*ac31a7b8SGabe Black "andl $0xffffffdf, %%eax\n\t" 143*ac31a7b8SGabe Black "movl %%eax, %%cr4\n\t" 144*ac31a7b8SGabe Black : 145*ac31a7b8SGabe Black : 146*ac31a7b8SGabe Black : "eax" 147*ac31a7b8SGabe Black ); 148*ac31a7b8SGabe Black } 149*ac31a7b8SGabe Black 150*ac31a7b8SGabe Black /* 151*ac31a7b8SGabe Black * Set physical memory to a particular value when the whole region fits on one 152*ac31a7b8SGabe Black * page. 153*ac31a7b8SGabe Black * 154*ac31a7b8SGabe Black * @param map_addr The address that starts the physical page. 155*ac31a7b8SGabe Black * @param offset How far into that page to start setting a value. 156*ac31a7b8SGabe Black * @param c The value to set memory to. 157*ac31a7b8SGabe Black * @param size The size in bytes of the area to set. 158*ac31a7b8SGabe Black */ 159*ac31a7b8SGabe Black static void x86_phys_memset_page(phys_addr_t map_addr, uintptr_t offset, int c, 160*ac31a7b8SGabe Black unsigned size) 161*ac31a7b8SGabe Black { 162*ac31a7b8SGabe Black /* 163*ac31a7b8SGabe Black * U-Boot should be far away from the beginning of memory, so that's a 164*ac31a7b8SGabe Black * good place to map our window on top of. 165*ac31a7b8SGabe Black */ 166*ac31a7b8SGabe Black const uintptr_t window = LARGE_PAGE_SIZE; 167*ac31a7b8SGabe Black 168*ac31a7b8SGabe Black /* Make sure the window is below U-Boot. */ 169*ac31a7b8SGabe Black assert(window + LARGE_PAGE_SIZE < 170*ac31a7b8SGabe Black gd->relocaddr - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_STACK_SIZE); 171*ac31a7b8SGabe Black /* Map the page into the window and then memset the appropriate part. */ 172*ac31a7b8SGabe Black x86_phys_map_page(window, map_addr, 1); 173*ac31a7b8SGabe Black memset((void *)(window + offset), c, size); 174*ac31a7b8SGabe Black } 175*ac31a7b8SGabe Black 176*ac31a7b8SGabe Black /* 177*ac31a7b8SGabe Black * A physical memory anologue to memset with matching parameters and return 178*ac31a7b8SGabe Black * value. 179*ac31a7b8SGabe Black */ 180*ac31a7b8SGabe Black phys_addr_t arch_phys_memset(phys_addr_t start, int c, phys_size_t size) 181*ac31a7b8SGabe Black { 182*ac31a7b8SGabe Black const phys_addr_t max_addr = (phys_addr_t)~(uintptr_t)0; 183*ac31a7b8SGabe Black const phys_addr_t orig_start = start; 184*ac31a7b8SGabe Black 185*ac31a7b8SGabe Black if (!size) 186*ac31a7b8SGabe Black return orig_start; 187*ac31a7b8SGabe Black 188*ac31a7b8SGabe Black /* Handle memory below 4GB. */ 189*ac31a7b8SGabe Black if (start <= max_addr) { 190*ac31a7b8SGabe Black phys_size_t low_size = MIN(max_addr + 1 - start, size); 191*ac31a7b8SGabe Black void *start_ptr = (void *)(uintptr_t)start; 192*ac31a7b8SGabe Black 193*ac31a7b8SGabe Black assert(((phys_addr_t)(uintptr_t)start) == start); 194*ac31a7b8SGabe Black memset(start_ptr, c, low_size); 195*ac31a7b8SGabe Black start += low_size; 196*ac31a7b8SGabe Black size -= low_size; 197*ac31a7b8SGabe Black } 198*ac31a7b8SGabe Black 199*ac31a7b8SGabe Black /* Use paging and PAE to handle memory above 4GB up to 64GB. */ 200*ac31a7b8SGabe Black if (size) { 201*ac31a7b8SGabe Black phys_addr_t map_addr = start & ~(LARGE_PAGE_SIZE - 1); 202*ac31a7b8SGabe Black phys_addr_t offset = start - map_addr; 203*ac31a7b8SGabe Black 204*ac31a7b8SGabe Black x86_phys_enter_paging(); 205*ac31a7b8SGabe Black 206*ac31a7b8SGabe Black /* Handle the first partial page. */ 207*ac31a7b8SGabe Black if (offset) { 208*ac31a7b8SGabe Black phys_addr_t end = 209*ac31a7b8SGabe Black MIN(map_addr + LARGE_PAGE_SIZE, start + size); 210*ac31a7b8SGabe Black phys_size_t cur_size = end - start; 211*ac31a7b8SGabe Black x86_phys_memset_page(map_addr, offset, c, cur_size); 212*ac31a7b8SGabe Black size -= cur_size; 213*ac31a7b8SGabe Black map_addr += LARGE_PAGE_SIZE; 214*ac31a7b8SGabe Black } 215*ac31a7b8SGabe Black /* Handle the complete pages. */ 216*ac31a7b8SGabe Black while (size > LARGE_PAGE_SIZE) { 217*ac31a7b8SGabe Black x86_phys_memset_page(map_addr, 0, c, LARGE_PAGE_SIZE); 218*ac31a7b8SGabe Black size -= LARGE_PAGE_SIZE; 219*ac31a7b8SGabe Black map_addr += LARGE_PAGE_SIZE; 220*ac31a7b8SGabe Black } 221*ac31a7b8SGabe Black /* Handle the last partial page. */ 222*ac31a7b8SGabe Black if (size) 223*ac31a7b8SGabe Black x86_phys_memset_page(map_addr, 0, c, size); 224*ac31a7b8SGabe Black 225*ac31a7b8SGabe Black x86_phys_exit_paging(); 226*ac31a7b8SGabe Black } 227*ac31a7b8SGabe Black return orig_start; 228*ac31a7b8SGabe Black } 229