Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
init.c
Go to the documentation of this file.
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 
10 
12 {
14  u16 real_mode_seg;
15  u32 *rel;
16  u32 count;
17  u32 *ptr;
18  u16 *seg;
19  int i;
20  unsigned char *base;
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);