physmem.c 5.8 KB

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