Commit | Line | Data |
---|---|---|
084ee1c6 JS |
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 | ||
b429dbf6 | 8 | struct real_mode_header *real_mode_header; |
084ee1c6 JS |
9 | |
10 | void __init setup_real_mode(void) | |
11 | { | |
12 | phys_addr_t mem; | |
13 | u16 real_mode_seg; | |
14 | u32 *rel; | |
15 | u32 count; | |
16 | u32 *ptr; | |
17 | u16 *seg; | |
18 | int i; | |
b429dbf6 | 19 | unsigned char *base; |
f37240f1 | 20 | struct trampoline_header *trampoline_header; |
b429dbf6 | 21 | size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob); |
f37240f1 JS |
22 | #ifdef CONFIG_X86_64 |
23 | u64 *trampoline_pgd; | |
24 | #endif | |
084ee1c6 JS |
25 | |
26 | /* Has to be in very low memory so we can execute real-mode AP code. */ | |
27 | mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE); | |
28 | if (!mem) | |
29 | panic("Cannot allocate trampoline\n"); | |
30 | ||
b429dbf6 | 31 | base = __va(mem); |
084ee1c6 | 32 | memblock_reserve(mem, size); |
b429dbf6 | 33 | real_mode_header = (struct real_mode_header *) base; |
084ee1c6 | 34 | printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n", |
b429dbf6 | 35 | base, (unsigned long long)mem, size); |
084ee1c6 | 36 | |
b429dbf6 | 37 | memcpy(base, real_mode_blob, size); |
084ee1c6 | 38 | |
b429dbf6 | 39 | real_mode_seg = __pa(base) >> 4; |
084ee1c6 JS |
40 | rel = (u32 *) real_mode_relocs; |
41 | ||
42 | /* 16-bit segment relocations. */ | |
43 | count = rel[0]; | |
44 | rel = &rel[1]; | |
45 | for (i = 0; i < count; i++) { | |
b429dbf6 | 46 | seg = (u16 *) (base + rel[i]); |
084ee1c6 JS |
47 | *seg = real_mode_seg; |
48 | } | |
49 | ||
50 | /* 32-bit linear relocations. */ | |
51 | count = rel[i]; | |
52 | rel = &rel[i + 1]; | |
53 | for (i = 0; i < count; i++) { | |
b429dbf6 JS |
54 | ptr = (u32 *) (base + rel[i]); |
55 | *ptr += __pa(base); | |
084ee1c6 JS |
56 | } |
57 | ||
f37240f1 JS |
58 | /* Must be perfomed *after* relocation. */ |
59 | trampoline_header = (struct trampoline_header *) | |
60 | __va(real_mode_header->trampoline_header); | |
61 | ||
48927bbb | 62 | #ifdef CONFIG_X86_32 |
f37240f1 JS |
63 | trampoline_header->start = __pa(startup_32_smp); |
64 | trampoline_header->gdt_limit = __BOOT_DS + 7; | |
65 | trampoline_header->gdt_base = __pa(boot_gdt); | |
48927bbb | 66 | #else |
f37240f1 JS |
67 | trampoline_header->start = (u64) secondary_startup_64; |
68 | trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd); | |
69 | trampoline_pgd[0] = __pa(level3_ident_pgt) + _KERNPG_TABLE; | |
70 | trampoline_pgd[511] = __pa(level3_kernel_pgt) + _KERNPG_TABLE; | |
48927bbb | 71 | #endif |
084ee1c6 JS |
72 | } |
73 | ||
74 | /* | |
75 | * set_real_mode_permissions() gets called very early, to guarantee the | |
76 | * availability of low memory. This is before the proper kernel page | |
77 | * tables are set up, so we cannot set page permissions in that | |
78 | * function. Thus, we use an arch_initcall instead. | |
79 | */ | |
80 | static int __init set_real_mode_permissions(void) | |
81 | { | |
b429dbf6 JS |
82 | unsigned char *base = (unsigned char *) real_mode_header; |
83 | size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob); | |
084ee1c6 | 84 | |
f156ffc4 | 85 | size_t ro_size = |
b429dbf6 JS |
86 | PAGE_ALIGN(real_mode_header->ro_end) - |
87 | __pa(base); | |
f156ffc4 JS |
88 | |
89 | size_t text_size = | |
b429dbf6 JS |
90 | PAGE_ALIGN(real_mode_header->ro_end) - |
91 | real_mode_header->text_start; | |
f156ffc4 JS |
92 | |
93 | unsigned long text_start = | |
b429dbf6 | 94 | (unsigned long) __va(real_mode_header->text_start); |
f156ffc4 | 95 | |
b429dbf6 JS |
96 | set_memory_nx((unsigned long) base, size >> PAGE_SHIFT); |
97 | set_memory_ro((unsigned long) base, ro_size >> PAGE_SHIFT); | |
f156ffc4 JS |
98 | set_memory_x((unsigned long) text_start, text_size >> PAGE_SHIFT); |
99 | ||
084ee1c6 JS |
100 | return 0; |
101 | } | |
102 | ||
103 | arch_initcall(set_real_mode_permissions); |