xref: /openbmc/u-boot/arch/x86/lib/physmem.c (revision ac31a7b81c5519a86ddce4fde4a99ba759332abc)
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