forked from mirrors/linux
		
	x86/boot/64: Rewrite startup_64() in C
The patch write most of startup_64 logic in C. This is preparation for 5-level paging enabling. Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-arch@vger.kernel.org Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/20170606113133.22974-8-kirill.shutemov@linux.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
		
							parent
							
								
									34bbb0009f
								
							
						
					
					
						commit
						c88d71508e
					
				
					 2 changed files with 87 additions and 93 deletions
				
			
		|  | @ -35,9 +35,92 @@ | |||
|  */ | ||||
| extern pgd_t early_level4_pgt[PTRS_PER_PGD]; | ||||
| extern pmd_t early_dynamic_pgts[EARLY_DYNAMIC_PAGE_TABLES][PTRS_PER_PMD]; | ||||
| static unsigned int __initdata next_early_pgt = 2; | ||||
| static unsigned int __initdata next_early_pgt; | ||||
| pmdval_t early_pmd_flags = __PAGE_KERNEL_LARGE & ~(_PAGE_GLOBAL | _PAGE_NX); | ||||
| 
 | ||||
| static void __init *fixup_pointer(void *ptr, unsigned long physaddr) | ||||
| { | ||||
| 	return ptr - (void *)_text + (void *)physaddr; | ||||
| } | ||||
| 
 | ||||
| void __init __startup_64(unsigned long physaddr) | ||||
| { | ||||
| 	unsigned long load_delta, *p; | ||||
| 	pgdval_t *pgd; | ||||
| 	pudval_t *pud; | ||||
| 	pmdval_t *pmd, pmd_entry; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* Is the address too large? */ | ||||
| 	if (physaddr >> MAX_PHYSMEM_BITS) | ||||
| 		for (;;); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Compute the delta between the address I am compiled to run at | ||||
| 	 * and the address I am actually running at. | ||||
| 	 */ | ||||
| 	load_delta = physaddr - (unsigned long)(_text - __START_KERNEL_map); | ||||
| 
 | ||||
| 	/* Is the address not 2M aligned? */ | ||||
| 	if (load_delta & ~PMD_PAGE_MASK) | ||||
| 		for (;;); | ||||
| 
 | ||||
| 	/* Fixup the physical addresses in the page table */ | ||||
| 
 | ||||
| 	pgd = fixup_pointer(&early_level4_pgt, physaddr); | ||||
| 	pgd[pgd_index(__START_KERNEL_map)] += load_delta; | ||||
| 
 | ||||
| 	pud = fixup_pointer(&level3_kernel_pgt, physaddr); | ||||
| 	pud[510] += load_delta; | ||||
| 	pud[511] += load_delta; | ||||
| 
 | ||||
| 	pmd = fixup_pointer(level2_fixmap_pgt, physaddr); | ||||
| 	pmd[506] += load_delta; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set up the identity mapping for the switchover.  These | ||||
| 	 * entries should *NOT* have the global bit set!  This also | ||||
| 	 * creates a bunch of nonsense entries but that is fine -- | ||||
| 	 * it avoids problems around wraparound. | ||||
| 	 */ | ||||
| 
 | ||||
| 	pud = fixup_pointer(early_dynamic_pgts[next_early_pgt++], physaddr); | ||||
| 	pmd = fixup_pointer(early_dynamic_pgts[next_early_pgt++], physaddr); | ||||
| 
 | ||||
| 	i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD; | ||||
| 	pgd[i + 0] = (pgdval_t)pud + _KERNPG_TABLE; | ||||
| 	pgd[i + 1] = (pgdval_t)pud + _KERNPG_TABLE; | ||||
| 
 | ||||
| 	i = (physaddr >> PUD_SHIFT) % PTRS_PER_PUD; | ||||
| 	pud[i + 0] = (pudval_t)pmd + _KERNPG_TABLE; | ||||
| 	pud[i + 1] = (pudval_t)pmd + _KERNPG_TABLE; | ||||
| 
 | ||||
| 	pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL; | ||||
| 	pmd_entry +=  physaddr; | ||||
| 
 | ||||
| 	for (i = 0; i < DIV_ROUND_UP(_end - _text, PMD_SIZE); i++) { | ||||
| 		int idx = i + (physaddr >> PMD_SHIFT) % PTRS_PER_PMD; | ||||
| 		pmd[idx] = pmd_entry + i * PMD_SIZE; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Fixup the kernel text+data virtual addresses. Note that | ||||
| 	 * we might write invalid pmds, when the kernel is relocated | ||||
| 	 * cleanup_highmap() fixes this up along with the mappings | ||||
| 	 * beyond _end. | ||||
| 	 */ | ||||
| 
 | ||||
| 	pmd = fixup_pointer(level2_kernel_pgt, physaddr); | ||||
| 	for (i = 0; i < PTRS_PER_PMD; i++) { | ||||
| 		if (pmd[i] & _PAGE_PRESENT) | ||||
| 			pmd[i] += load_delta; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Fixup phys_base */ | ||||
| 	p = fixup_pointer(&phys_base, physaddr); | ||||
| 	*p += load_delta; | ||||
| } | ||||
| 
 | ||||
| /* Wipe all early page tables except for the kernel symbol map */ | ||||
| static void __init reset_early_page_tables(void) | ||||
| { | ||||
|  |  | |||
|  | @ -72,100 +72,11 @@ startup_64: | |||
| 	/* Sanitize CPU configuration */ | ||||
| 	call verify_cpu | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Compute the delta between the address I am compiled to run at and the | ||||
| 	 * address I am actually running at. | ||||
| 	 */ | ||||
| 	leaq	_text(%rip), %rbp | ||||
| 	subq	$_text - __START_KERNEL_map, %rbp | ||||
| 
 | ||||
| 	/* Is the address not 2M aligned? */ | ||||
| 	testl	$~PMD_PAGE_MASK, %ebp | ||||
| 	jnz	bad_address | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Is the address too large? | ||||
| 	 */ | ||||
| 	leaq	_text(%rip), %rax | ||||
| 	shrq	$MAX_PHYSMEM_BITS, %rax | ||||
| 	jnz	bad_address | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Fixup the physical addresses in the page table | ||||
| 	 */ | ||||
| 	addq	%rbp, early_level4_pgt + (L4_START_KERNEL*8)(%rip) | ||||
| 
 | ||||
| 	addq	%rbp, level3_kernel_pgt + (510*8)(%rip) | ||||
| 	addq	%rbp, level3_kernel_pgt + (511*8)(%rip) | ||||
| 
 | ||||
| 	addq	%rbp, level2_fixmap_pgt + (506*8)(%rip) | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Set up the identity mapping for the switchover.  These | ||||
| 	 * entries should *NOT* have the global bit set!  This also | ||||
| 	 * creates a bunch of nonsense entries but that is fine -- | ||||
| 	 * it avoids problems around wraparound. | ||||
| 	 */ | ||||
| 	leaq	_text(%rip), %rdi | ||||
| 	leaq	early_level4_pgt(%rip), %rbx | ||||
| 	pushq	%rsi | ||||
| 	call	__startup_64 | ||||
| 	popq	%rsi | ||||
| 
 | ||||
| 	movq	%rdi, %rax | ||||
| 	shrq	$PGDIR_SHIFT, %rax | ||||
| 
 | ||||
| 	leaq	(PAGE_SIZE + _KERNPG_TABLE)(%rbx), %rdx | ||||
| 	movq	%rdx, 0(%rbx,%rax,8) | ||||
| 	movq	%rdx, 8(%rbx,%rax,8) | ||||
| 
 | ||||
| 	addq	$PAGE_SIZE, %rdx | ||||
| 	movq	%rdi, %rax | ||||
| 	shrq	$PUD_SHIFT, %rax | ||||
| 	andl	$(PTRS_PER_PUD-1), %eax | ||||
| 	movq	%rdx, PAGE_SIZE(%rbx,%rax,8) | ||||
| 	incl	%eax | ||||
| 	andl	$(PTRS_PER_PUD-1), %eax | ||||
| 	movq	%rdx, PAGE_SIZE(%rbx,%rax,8) | ||||
| 
 | ||||
| 	addq	$PAGE_SIZE * 2, %rbx | ||||
| 	movq	%rdi, %rax | ||||
| 	shrq	$PMD_SHIFT, %rdi | ||||
| 	addq	$(__PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL), %rax | ||||
| 	leaq	(_end - 1)(%rip), %rcx | ||||
| 	shrq	$PMD_SHIFT, %rcx | ||||
| 	subq	%rdi, %rcx | ||||
| 	incl	%ecx | ||||
| 
 | ||||
| 1: | ||||
| 	andq	$(PTRS_PER_PMD - 1), %rdi | ||||
| 	movq	%rax, (%rbx,%rdi,8) | ||||
| 	incq	%rdi | ||||
| 	addq	$PMD_SIZE, %rax | ||||
| 	decl	%ecx | ||||
| 	jnz	1b | ||||
| 
 | ||||
| 	test %rbp, %rbp | ||||
| 	jz .Lskip_fixup | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Fixup the kernel text+data virtual addresses. Note that | ||||
| 	 * we might write invalid pmds, when the kernel is relocated | ||||
| 	 * cleanup_highmap() fixes this up along with the mappings | ||||
| 	 * beyond _end. | ||||
| 	 */ | ||||
| 	leaq	level2_kernel_pgt(%rip), %rdi | ||||
| 	leaq	PAGE_SIZE(%rdi), %r8 | ||||
| 	/* See if it is a valid page table entry */ | ||||
| 1:	testb	$_PAGE_PRESENT, 0(%rdi) | ||||
| 	jz	2f | ||||
| 	addq	%rbp, 0(%rdi) | ||||
| 	/* Go to the next page */ | ||||
| 2:	addq	$8, %rdi | ||||
| 	cmp	%r8, %rdi | ||||
| 	jne	1b | ||||
| 
 | ||||
| 	/* Fixup phys_base */ | ||||
| 	addq	%rbp, phys_base(%rip) | ||||
| 
 | ||||
| .Lskip_fixup: | ||||
| 	movq	$(early_level4_pgt - __START_KERNEL_map), %rax | ||||
| 	jmp 1f | ||||
| ENTRY(secondary_startup_64) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kirill A. Shutemov
						Kirill A. Shutemov