blob: 4b5bdc8c8710e299c607a45949713b67d4b6a5fa [file] [log] [blame]
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +03001#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
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +03008struct real_mode_header *real_mode_header;
Jarkko Sakkinencda846f2012-05-08 21:22:46 +03009u32 *trampoline_cr4_features;
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030010
Yinghai Lu4f7b92262013-01-24 12:19:51 -080011void __init reserve_real_mode(void)
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030012{
13 phys_addr_t mem;
Yinghai Lu4f7b92262013-01-24 12:19:51 -080014 unsigned char *base;
15 size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
16
17 /* Has to be under 1M so we can execute real-mode AP code. */
18 mem = memblock_find_in_range(0, 1<<20, size, PAGE_SIZE);
19 if (!mem)
20 panic("Cannot allocate trampoline\n");
21
22 base = __va(mem);
23 memblock_reserve(mem, size);
24 real_mode_header = (struct real_mode_header *) base;
25 printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n",
26 base, (unsigned long long)mem, size);
27}
28
29void __init setup_real_mode(void)
30{
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030031 u16 real_mode_seg;
32 u32 *rel;
33 u32 count;
34 u32 *ptr;
35 u16 *seg;
36 int i;
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030037 unsigned char *base;
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030038 struct trampoline_header *trampoline_header;
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030039 size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030040#ifdef CONFIG_X86_64
41 u64 *trampoline_pgd;
H. Peter Anvin638d9572012-05-16 14:02:05 -070042 u64 efer;
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030043#endif
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030044
Yinghai Lu4f7b92262013-01-24 12:19:51 -080045 base = (unsigned char *)real_mode_header;
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030046
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030047 memcpy(base, real_mode_blob, size);
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030048
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030049 real_mode_seg = __pa(base) >> 4;
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030050 rel = (u32 *) real_mode_relocs;
51
52 /* 16-bit segment relocations. */
53 count = rel[0];
54 rel = &rel[1];
55 for (i = 0; i < count; i++) {
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030056 seg = (u16 *) (base + rel[i]);
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030057 *seg = real_mode_seg;
58 }
59
60 /* 32-bit linear relocations. */
61 count = rel[i];
62 rel = &rel[i + 1];
63 for (i = 0; i < count; i++) {
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +030064 ptr = (u32 *) (base + rel[i]);
65 *ptr += __pa(base);
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030066 }
67
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030068 /* Must be perfomed *after* relocation. */
69 trampoline_header = (struct trampoline_header *)
70 __va(real_mode_header->trampoline_header);
71
Jarkko Sakkinen48927bb2012-05-08 21:22:28 +030072#ifdef CONFIG_X86_32
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030073 trampoline_header->start = __pa(startup_32_smp);
74 trampoline_header->gdt_limit = __BOOT_DS + 7;
75 trampoline_header->gdt_base = __pa(boot_gdt);
Jarkko Sakkinen48927bb2012-05-08 21:22:28 +030076#else
H. Peter Anvin79603872012-05-16 13:22:41 -070077 /*
78 * Some AMD processors will #GP(0) if EFER.LMA is set in WRMSR
79 * so we need to mask it out.
80 */
H. Peter Anvin638d9572012-05-16 14:02:05 -070081 rdmsrl(MSR_EFER, efer);
82 trampoline_header->efer = efer & ~EFER_LMA;
Jarkko Sakkinencda846f2012-05-08 21:22:46 +030083
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030084 trampoline_header->start = (u64) secondary_startup_64;
Jarkko Sakkinencda846f2012-05-08 21:22:46 +030085 trampoline_cr4_features = &trampoline_header->cr4;
86 *trampoline_cr4_features = read_cr4();
87
Jarkko Sakkinenf37240f2012-05-08 21:22:43 +030088 trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd);
Yinghai Lu9735e912013-01-24 12:19:50 -080089 trampoline_pgd[0] = init_level4_pgt[pgd_index(__PAGE_OFFSET)].pgd;
90 trampoline_pgd[511] = init_level4_pgt[511].pgd;
Jarkko Sakkinen48927bb2012-05-08 21:22:28 +030091#endif
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030092}
93
94/*
Yinghai Lu4f7b92262013-01-24 12:19:51 -080095 * reserve_real_mode() gets called very early, to guarantee the
Yinghai Lu231b3642013-01-24 12:19:47 -080096 * availability of low memory. This is before the proper kernel page
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +030097 * tables are set up, so we cannot set page permissions in that
Yinghai Lu231b3642013-01-24 12:19:47 -080098 * function. Also trampoline code will be executed by APs so we
99 * need to mark it executable at do_pre_smp_initcalls() at least,
100 * thus run it as a early_initcall().
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +0300101 */
102static int __init set_real_mode_permissions(void)
103{
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +0300104 unsigned char *base = (unsigned char *) real_mode_header;
105 size_t size = PAGE_ALIGN(real_mode_blob_end - real_mode_blob);
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +0300106
Jarkko Sakkinenf156ffc2012-05-08 21:22:30 +0300107 size_t ro_size =
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +0300108 PAGE_ALIGN(real_mode_header->ro_end) -
109 __pa(base);
Jarkko Sakkinenf156ffc2012-05-08 21:22:30 +0300110
111 size_t text_size =
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +0300112 PAGE_ALIGN(real_mode_header->ro_end) -
113 real_mode_header->text_start;
Jarkko Sakkinenf156ffc2012-05-08 21:22:30 +0300114
115 unsigned long text_start =
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +0300116 (unsigned long) __va(real_mode_header->text_start);
Jarkko Sakkinenf156ffc2012-05-08 21:22:30 +0300117
Jarkko Sakkinenb429dbf2012-05-08 21:22:41 +0300118 set_memory_nx((unsigned long) base, size >> PAGE_SHIFT);
119 set_memory_ro((unsigned long) base, ro_size >> PAGE_SHIFT);
Jarkko Sakkinenf156ffc2012-05-08 21:22:30 +0300120 set_memory_x((unsigned long) text_start, text_size >> PAGE_SHIFT);
121
Jarkko Sakkinen084ee1c62012-05-08 21:22:26 +0300122 return 0;
123}
Yinghai Lu231b3642013-01-24 12:19:47 -0800124early_initcall(set_real_mode_permissions);