]>
Commit | Line | Data |
---|---|---|
5b83683f HY |
1 | /* |
2 | * x86_64 specific EFI support functions | |
3 | * Based on Extensible Firmware Interface Specification version 1.0 | |
4 | * | |
5 | * Copyright (C) 2005-2008 Intel Co. | |
6 | * Fenghua Yu <fenghua.yu@intel.com> | |
7 | * Bibo Mao <bibo.mao@intel.com> | |
8 | * Chandramouli Narayanan <mouli@linux.intel.com> | |
9 | * Huang Ying <ying.huang@intel.com> | |
10 | * | |
11 | * Code to convert EFI to E820 map has been implemented in elilo bootloader | |
12 | * based on a EFI patch by Edgar Hucek. Based on the E820 map, the page table | |
13 | * is setup appropriately for EFI runtime code. | |
14 | * - mouli 06/14/2007. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/kernel.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/mm.h> | |
21 | #include <linux/types.h> | |
22 | #include <linux/spinlock.h> | |
23 | #include <linux/bootmem.h> | |
24 | #include <linux/ioport.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/efi.h> | |
27 | #include <linux/uaccess.h> | |
28 | #include <linux/io.h> | |
29 | #include <linux/reboot.h> | |
30 | ||
31 | #include <asm/setup.h> | |
32 | #include <asm/page.h> | |
33 | #include <asm/e820.h> | |
34 | #include <asm/pgtable.h> | |
35 | #include <asm/tlbflush.h> | |
36 | #include <asm/cacheflush.h> | |
37 | #include <asm/proto.h> | |
38 | #include <asm/efi.h> | |
39 | ||
40 | static pgd_t save_pgd __initdata; | |
41 | static unsigned long efi_flags __initdata; | |
42 | ||
5b83683f HY |
43 | static void __init early_mapping_set_exec(unsigned long start, |
44 | unsigned long end, | |
45 | int executable) | |
46 | { | |
47 | pte_t *kpte; | |
48 | int level; | |
49 | ||
50 | while (start < end) { | |
51 | kpte = lookup_address((unsigned long)__va(start), &level); | |
52 | BUG_ON(!kpte); | |
53 | if (executable) | |
54 | set_pte(kpte, pte_mkexec(*kpte)); | |
55 | else | |
56 | set_pte(kpte, __pte((pte_val(*kpte) | _PAGE_NX) & \ | |
57 | __supported_pte_mask)); | |
58 | if (pte_huge(*kpte)) | |
59 | start = (start + PMD_SIZE) & PMD_MASK; | |
60 | else | |
61 | start = (start + PAGE_SIZE) & PAGE_MASK; | |
62 | } | |
63 | } | |
64 | ||
65 | static void __init early_runtime_code_mapping_set_exec(int executable) | |
66 | { | |
67 | efi_memory_desc_t *md; | |
68 | void *p; | |
69 | ||
70 | /* Make EFI runtime service code area executable */ | |
71 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | |
72 | md = p; | |
73 | if (md->type == EFI_RUNTIME_SERVICES_CODE) { | |
74 | unsigned long end; | |
75 | end = md->phys_addr + (md->num_pages << PAGE_SHIFT); | |
76 | early_mapping_set_exec(md->phys_addr, end, executable); | |
77 | } | |
78 | } | |
79 | } | |
80 | ||
81 | void __init efi_call_phys_prelog(void) | |
82 | { | |
83 | unsigned long vaddress; | |
84 | ||
85 | local_irq_save(efi_flags); | |
86 | early_runtime_code_mapping_set_exec(1); | |
87 | vaddress = (unsigned long)__va(0x0UL); | |
88 | pgd_val(save_pgd) = pgd_val(*pgd_offset_k(0x0UL)); | |
89 | set_pgd(pgd_offset_k(0x0UL), *pgd_offset_k(vaddress)); | |
90 | __flush_tlb_all(); | |
91 | } | |
92 | ||
93 | void __init efi_call_phys_epilog(void) | |
94 | { | |
95 | /* | |
96 | * After the lock is released, the original page table is restored. | |
97 | */ | |
98 | set_pgd(pgd_offset_k(0x0UL), save_pgd); | |
99 | early_runtime_code_mapping_set_exec(0); | |
100 | __flush_tlb_all(); | |
101 | local_irq_restore(efi_flags); | |
102 | } | |
103 | ||
104 | /* | |
105 | * We need to map the EFI memory map again after init_memory_mapping(). | |
106 | */ | |
107 | void __init efi_map_memmap(void) | |
108 | { | |
109 | memmap.map = __va(memmap.phys_map); | |
110 | memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); | |
111 | } | |
112 | ||
113 | void __init efi_reserve_bootmem(void) | |
114 | { | |
115 | reserve_bootmem_generic((unsigned long)memmap.phys_map, | |
116 | memmap.nr_map * memmap.desc_size); | |
117 | } | |
118 | ||
119 | void __init runtime_code_page_mkexec(void) | |
120 | { | |
121 | efi_memory_desc_t *md; | |
122 | void *p; | |
123 | ||
124 | /* Make EFI runtime service code area executable */ | |
125 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | |
126 | md = p; | |
127 | if (md->type == EFI_RUNTIME_SERVICES_CODE) | |
128 | change_page_attr_addr(md->virt_addr, | |
129 | md->num_pages, | |
130 | PAGE_KERNEL_EXEC); | |
131 | } | |
132 | __flush_tlb_all(); | |
133 | } | |
134 | ||
135 | void __iomem * __init efi_ioremap(unsigned long offset, | |
136 | unsigned long size) | |
137 | { | |
138 | static unsigned pages_mapped; | |
139 | unsigned long last_addr; | |
140 | unsigned i, pages; | |
141 | ||
142 | last_addr = offset + size - 1; | |
143 | offset &= PAGE_MASK; | |
144 | pages = (PAGE_ALIGN(last_addr) - offset) >> PAGE_SHIFT; | |
145 | if (pages_mapped + pages > MAX_EFI_IO_PAGES) | |
146 | return NULL; | |
147 | ||
148 | for (i = 0; i < pages; i++) { | |
149 | set_fixmap_nocache(FIX_EFI_IO_MAP_FIRST_PAGE - pages_mapped, | |
150 | offset); | |
151 | offset += PAGE_SIZE; | |
152 | pages_mapped++; | |
153 | } | |
154 | ||
155 | return (void __iomem *)__fix_to_virt(FIX_EFI_IO_MAP_FIRST_PAGE - \ | |
156 | (pages_mapped - pages)); | |
157 | } |