xref: /openbmc/linux/arch/x86/realmode/init.c (revision d0b73b48)
1 #include <linux/io.h>
2 #include <linux/memblock.h>
3 
4 #include <asm/cacheflush.h>
5 #include <asm/pgtable.h>
6 #include <asm/realmode.h>
7 
8 struct real_mode_header *real_mode_header;
9 u32 *trampoline_cr4_features;
10 
11 void __init setup_real_mode(void)
12 {
13 	phys_addr_t mem;
14 	u16 real_mode_seg;
15 	u32 *rel;
16 	u32 count;
17 	u32 *ptr;
18 	u16 *seg;
19 	int i;
20 	unsigned char *base;
21 	struct trampoline_header *trampoline_header;
22 	size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
23 #ifdef CONFIG_X86_64
24 	u64 *trampoline_pgd;
25 	u64 efer;
26 #endif
27 
28 	/* Has to be in very low memory so we can execute real-mode AP code. */
29 	mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE);
30 	if (!mem)
31 		panic("Cannot allocate trampoline\n");
32 
33 	base = __va(mem);
34 	memblock_reserve(mem, size);
35 	real_mode_header = (struct real_mode_header *) base;
36 	printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n",
37 	       base, (unsigned long long)mem, size);
38 
39 	memcpy(base, real_mode_blob, size);
40 
41 	real_mode_seg = __pa(base) >> 4;
42 	rel = (u32 *) real_mode_relocs;
43 
44 	/* 16-bit segment relocations. */
45 	count = rel[0];
46 	rel = &rel[1];
47 	for (i = 0; i < count; i++) {
48 		seg = (u16 *) (base + rel[i]);
49 		*seg = real_mode_seg;
50 	}
51 
52 	/* 32-bit linear relocations. */
53 	count = rel[i];
54 	rel =  &rel[i + 1];
55 	for (i = 0; i < count; i++) {
56 		ptr = (u32 *) (base + rel[i]);
57 		*ptr += __pa(base);
58 	}
59 
60 	/* Must be perfomed *after* relocation. */
61 	trampoline_header = (struct trampoline_header *)
62 		__va(real_mode_header->trampoline_header);
63 
64 #ifdef CONFIG_X86_32
65 	trampoline_header->start = __pa(startup_32_smp);
66 	trampoline_header->gdt_limit = __BOOT_DS + 7;
67 	trampoline_header->gdt_base = __pa(boot_gdt);
68 #else
69 	/*
70 	 * Some AMD processors will #GP(0) if EFER.LMA is set in WRMSR
71 	 * so we need to mask it out.
72 	 */
73 	rdmsrl(MSR_EFER, efer);
74 	trampoline_header->efer = efer & ~EFER_LMA;
75 
76 	trampoline_header->start = (u64) secondary_startup_64;
77 	trampoline_cr4_features = &trampoline_header->cr4;
78 	*trampoline_cr4_features = read_cr4();
79 
80 	trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd);
81 	trampoline_pgd[0] = __pa(level3_ident_pgt) + _KERNPG_TABLE;
82 	trampoline_pgd[511] = __pa(level3_kernel_pgt) + _KERNPG_TABLE;
83 #endif
84 }
85 
86 /*
87  * set_real_mode_permissions() gets called very early, to guarantee the
88  * availability of low memory.  This is before the proper kernel page
89  * tables are set up, so we cannot set page permissions in that
90  * function.  Thus, we use an arch_initcall instead.
91  */
92 static int __init set_real_mode_permissions(void)
93 {
94 	unsigned char *base = (unsigned char *) real_mode_header;
95 	size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
96 
97 	size_t ro_size =
98 		PAGE_ALIGN(real_mode_header->ro_end) -
99 		__pa(base);
100 
101 	size_t text_size =
102 		PAGE_ALIGN(real_mode_header->ro_end) -
103 		real_mode_header->text_start;
104 
105 	unsigned long text_start =
106 		(unsigned long) __va(real_mode_header->text_start);
107 
108 	set_memory_nx((unsigned long) base, size >> PAGE_SHIFT);
109 	set_memory_ro((unsigned long) base, ro_size >> PAGE_SHIFT);
110 	set_memory_x((unsigned long) text_start, text_size >> PAGE_SHIFT);
111 
112 	return 0;
113 }
114 
115 arch_initcall(set_real_mode_permissions);
116