diff options
author | Jarkko Sakkinen <jarkko.sakkinen@intel.com> | 2012-05-08 14:22:26 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-05-08 14:41:49 -0400 |
commit | 084ee1c641a068bfd1194d545f7dc9ab2043eb35 (patch) | |
tree | bc5454aedede17313df04eb82d354a76d14ee284 | |
parent | b3266bd6ff52efb9e57c7fbfff4c8f7363dfaab3 (diff) |
x86, realmode: Relocator for realmode code
Implements relocator for real mode code that is called
as part of setup_arch(). Processes segment relocations
and linear relocations. Real-mode code is relocated to
a free hole below 1 MB.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com>
Link: http://lkml.kernel.org/r/1336501366-28617-4-git-send-email-jarkko.sakkinen@intel.com
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/realmode.h | 26 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/realmode.c | 79 | ||||
-rw-r--r-- | arch/x86/kernel/setup.c | 2 |
4 files changed, 108 insertions, 0 deletions
diff --git a/arch/x86/include/asm/realmode.h b/arch/x86/include/asm/realmode.h new file mode 100644 index 000000000000..dc1bba534c14 --- /dev/null +++ b/arch/x86/include/asm/realmode.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef _ARCH_X86_REALMODE_H | ||
2 | #define _ARCH_X86_REALMODE_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <asm/io.h> | ||
6 | |||
7 | /* This must match data at realmode.S */ | ||
8 | struct real_mode_header { | ||
9 | u32 text_start; | ||
10 | u32 ro_end; | ||
11 | u32 end; | ||
12 | } __attribute__((__packed__)); | ||
13 | |||
14 | extern struct real_mode_header real_mode_header; | ||
15 | extern unsigned char *real_mode_base; | ||
16 | |||
17 | extern unsigned long init_rsp; | ||
18 | extern unsigned long initial_code; | ||
19 | extern unsigned long initial_gs; | ||
20 | |||
21 | extern unsigned char real_mode_blob[]; | ||
22 | extern unsigned char real_mode_relocs[]; | ||
23 | |||
24 | extern void __init setup_real_mode(void); | ||
25 | |||
26 | #endif /* _ARCH_X86_REALMODE_H */ | ||
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 532d2e090e6f..f9e19d4eb984 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -36,6 +36,7 @@ obj-y += pci-iommu_table.o | |||
36 | obj-y += resource.o | 36 | obj-y += resource.o |
37 | 37 | ||
38 | obj-y += trampoline.o trampoline_$(BITS).o | 38 | obj-y += trampoline.o trampoline_$(BITS).o |
39 | obj-y += realmode.o | ||
39 | obj-y += process.o | 40 | obj-y += process.o |
40 | obj-y += i387.o xsave.o | 41 | obj-y += i387.o xsave.o |
41 | obj-y += ptrace.o | 42 | obj-y += ptrace.o |
diff --git a/arch/x86/kernel/realmode.c b/arch/x86/kernel/realmode.c new file mode 100644 index 000000000000..7415c42547ac --- /dev/null +++ b/arch/x86/kernel/realmode.c | |||
@@ -0,0 +1,79 @@ | |||
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 | unsigned char *real_mode_base; | ||
9 | struct real_mode_header real_mode_header; | ||
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 | |||
21 | struct real_mode_header *header = | ||
22 | (struct real_mode_header *) real_mode_blob; | ||
23 | |||
24 | size_t size = PAGE_ALIGN(header->end); | ||
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 | |||
31 | real_mode_base = __va(mem); | ||
32 | memblock_reserve(mem, size); | ||
33 | |||
34 | printk(KERN_DEBUG "Base memory trampoline at [%p] %llx size %zu\n", | ||
35 | real_mode_base, (unsigned long long)mem, size); | ||
36 | |||
37 | memcpy(real_mode_base, real_mode_blob, size); | ||
38 | |||
39 | real_mode_seg = __pa(real_mode_base) >> 4; | ||
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++) { | ||
46 | seg = (u16 *) (real_mode_base + rel[i]); | ||
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++) { | ||
54 | ptr = (u32 *) (real_mode_base + rel[i]); | ||
55 | *ptr += __pa(real_mode_base); | ||
56 | } | ||
57 | |||
58 | /* Copied header will contain relocated physical addresses. */ | ||
59 | memcpy(&real_mode_header, real_mode_base, | ||
60 | sizeof(struct real_mode_header)); | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * set_real_mode_permissions() gets called very early, to guarantee the | ||
65 | * availability of low memory. This is before the proper kernel page | ||
66 | * tables are set up, so we cannot set page permissions in that | ||
67 | * function. Thus, we use an arch_initcall instead. | ||
68 | */ | ||
69 | static int __init set_real_mode_permissions(void) | ||
70 | { | ||
71 | size_t all_size = | ||
72 | PAGE_ALIGN(real_mode_header.end) - | ||
73 | __pa(real_mode_base); | ||
74 | |||
75 | set_memory_x((unsigned long) real_mode_base, all_size >> PAGE_SHIFT); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | arch_initcall(set_real_mode_permissions); | ||
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 1a2901562059..56e41242a6b8 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
@@ -74,6 +74,7 @@ | |||
74 | #include <asm/mtrr.h> | 74 | #include <asm/mtrr.h> |
75 | #include <asm/apic.h> | 75 | #include <asm/apic.h> |
76 | #include <asm/trampoline.h> | 76 | #include <asm/trampoline.h> |
77 | #include <asm/realmode.h> | ||
77 | #include <asm/e820.h> | 78 | #include <asm/e820.h> |
78 | #include <asm/mpspec.h> | 79 | #include <asm/mpspec.h> |
79 | #include <asm/setup.h> | 80 | #include <asm/setup.h> |
@@ -918,6 +919,7 @@ void __init setup_arch(char **cmdline_p) | |||
918 | max_pfn_mapped<<PAGE_SHIFT); | 919 | max_pfn_mapped<<PAGE_SHIFT); |
919 | 920 | ||
920 | setup_trampolines(); | 921 | setup_trampolines(); |
922 | setup_real_mode(); | ||
921 | 923 | ||
922 | init_gbpages(); | 924 | init_gbpages(); |
923 | 925 | ||