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