cpu/x86/pae/pgtbl.c: extract reusable code from memset_pae()
Code dealing with PAE can be used outside of memset_pae(). This change extracts creation of identity mapped pagetables to init_pae_pagetables() and mapping of single 2 MiB map to pae_map_2M_page(). Both functions are exported in include/cpu/x86/pae.h to allow use outside of pgtbl.c. MEMSET_PAE_* macros were renamed to PAE_* since they no longer apply only to memset_pae(). Change-Id: I8aa80eb246ff0e77e1f51d71933d3d00ab75aaeb Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/82249 Reviewed-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
		
				
					committed by
					
						 Felix Held
						Felix Held
					
				
			
			
				
	
			
			
			
						parent
						
							b1bd442ca9
						
					
				
				
					commit
					fda9d75d90
				
			| @@ -100,6 +100,97 @@ void paging_disable_pae(void) | |||||||
| 	write_cr4(cr4); | 	write_cr4(cr4); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Prepare PAE pagetables that identity map the whole 32-bit address space using | ||||||
|  |  * 2 MiB pages. The PAT are set to all cacheable, but MTRRs still apply. CR3 is | ||||||
|  |  * loaded and PAE is enabled by this function. | ||||||
|  |  * | ||||||
|  |  * Requires a scratch memory for pagetables. | ||||||
|  |  * | ||||||
|  |  * @param pgtbl     Where pagetables reside, must be 4 KiB aligned and 20 KiB in | ||||||
|  |  *                  size. | ||||||
|  |  *                  Content at physical address isn't preserved. | ||||||
|  |  * @return 0 on success, 1 on error | ||||||
|  |  */ | ||||||
|  | int init_pae_pagetables(void *pgtbl) | ||||||
|  | { | ||||||
|  | 	struct pg_table *pgtbl_buf = (struct pg_table *)pgtbl; | ||||||
|  | 	struct pde *pd = pgtbl_buf->pd, *pdp = pgtbl_buf->pdp; | ||||||
|  |  | ||||||
|  | 	printk(BIOS_DEBUG, "%s: Using address %p for page tables\n", | ||||||
|  | 	       __func__, pgtbl_buf); | ||||||
|  |  | ||||||
|  | 	/* Cover some basic error conditions */ | ||||||
|  | 	if (!IS_ALIGNED((uintptr_t)pgtbl_buf, s4KiB)) { | ||||||
|  | 		printk(BIOS_ERR, "%s: Invalid alignment\n", __func__); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	paging_disable_pae(); | ||||||
|  |  | ||||||
|  | 	/* Point the page directory pointers at the page directories. */ | ||||||
|  | 	memset(pgtbl_buf->pdp, 0, sizeof(pgtbl_buf->pdp)); | ||||||
|  |  | ||||||
|  | 	pdp[0].addr_lo = ((uintptr_t)&pd[512*0]) | PDPTE_PRES; | ||||||
|  | 	pdp[1].addr_lo = ((uintptr_t)&pd[512*1]) | PDPTE_PRES; | ||||||
|  | 	pdp[2].addr_lo = ((uintptr_t)&pd[512*2]) | PDPTE_PRES; | ||||||
|  | 	pdp[3].addr_lo = ((uintptr_t)&pd[512*3]) | PDPTE_PRES; | ||||||
|  |  | ||||||
|  | 	/* Identity map the whole 32-bit address space */ | ||||||
|  | 	for (size_t i = 0; i < 2048; i++) { | ||||||
|  | 		pd[i].addr_lo = (i << PDE_IDX_SHIFT) | PDE_PS | PDE_PRES | PDE_RW; | ||||||
|  | 		pd[i].addr_hi = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	paging_enable_pae_cr3((uintptr_t)pdp); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Map single 2 MiB page in pagetables created by init_pae_pagetables(). | ||||||
|  |  * | ||||||
|  |  * The function does not check if the page was already non identity mapped, | ||||||
|  |  * this allows callers to reuse one page without having to explicitly unmap it | ||||||
|  |  * between calls. | ||||||
|  |  * | ||||||
|  |  * @param pgtbl     Where pagetables created by init_pae_pagetables() reside. | ||||||
|  |  *                  Content at physical address is preserved except for single | ||||||
|  |  *                  entry corresponding to vmem_addr. | ||||||
|  |  * @param paddr     Physical memory address to map. Function prints a warning if | ||||||
|  |  *                  it isn't aligned to 2 MiB. | ||||||
|  |  * @param vmem_addr Where the virtual non identity mapped page resides, must | ||||||
|  |  *                  be at least 2 MiB in size. Function prints a warning if it | ||||||
|  |  *                  isn't aligned to 2 MiB. | ||||||
|  |  *                  Content at physical address is preserved. | ||||||
|  |  * @return 0 on success, 1 on error | ||||||
|  |  */ | ||||||
|  | void pae_map_2M_page(void *pgtbl, uint64_t paddr, void *vmem_addr) | ||||||
|  | { | ||||||
|  | 	struct pg_table *pgtbl_buf = (struct pg_table *)pgtbl; | ||||||
|  | 	struct pde *pd; | ||||||
|  |  | ||||||
|  | 	if (!IS_ALIGNED(paddr, s2MiB)) { | ||||||
|  | 		printk(BIOS_WARNING, "%s: Aligning physical address to 2MiB\n", | ||||||
|  | 		       __func__); | ||||||
|  | 		paddr = ALIGN_DOWN(paddr, s2MiB); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!IS_ALIGNED((uintptr_t)vmem_addr, s2MiB)) { | ||||||
|  | 		printk(BIOS_WARNING, "%s: Aligning virtual address to 2MiB\n", | ||||||
|  | 		       __func__); | ||||||
|  | 		vmem_addr = (void *)ALIGN_DOWN((uintptr_t)vmem_addr, s2MiB); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Map a page using PAE at virtual address vmem_addr. */ | ||||||
|  | 	pd = &pgtbl_buf->pd[((uintptr_t)vmem_addr) >> PDE_IDX_SHIFT]; | ||||||
|  | 	pd->addr_lo = paddr | PDE_PS | PDE_PRES | PDE_RW; | ||||||
|  | 	pd->addr_hi = paddr >> 32; | ||||||
|  |  | ||||||
|  | 	/* Update page tables */ | ||||||
|  | 	asm volatile ("invlpg (%0)" :: "b"(vmem_addr) : "memory"); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Use PAE to map a page and then memset it with the pattern specified. |  * Use PAE to map a page and then memset it with the pattern specified. | ||||||
|  * In order to use PAE pagetables for virtual addressing are set up and reloaded |  * In order to use PAE pagetables for virtual addressing are set up and reloaded | ||||||
| @@ -130,22 +221,18 @@ void paging_disable_pae(void) | |||||||
| int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | ||||||
| 	       void *vmem_addr) | 	       void *vmem_addr) | ||||||
| { | { | ||||||
| 	struct pg_table *pgtbl_buf = (struct pg_table *)pgtbl; |  | ||||||
| 	ssize_t offset; | 	ssize_t offset; | ||||||
|  | 	const uintptr_t pgtbl_s = (uintptr_t)pgtbl; | ||||||
|  | 	const uintptr_t pgtbl_e = pgtbl_s + sizeof(struct pg_table); | ||||||
|  |  | ||||||
| 	printk(BIOS_DEBUG, "%s: Using virtual address %p as scratchpad\n", | 	printk(BIOS_DEBUG, "%s: Using virtual address %p as scratchpad\n", | ||||||
| 	       __func__, vmem_addr); | 	       __func__, vmem_addr); | ||||||
| 	printk(BIOS_DEBUG, "%s: Using address %p for page tables\n", |  | ||||||
| 	       __func__, pgtbl_buf); |  | ||||||
|  |  | ||||||
| 	/* Cover some basic error conditions */ | 	/* Cover some basic error conditions */ | ||||||
| 	if (!IS_ALIGNED((uintptr_t)pgtbl_buf, s4KiB) || | 	if (!IS_ALIGNED((uintptr_t)vmem_addr, s2MiB)) { | ||||||
| 	    !IS_ALIGNED((uintptr_t)vmem_addr, s2MiB)) { |  | ||||||
| 		printk(BIOS_ERR, "%s: Invalid alignment\n", __func__); | 		printk(BIOS_ERR, "%s: Invalid alignment\n", __func__); | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
| 	const uintptr_t pgtbl_s = (uintptr_t)pgtbl_buf; |  | ||||||
| 	const uintptr_t pgtbl_e = pgtbl_s + sizeof(struct pg_table); |  | ||||||
|  |  | ||||||
| 	if (OVERLAP(dest, dest + length, pgtbl_s, pgtbl_e)) { | 	if (OVERLAP(dest, dest + length, pgtbl_s, pgtbl_e)) { | ||||||
| 		printk(BIOS_ERR, "%s: destination overlaps page tables\n", | 		printk(BIOS_ERR, "%s: destination overlaps page tables\n", | ||||||
| @@ -160,31 +247,12 @@ int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | |||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	paging_disable_pae(); | 	if (init_pae_pagetables(pgtbl)) | ||||||
|  | 		return 1; | ||||||
| 	struct pde *pd = pgtbl_buf->pd, *pdp = pgtbl_buf->pdp; |  | ||||||
| 	/* Point the page directory pointers at the page directories. */ |  | ||||||
| 	memset(pgtbl_buf->pdp, 0, sizeof(pgtbl_buf->pdp)); |  | ||||||
|  |  | ||||||
| 	pdp[0].addr_lo = ((uintptr_t)&pd[512*0]) | PDPTE_PRES; |  | ||||||
| 	pdp[1].addr_lo = ((uintptr_t)&pd[512*1]) | PDPTE_PRES; |  | ||||||
| 	pdp[2].addr_lo = ((uintptr_t)&pd[512*2]) | PDPTE_PRES; |  | ||||||
| 	pdp[3].addr_lo = ((uintptr_t)&pd[512*3]) | PDPTE_PRES; |  | ||||||
|  |  | ||||||
| 	offset = dest - ALIGN_DOWN(dest, s2MiB); | 	offset = dest - ALIGN_DOWN(dest, s2MiB); | ||||||
| 	dest = ALIGN_DOWN(dest, s2MiB); | 	dest = ALIGN_DOWN(dest, s2MiB); | ||||||
|  |  | ||||||
| 	/* Identity map the whole 32-bit address space */ |  | ||||||
| 	for (size_t i = 0; i < 2048; i++) { |  | ||||||
| 		pd[i].addr_lo = (i << PDE_IDX_SHIFT) | PDE_PS | PDE_PRES | PDE_RW; |  | ||||||
| 		pd[i].addr_hi = 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Get pointer to PD that's not identity mapped */ |  | ||||||
| 	pd = &pgtbl_buf->pd[((uintptr_t)vmem_addr) >> PDE_IDX_SHIFT]; |  | ||||||
|  |  | ||||||
| 	paging_enable_pae_cr3((uintptr_t)pdp); |  | ||||||
|  |  | ||||||
| 	do { | 	do { | ||||||
| 		const size_t len = MIN(length, s2MiB - offset); | 		const size_t len = MIN(length, s2MiB - offset); | ||||||
|  |  | ||||||
| @@ -192,11 +260,7 @@ int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | |||||||
| 		 * Map a page using PAE at virtual address vmem_addr. | 		 * Map a page using PAE at virtual address vmem_addr. | ||||||
| 		 * dest is already 2 MiB aligned. | 		 * dest is already 2 MiB aligned. | ||||||
| 		 */ | 		 */ | ||||||
| 		pd->addr_lo = dest | PDE_PS | PDE_PRES | PDE_RW; | 		pae_map_2M_page(pgtbl, dest, vmem_addr); | ||||||
| 		pd->addr_hi = dest >> 32; |  | ||||||
|  |  | ||||||
| 		/* Update page tables */ |  | ||||||
| 		asm volatile ("invlpg (%0)" :: "b"(vmem_addr) : "memory"); |  | ||||||
|  |  | ||||||
| 		printk(BIOS_SPEW, "%s: Clearing %llx[%lx] - %zx\n", __func__, | 		printk(BIOS_SPEW, "%s: Clearing %llx[%lx] - %zx\n", __func__, | ||||||
| 		       dest + offset, (uintptr_t)vmem_addr + offset, len); | 		       dest + offset, (uintptr_t)vmem_addr + offset, len); | ||||||
|   | |||||||
| @@ -35,11 +35,15 @@ void paging_set_default_pat(void); | |||||||
|  * failure. */ |  * failure. */ | ||||||
| int paging_enable_for_car(const char *pdpt_name, const char *pt_name); | int paging_enable_for_car(const char *pdpt_name, const char *pt_name); | ||||||
|  |  | ||||||
| /* To be used with memset_pae */ | /* To be used with memset_pae and pae_map_2M_page */ | ||||||
| #define MEMSET_PAE_VMEM_ALIGN (2 * MiB) | #define PAE_VMEM_ALIGN (2 * MiB) | ||||||
| #define MEMSET_PAE_VMEM_SIZE (2 * MiB) | #define PAE_VMEM_SIZE (2 * MiB) | ||||||
| #define MEMSET_PAE_PGTL_ALIGN (4 * KiB) | #define PAE_PGTL_ALIGN (4 * KiB) | ||||||
| #define MEMSET_PAE_PGTL_SIZE (20 * KiB) | #define PAE_PGTL_SIZE (20 * KiB) | ||||||
|  |  | ||||||
|  | int init_pae_pagetables(void *pgtbl); | ||||||
|  |  | ||||||
|  | void pae_map_2M_page(void *pgtbl, uint64_t paddr, void *vmem_addr); | ||||||
|  |  | ||||||
| int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | int memset_pae(uint64_t dest, unsigned char pat, uint64_t length, void *pgtbl, | ||||||
| 	       void *vmem_addr); | 	       void *vmem_addr); | ||||||
|   | |||||||
| @@ -4,10 +4,10 @@ | |||||||
| #include <cpu/x86/pae.h> | #include <cpu/x86/pae.h> | ||||||
| #else | #else | ||||||
| #define memset_pae(a, b, c, d, e) 0 | #define memset_pae(a, b, c, d, e) 0 | ||||||
| #define MEMSET_PAE_PGTL_ALIGN 0 | #define PAE_PGTL_ALIGN 0 | ||||||
| #define MEMSET_PAE_PGTL_SIZE 0 | #define PAE_PGTL_SIZE 0 | ||||||
| #define MEMSET_PAE_VMEM_ALIGN 0 | #define PAE_VMEM_ALIGN 0 | ||||||
| #define MEMSET_PAE_VMEM_SIZE 0 | #define PAE_VMEM_SIZE 0 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #include <memrange.h> | #include <memrange.h> | ||||||
| @@ -84,15 +84,12 @@ static void clear_memory(void *unused) | |||||||
|  |  | ||||||
| 	if (ENV_X86) { | 	if (ENV_X86) { | ||||||
| 		/* Find space for PAE enabled memset */ | 		/* Find space for PAE enabled memset */ | ||||||
| 		pgtbl = get_free_memory_range(&mem, MEMSET_PAE_PGTL_ALIGN, | 		pgtbl = get_free_memory_range(&mem, PAE_PGTL_ALIGN, PAE_PGTL_SIZE); | ||||||
| 					MEMSET_PAE_PGTL_SIZE); |  | ||||||
|  |  | ||||||
| 		/* Don't touch page tables while clearing */ | 		/* Don't touch page tables while clearing */ | ||||||
| 		memranges_insert(&mem, pgtbl, MEMSET_PAE_PGTL_SIZE, | 		memranges_insert(&mem, pgtbl, PAE_PGTL_SIZE, BM_MEM_TABLE); | ||||||
| 					BM_MEM_TABLE); |  | ||||||
|  |  | ||||||
| 		vmem_addr = get_free_memory_range(&mem, MEMSET_PAE_VMEM_ALIGN, | 		vmem_addr = get_free_memory_range(&mem, PAE_VMEM_ALIGN, PAE_VMEM_SIZE); | ||||||
| 						MEMSET_PAE_VMEM_SIZE); |  | ||||||
|  |  | ||||||
| 		printk(BIOS_SPEW, "%s: pgtbl at %p, virt memory at %p\n", | 		printk(BIOS_SPEW, "%s: pgtbl at %p, virt memory at %p\n", | ||||||
| 		__func__, (void *)pgtbl, (void *)vmem_addr); | 		__func__, (void *)pgtbl, (void *)vmem_addr); | ||||||
| @@ -128,9 +125,9 @@ static void clear_memory(void *unused) | |||||||
| 	if (ENV_X86) { | 	if (ENV_X86) { | ||||||
| 		/* Clear previously skipped memory reserved for pagetables */ | 		/* Clear previously skipped memory reserved for pagetables */ | ||||||
| 		printk(BIOS_DEBUG, "%s: Clearing DRAM %016lx-%016lx\n", | 		printk(BIOS_DEBUG, "%s: Clearing DRAM %016lx-%016lx\n", | ||||||
| 		__func__, pgtbl, pgtbl + MEMSET_PAE_PGTL_SIZE); | 		__func__, pgtbl, pgtbl + PAE_PGTL_SIZE); | ||||||
|  |  | ||||||
| 		memset((void *)pgtbl, 0, MEMSET_PAE_PGTL_SIZE); | 		memset((void *)pgtbl, 0, PAE_PGTL_SIZE); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	memranges_teardown(&mem); | 	memranges_teardown(&mem); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user