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 pgd_t early_level4_pgt[PTRS_PER_PGD]; | ||||||
| extern pmd_t early_dynamic_pgts[EARLY_DYNAMIC_PAGE_TABLES][PTRS_PER_PMD]; | 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); | 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 */ | /* Wipe all early page tables except for the kernel symbol map */ | ||||||
| static void __init reset_early_page_tables(void) | static void __init reset_early_page_tables(void) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -72,100 +72,11 @@ startup_64: | ||||||
| 	/* Sanitize CPU configuration */ | 	/* Sanitize CPU configuration */ | ||||||
| 	call verify_cpu | 	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	_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 | 	movq	$(early_level4_pgt - __START_KERNEL_map), %rax | ||||||
| 	jmp 1f | 	jmp 1f | ||||||
| ENTRY(secondary_startup_64) | ENTRY(secondary_startup_64) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kirill A. Shutemov
						Kirill A. Shutemov