diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:01 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:01 -0400 |
commit | 9a163ed8e0552fdcffe405d2ea7134819a81456e (patch) | |
tree | b322fd2afbb812ba7ddfd22f3734aaab007c2aa5 /arch/x86/kernel/efi_32.c | |
parent | f7627e2513987bb5d4e8cb13c4e0a478352141ac (diff) |
i386: move kernel
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/efi_32.c')
-rw-r--r-- | arch/x86/kernel/efi_32.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/arch/x86/kernel/efi_32.c b/arch/x86/kernel/efi_32.c new file mode 100644 index 000000000000..2452c6fbe992 --- /dev/null +++ b/arch/x86/kernel/efi_32.c | |||
@@ -0,0 +1,712 @@ | |||
1 | /* | ||
2 | * Extensible Firmware Interface | ||
3 | * | ||
4 | * Based on Extensible Firmware Interface Specification version 1.0 | ||
5 | * | ||
6 | * Copyright (C) 1999 VA Linux Systems | ||
7 | * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> | ||
8 | * Copyright (C) 1999-2002 Hewlett-Packard Co. | ||
9 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
10 | * Stephane Eranian <eranian@hpl.hp.com> | ||
11 | * | ||
12 | * All EFI Runtime Services are not implemented yet as EFI only | ||
13 | * supports physical mode addressing on SoftSDV. This is to be fixed | ||
14 | * in a future version. --drummond 1999-07-20 | ||
15 | * | ||
16 | * Implemented EFI runtime services and virtual mode calls. --davidm | ||
17 | * | ||
18 | * Goutham Rao: <goutham.rao@intel.com> | ||
19 | * Skip non-WB memory and ignore empty memory ranges. | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | #include <linux/bootmem.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/efi.h> | ||
32 | #include <linux/kexec.h> | ||
33 | |||
34 | #include <asm/setup.h> | ||
35 | #include <asm/io.h> | ||
36 | #include <asm/page.h> | ||
37 | #include <asm/pgtable.h> | ||
38 | #include <asm/processor.h> | ||
39 | #include <asm/desc.h> | ||
40 | #include <asm/tlbflush.h> | ||
41 | |||
42 | #define EFI_DEBUG 0 | ||
43 | #define PFX "EFI: " | ||
44 | |||
45 | extern efi_status_t asmlinkage efi_call_phys(void *, ...); | ||
46 | |||
47 | struct efi efi; | ||
48 | EXPORT_SYMBOL(efi); | ||
49 | static struct efi efi_phys; | ||
50 | struct efi_memory_map memmap; | ||
51 | |||
52 | /* | ||
53 | * We require an early boot_ioremap mapping mechanism initially | ||
54 | */ | ||
55 | extern void * boot_ioremap(unsigned long, unsigned long); | ||
56 | |||
57 | /* | ||
58 | * To make EFI call EFI runtime service in physical addressing mode we need | ||
59 | * prelog/epilog before/after the invocation to disable interrupt, to | ||
60 | * claim EFI runtime service handler exclusively and to duplicate a memory in | ||
61 | * low memory space say 0 - 3G. | ||
62 | */ | ||
63 | |||
64 | static unsigned long efi_rt_eflags; | ||
65 | static DEFINE_SPINLOCK(efi_rt_lock); | ||
66 | static pgd_t efi_bak_pg_dir_pointer[2]; | ||
67 | |||
68 | static void efi_call_phys_prelog(void) __acquires(efi_rt_lock) | ||
69 | { | ||
70 | unsigned long cr4; | ||
71 | unsigned long temp; | ||
72 | struct Xgt_desc_struct gdt_descr; | ||
73 | |||
74 | spin_lock(&efi_rt_lock); | ||
75 | local_irq_save(efi_rt_eflags); | ||
76 | |||
77 | /* | ||
78 | * If I don't have PSE, I should just duplicate two entries in page | ||
79 | * directory. If I have PSE, I just need to duplicate one entry in | ||
80 | * page directory. | ||
81 | */ | ||
82 | cr4 = read_cr4(); | ||
83 | |||
84 | if (cr4 & X86_CR4_PSE) { | ||
85 | efi_bak_pg_dir_pointer[0].pgd = | ||
86 | swapper_pg_dir[pgd_index(0)].pgd; | ||
87 | swapper_pg_dir[0].pgd = | ||
88 | swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd; | ||
89 | } else { | ||
90 | efi_bak_pg_dir_pointer[0].pgd = | ||
91 | swapper_pg_dir[pgd_index(0)].pgd; | ||
92 | efi_bak_pg_dir_pointer[1].pgd = | ||
93 | swapper_pg_dir[pgd_index(0x400000)].pgd; | ||
94 | swapper_pg_dir[pgd_index(0)].pgd = | ||
95 | swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd; | ||
96 | temp = PAGE_OFFSET + 0x400000; | ||
97 | swapper_pg_dir[pgd_index(0x400000)].pgd = | ||
98 | swapper_pg_dir[pgd_index(temp)].pgd; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * After the lock is released, the original page table is restored. | ||
103 | */ | ||
104 | local_flush_tlb(); | ||
105 | |||
106 | gdt_descr.address = __pa(get_cpu_gdt_table(0)); | ||
107 | gdt_descr.size = GDT_SIZE - 1; | ||
108 | load_gdt(&gdt_descr); | ||
109 | } | ||
110 | |||
111 | static void efi_call_phys_epilog(void) __releases(efi_rt_lock) | ||
112 | { | ||
113 | unsigned long cr4; | ||
114 | struct Xgt_desc_struct gdt_descr; | ||
115 | |||
116 | gdt_descr.address = (unsigned long)get_cpu_gdt_table(0); | ||
117 | gdt_descr.size = GDT_SIZE - 1; | ||
118 | load_gdt(&gdt_descr); | ||
119 | |||
120 | cr4 = read_cr4(); | ||
121 | |||
122 | if (cr4 & X86_CR4_PSE) { | ||
123 | swapper_pg_dir[pgd_index(0)].pgd = | ||
124 | efi_bak_pg_dir_pointer[0].pgd; | ||
125 | } else { | ||
126 | swapper_pg_dir[pgd_index(0)].pgd = | ||
127 | efi_bak_pg_dir_pointer[0].pgd; | ||
128 | swapper_pg_dir[pgd_index(0x400000)].pgd = | ||
129 | efi_bak_pg_dir_pointer[1].pgd; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * After the lock is released, the original page table is restored. | ||
134 | */ | ||
135 | local_flush_tlb(); | ||
136 | |||
137 | local_irq_restore(efi_rt_eflags); | ||
138 | spin_unlock(&efi_rt_lock); | ||
139 | } | ||
140 | |||
141 | static efi_status_t | ||
142 | phys_efi_set_virtual_address_map(unsigned long memory_map_size, | ||
143 | unsigned long descriptor_size, | ||
144 | u32 descriptor_version, | ||
145 | efi_memory_desc_t *virtual_map) | ||
146 | { | ||
147 | efi_status_t status; | ||
148 | |||
149 | efi_call_phys_prelog(); | ||
150 | status = efi_call_phys(efi_phys.set_virtual_address_map, | ||
151 | memory_map_size, descriptor_size, | ||
152 | descriptor_version, virtual_map); | ||
153 | efi_call_phys_epilog(); | ||
154 | return status; | ||
155 | } | ||
156 | |||
157 | static efi_status_t | ||
158 | phys_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) | ||
159 | { | ||
160 | efi_status_t status; | ||
161 | |||
162 | efi_call_phys_prelog(); | ||
163 | status = efi_call_phys(efi_phys.get_time, tm, tc); | ||
164 | efi_call_phys_epilog(); | ||
165 | return status; | ||
166 | } | ||
167 | |||
168 | inline int efi_set_rtc_mmss(unsigned long nowtime) | ||
169 | { | ||
170 | int real_seconds, real_minutes; | ||
171 | efi_status_t status; | ||
172 | efi_time_t eft; | ||
173 | efi_time_cap_t cap; | ||
174 | |||
175 | spin_lock(&efi_rt_lock); | ||
176 | status = efi.get_time(&eft, &cap); | ||
177 | spin_unlock(&efi_rt_lock); | ||
178 | if (status != EFI_SUCCESS) | ||
179 | panic("Ooops, efitime: can't read time!\n"); | ||
180 | real_seconds = nowtime % 60; | ||
181 | real_minutes = nowtime / 60; | ||
182 | |||
183 | if (((abs(real_minutes - eft.minute) + 15)/30) & 1) | ||
184 | real_minutes += 30; | ||
185 | real_minutes %= 60; | ||
186 | |||
187 | eft.minute = real_minutes; | ||
188 | eft.second = real_seconds; | ||
189 | |||
190 | if (status != EFI_SUCCESS) { | ||
191 | printk("Ooops: efitime: can't read time!\n"); | ||
192 | return -1; | ||
193 | } | ||
194 | return 0; | ||
195 | } | ||
196 | /* | ||
197 | * This is used during kernel init before runtime | ||
198 | * services have been remapped and also during suspend, therefore, | ||
199 | * we'll need to call both in physical and virtual modes. | ||
200 | */ | ||
201 | inline unsigned long efi_get_time(void) | ||
202 | { | ||
203 | efi_status_t status; | ||
204 | efi_time_t eft; | ||
205 | efi_time_cap_t cap; | ||
206 | |||
207 | if (efi.get_time) { | ||
208 | /* if we are in virtual mode use remapped function */ | ||
209 | status = efi.get_time(&eft, &cap); | ||
210 | } else { | ||
211 | /* we are in physical mode */ | ||
212 | status = phys_efi_get_time(&eft, &cap); | ||
213 | } | ||
214 | |||
215 | if (status != EFI_SUCCESS) | ||
216 | printk("Oops: efitime: can't read time status: 0x%lx\n",status); | ||
217 | |||
218 | return mktime(eft.year, eft.month, eft.day, eft.hour, | ||
219 | eft.minute, eft.second); | ||
220 | } | ||
221 | |||
222 | int is_available_memory(efi_memory_desc_t * md) | ||
223 | { | ||
224 | if (!(md->attribute & EFI_MEMORY_WB)) | ||
225 | return 0; | ||
226 | |||
227 | switch (md->type) { | ||
228 | case EFI_LOADER_CODE: | ||
229 | case EFI_LOADER_DATA: | ||
230 | case EFI_BOOT_SERVICES_CODE: | ||
231 | case EFI_BOOT_SERVICES_DATA: | ||
232 | case EFI_CONVENTIONAL_MEMORY: | ||
233 | return 1; | ||
234 | } | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * We need to map the EFI memory map again after paging_init(). | ||
240 | */ | ||
241 | void __init efi_map_memmap(void) | ||
242 | { | ||
243 | memmap.map = NULL; | ||
244 | |||
245 | memmap.map = bt_ioremap((unsigned long) memmap.phys_map, | ||
246 | (memmap.nr_map * memmap.desc_size)); | ||
247 | if (memmap.map == NULL) | ||
248 | printk(KERN_ERR PFX "Could not remap the EFI memmap!\n"); | ||
249 | |||
250 | memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); | ||
251 | } | ||
252 | |||
253 | #if EFI_DEBUG | ||
254 | static void __init print_efi_memmap(void) | ||
255 | { | ||
256 | efi_memory_desc_t *md; | ||
257 | void *p; | ||
258 | int i; | ||
259 | |||
260 | for (p = memmap.map, i = 0; p < memmap.map_end; p += memmap.desc_size, i++) { | ||
261 | md = p; | ||
262 | printk(KERN_INFO "mem%02u: type=%u, attr=0x%llx, " | ||
263 | "range=[0x%016llx-0x%016llx) (%lluMB)\n", | ||
264 | i, md->type, md->attribute, md->phys_addr, | ||
265 | md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), | ||
266 | (md->num_pages >> (20 - EFI_PAGE_SHIFT))); | ||
267 | } | ||
268 | } | ||
269 | #endif /* EFI_DEBUG */ | ||
270 | |||
271 | /* | ||
272 | * Walks the EFI memory map and calls CALLBACK once for each EFI | ||
273 | * memory descriptor that has memory that is available for kernel use. | ||
274 | */ | ||
275 | void efi_memmap_walk(efi_freemem_callback_t callback, void *arg) | ||
276 | { | ||
277 | int prev_valid = 0; | ||
278 | struct range { | ||
279 | unsigned long start; | ||
280 | unsigned long end; | ||
281 | } uninitialized_var(prev), curr; | ||
282 | efi_memory_desc_t *md; | ||
283 | unsigned long start, end; | ||
284 | void *p; | ||
285 | |||
286 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
287 | md = p; | ||
288 | |||
289 | if ((md->num_pages == 0) || (!is_available_memory(md))) | ||
290 | continue; | ||
291 | |||
292 | curr.start = md->phys_addr; | ||
293 | curr.end = curr.start + (md->num_pages << EFI_PAGE_SHIFT); | ||
294 | |||
295 | if (!prev_valid) { | ||
296 | prev = curr; | ||
297 | prev_valid = 1; | ||
298 | } else { | ||
299 | if (curr.start < prev.start) | ||
300 | printk(KERN_INFO PFX "Unordered memory map\n"); | ||
301 | if (prev.end == curr.start) | ||
302 | prev.end = curr.end; | ||
303 | else { | ||
304 | start = | ||
305 | (unsigned long) (PAGE_ALIGN(prev.start)); | ||
306 | end = (unsigned long) (prev.end & PAGE_MASK); | ||
307 | if ((end > start) | ||
308 | && (*callback) (start, end, arg) < 0) | ||
309 | return; | ||
310 | prev = curr; | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | if (prev_valid) { | ||
315 | start = (unsigned long) PAGE_ALIGN(prev.start); | ||
316 | end = (unsigned long) (prev.end & PAGE_MASK); | ||
317 | if (end > start) | ||
318 | (*callback) (start, end, arg); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | void __init efi_init(void) | ||
323 | { | ||
324 | efi_config_table_t *config_tables; | ||
325 | efi_runtime_services_t *runtime; | ||
326 | efi_char16_t *c16; | ||
327 | char vendor[100] = "unknown"; | ||
328 | unsigned long num_config_tables; | ||
329 | int i = 0; | ||
330 | |||
331 | memset(&efi, 0, sizeof(efi) ); | ||
332 | memset(&efi_phys, 0, sizeof(efi_phys)); | ||
333 | |||
334 | efi_phys.systab = EFI_SYSTAB; | ||
335 | memmap.phys_map = EFI_MEMMAP; | ||
336 | memmap.nr_map = EFI_MEMMAP_SIZE/EFI_MEMDESC_SIZE; | ||
337 | memmap.desc_version = EFI_MEMDESC_VERSION; | ||
338 | memmap.desc_size = EFI_MEMDESC_SIZE; | ||
339 | |||
340 | efi.systab = (efi_system_table_t *) | ||
341 | boot_ioremap((unsigned long) efi_phys.systab, | ||
342 | sizeof(efi_system_table_t)); | ||
343 | /* | ||
344 | * Verify the EFI Table | ||
345 | */ | ||
346 | if (efi.systab == NULL) | ||
347 | printk(KERN_ERR PFX "Woah! Couldn't map the EFI system table.\n"); | ||
348 | if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | ||
349 | printk(KERN_ERR PFX "Woah! EFI system table signature incorrect\n"); | ||
350 | if ((efi.systab->hdr.revision >> 16) == 0) | ||
351 | printk(KERN_ERR PFX "Warning: EFI system table version " | ||
352 | "%d.%02d, expected 1.00 or greater\n", | ||
353 | efi.systab->hdr.revision >> 16, | ||
354 | efi.systab->hdr.revision & 0xffff); | ||
355 | |||
356 | /* | ||
357 | * Grab some details from the system table | ||
358 | */ | ||
359 | num_config_tables = efi.systab->nr_tables; | ||
360 | config_tables = (efi_config_table_t *)efi.systab->tables; | ||
361 | runtime = efi.systab->runtime; | ||
362 | |||
363 | /* | ||
364 | * Show what we know for posterity | ||
365 | */ | ||
366 | c16 = (efi_char16_t *) boot_ioremap(efi.systab->fw_vendor, 2); | ||
367 | if (c16) { | ||
368 | for (i = 0; i < (sizeof(vendor) - 1) && *c16; ++i) | ||
369 | vendor[i] = *c16++; | ||
370 | vendor[i] = '\0'; | ||
371 | } else | ||
372 | printk(KERN_ERR PFX "Could not map the firmware vendor!\n"); | ||
373 | |||
374 | printk(KERN_INFO PFX "EFI v%u.%.02u by %s \n", | ||
375 | efi.systab->hdr.revision >> 16, | ||
376 | efi.systab->hdr.revision & 0xffff, vendor); | ||
377 | |||
378 | /* | ||
379 | * Let's see what config tables the firmware passed to us. | ||
380 | */ | ||
381 | config_tables = (efi_config_table_t *) | ||
382 | boot_ioremap((unsigned long) config_tables, | ||
383 | num_config_tables * sizeof(efi_config_table_t)); | ||
384 | |||
385 | if (config_tables == NULL) | ||
386 | printk(KERN_ERR PFX "Could not map EFI Configuration Table!\n"); | ||
387 | |||
388 | efi.mps = EFI_INVALID_TABLE_ADDR; | ||
389 | efi.acpi = EFI_INVALID_TABLE_ADDR; | ||
390 | efi.acpi20 = EFI_INVALID_TABLE_ADDR; | ||
391 | efi.smbios = EFI_INVALID_TABLE_ADDR; | ||
392 | efi.sal_systab = EFI_INVALID_TABLE_ADDR; | ||
393 | efi.boot_info = EFI_INVALID_TABLE_ADDR; | ||
394 | efi.hcdp = EFI_INVALID_TABLE_ADDR; | ||
395 | efi.uga = EFI_INVALID_TABLE_ADDR; | ||
396 | |||
397 | for (i = 0; i < num_config_tables; i++) { | ||
398 | if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { | ||
399 | efi.mps = config_tables[i].table; | ||
400 | printk(KERN_INFO " MPS=0x%lx ", config_tables[i].table); | ||
401 | } else | ||
402 | if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { | ||
403 | efi.acpi20 = config_tables[i].table; | ||
404 | printk(KERN_INFO " ACPI 2.0=0x%lx ", config_tables[i].table); | ||
405 | } else | ||
406 | if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { | ||
407 | efi.acpi = config_tables[i].table; | ||
408 | printk(KERN_INFO " ACPI=0x%lx ", config_tables[i].table); | ||
409 | } else | ||
410 | if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { | ||
411 | efi.smbios = config_tables[i].table; | ||
412 | printk(KERN_INFO " SMBIOS=0x%lx ", config_tables[i].table); | ||
413 | } else | ||
414 | if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { | ||
415 | efi.hcdp = config_tables[i].table; | ||
416 | printk(KERN_INFO " HCDP=0x%lx ", config_tables[i].table); | ||
417 | } else | ||
418 | if (efi_guidcmp(config_tables[i].guid, UGA_IO_PROTOCOL_GUID) == 0) { | ||
419 | efi.uga = config_tables[i].table; | ||
420 | printk(KERN_INFO " UGA=0x%lx ", config_tables[i].table); | ||
421 | } | ||
422 | } | ||
423 | printk("\n"); | ||
424 | |||
425 | /* | ||
426 | * Check out the runtime services table. We need to map | ||
427 | * the runtime services table so that we can grab the physical | ||
428 | * address of several of the EFI runtime functions, needed to | ||
429 | * set the firmware into virtual mode. | ||
430 | */ | ||
431 | |||
432 | runtime = (efi_runtime_services_t *) boot_ioremap((unsigned long) | ||
433 | runtime, | ||
434 | sizeof(efi_runtime_services_t)); | ||
435 | if (runtime != NULL) { | ||
436 | /* | ||
437 | * We will only need *early* access to the following | ||
438 | * two EFI runtime services before set_virtual_address_map | ||
439 | * is invoked. | ||
440 | */ | ||
441 | efi_phys.get_time = (efi_get_time_t *) runtime->get_time; | ||
442 | efi_phys.set_virtual_address_map = | ||
443 | (efi_set_virtual_address_map_t *) | ||
444 | runtime->set_virtual_address_map; | ||
445 | } else | ||
446 | printk(KERN_ERR PFX "Could not map the runtime service table!\n"); | ||
447 | |||
448 | /* Map the EFI memory map for use until paging_init() */ | ||
449 | memmap.map = boot_ioremap((unsigned long) EFI_MEMMAP, EFI_MEMMAP_SIZE); | ||
450 | if (memmap.map == NULL) | ||
451 | printk(KERN_ERR PFX "Could not map the EFI memory map!\n"); | ||
452 | |||
453 | memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size); | ||
454 | |||
455 | #if EFI_DEBUG | ||
456 | print_efi_memmap(); | ||
457 | #endif | ||
458 | } | ||
459 | |||
460 | static inline void __init check_range_for_systab(efi_memory_desc_t *md) | ||
461 | { | ||
462 | if (((unsigned long)md->phys_addr <= (unsigned long)efi_phys.systab) && | ||
463 | ((unsigned long)efi_phys.systab < md->phys_addr + | ||
464 | ((unsigned long)md->num_pages << EFI_PAGE_SHIFT))) { | ||
465 | unsigned long addr; | ||
466 | |||
467 | addr = md->virt_addr - md->phys_addr + | ||
468 | (unsigned long)efi_phys.systab; | ||
469 | efi.systab = (efi_system_table_t *)addr; | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * Wrap all the virtual calls in a way that forces the parameters on the stack. | ||
475 | */ | ||
476 | |||
477 | #define efi_call_virt(f, args...) \ | ||
478 | ((efi_##f##_t __attribute__((regparm(0)))*)efi.systab->runtime->f)(args) | ||
479 | |||
480 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) | ||
481 | { | ||
482 | return efi_call_virt(get_time, tm, tc); | ||
483 | } | ||
484 | |||
485 | static efi_status_t virt_efi_set_time (efi_time_t *tm) | ||
486 | { | ||
487 | return efi_call_virt(set_time, tm); | ||
488 | } | ||
489 | |||
490 | static efi_status_t virt_efi_get_wakeup_time (efi_bool_t *enabled, | ||
491 | efi_bool_t *pending, | ||
492 | efi_time_t *tm) | ||
493 | { | ||
494 | return efi_call_virt(get_wakeup_time, enabled, pending, tm); | ||
495 | } | ||
496 | |||
497 | static efi_status_t virt_efi_set_wakeup_time (efi_bool_t enabled, | ||
498 | efi_time_t *tm) | ||
499 | { | ||
500 | return efi_call_virt(set_wakeup_time, enabled, tm); | ||
501 | } | ||
502 | |||
503 | static efi_status_t virt_efi_get_variable (efi_char16_t *name, | ||
504 | efi_guid_t *vendor, u32 *attr, | ||
505 | unsigned long *data_size, void *data) | ||
506 | { | ||
507 | return efi_call_virt(get_variable, name, vendor, attr, data_size, data); | ||
508 | } | ||
509 | |||
510 | static efi_status_t virt_efi_get_next_variable (unsigned long *name_size, | ||
511 | efi_char16_t *name, | ||
512 | efi_guid_t *vendor) | ||
513 | { | ||
514 | return efi_call_virt(get_next_variable, name_size, name, vendor); | ||
515 | } | ||
516 | |||
517 | static efi_status_t virt_efi_set_variable (efi_char16_t *name, | ||
518 | efi_guid_t *vendor, | ||
519 | unsigned long attr, | ||
520 | unsigned long data_size, void *data) | ||
521 | { | ||
522 | return efi_call_virt(set_variable, name, vendor, attr, data_size, data); | ||
523 | } | ||
524 | |||
525 | static efi_status_t virt_efi_get_next_high_mono_count (u32 *count) | ||
526 | { | ||
527 | return efi_call_virt(get_next_high_mono_count, count); | ||
528 | } | ||
529 | |||
530 | static void virt_efi_reset_system (int reset_type, efi_status_t status, | ||
531 | unsigned long data_size, | ||
532 | efi_char16_t *data) | ||
533 | { | ||
534 | efi_call_virt(reset_system, reset_type, status, data_size, data); | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * This function will switch the EFI runtime services to virtual mode. | ||
539 | * Essentially, look through the EFI memmap and map every region that | ||
540 | * has the runtime attribute bit set in its memory descriptor and update | ||
541 | * that memory descriptor with the virtual address obtained from ioremap(). | ||
542 | * This enables the runtime services to be called without having to | ||
543 | * thunk back into physical mode for every invocation. | ||
544 | */ | ||
545 | |||
546 | void __init efi_enter_virtual_mode(void) | ||
547 | { | ||
548 | efi_memory_desc_t *md; | ||
549 | efi_status_t status; | ||
550 | void *p; | ||
551 | |||
552 | efi.systab = NULL; | ||
553 | |||
554 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
555 | md = p; | ||
556 | |||
557 | if (!(md->attribute & EFI_MEMORY_RUNTIME)) | ||
558 | continue; | ||
559 | |||
560 | md->virt_addr = (unsigned long)ioremap(md->phys_addr, | ||
561 | md->num_pages << EFI_PAGE_SHIFT); | ||
562 | if (!(unsigned long)md->virt_addr) { | ||
563 | printk(KERN_ERR PFX "ioremap of 0x%lX failed\n", | ||
564 | (unsigned long)md->phys_addr); | ||
565 | } | ||
566 | /* update the virtual address of the EFI system table */ | ||
567 | check_range_for_systab(md); | ||
568 | } | ||
569 | |||
570 | BUG_ON(!efi.systab); | ||
571 | |||
572 | status = phys_efi_set_virtual_address_map( | ||
573 | memmap.desc_size * memmap.nr_map, | ||
574 | memmap.desc_size, | ||
575 | memmap.desc_version, | ||
576 | memmap.phys_map); | ||
577 | |||
578 | if (status != EFI_SUCCESS) { | ||
579 | printk (KERN_ALERT "You are screwed! " | ||
580 | "Unable to switch EFI into virtual mode " | ||
581 | "(status=%lx)\n", status); | ||
582 | panic("EFI call to SetVirtualAddressMap() failed!"); | ||
583 | } | ||
584 | |||
585 | /* | ||
586 | * Now that EFI is in virtual mode, update the function | ||
587 | * pointers in the runtime service table to the new virtual addresses. | ||
588 | */ | ||
589 | |||
590 | efi.get_time = virt_efi_get_time; | ||
591 | efi.set_time = virt_efi_set_time; | ||
592 | efi.get_wakeup_time = virt_efi_get_wakeup_time; | ||
593 | efi.set_wakeup_time = virt_efi_set_wakeup_time; | ||
594 | efi.get_variable = virt_efi_get_variable; | ||
595 | efi.get_next_variable = virt_efi_get_next_variable; | ||
596 | efi.set_variable = virt_efi_set_variable; | ||
597 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; | ||
598 | efi.reset_system = virt_efi_reset_system; | ||
599 | } | ||
600 | |||
601 | void __init | ||
602 | efi_initialize_iomem_resources(struct resource *code_resource, | ||
603 | struct resource *data_resource) | ||
604 | { | ||
605 | struct resource *res; | ||
606 | efi_memory_desc_t *md; | ||
607 | void *p; | ||
608 | |||
609 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
610 | md = p; | ||
611 | |||
612 | if ((md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT)) > | ||
613 | 0x100000000ULL) | ||
614 | continue; | ||
615 | res = kzalloc(sizeof(struct resource), GFP_ATOMIC); | ||
616 | switch (md->type) { | ||
617 | case EFI_RESERVED_TYPE: | ||
618 | res->name = "Reserved Memory"; | ||
619 | break; | ||
620 | case EFI_LOADER_CODE: | ||
621 | res->name = "Loader Code"; | ||
622 | break; | ||
623 | case EFI_LOADER_DATA: | ||
624 | res->name = "Loader Data"; | ||
625 | break; | ||
626 | case EFI_BOOT_SERVICES_DATA: | ||
627 | res->name = "BootServices Data"; | ||
628 | break; | ||
629 | case EFI_BOOT_SERVICES_CODE: | ||
630 | res->name = "BootServices Code"; | ||
631 | break; | ||
632 | case EFI_RUNTIME_SERVICES_CODE: | ||
633 | res->name = "Runtime Service Code"; | ||
634 | break; | ||
635 | case EFI_RUNTIME_SERVICES_DATA: | ||
636 | res->name = "Runtime Service Data"; | ||
637 | break; | ||
638 | case EFI_CONVENTIONAL_MEMORY: | ||
639 | res->name = "Conventional Memory"; | ||
640 | break; | ||
641 | case EFI_UNUSABLE_MEMORY: | ||
642 | res->name = "Unusable Memory"; | ||
643 | break; | ||
644 | case EFI_ACPI_RECLAIM_MEMORY: | ||
645 | res->name = "ACPI Reclaim"; | ||
646 | break; | ||
647 | case EFI_ACPI_MEMORY_NVS: | ||
648 | res->name = "ACPI NVS"; | ||
649 | break; | ||
650 | case EFI_MEMORY_MAPPED_IO: | ||
651 | res->name = "Memory Mapped IO"; | ||
652 | break; | ||
653 | case EFI_MEMORY_MAPPED_IO_PORT_SPACE: | ||
654 | res->name = "Memory Mapped IO Port Space"; | ||
655 | break; | ||
656 | default: | ||
657 | res->name = "Reserved"; | ||
658 | break; | ||
659 | } | ||
660 | res->start = md->phys_addr; | ||
661 | res->end = res->start + ((md->num_pages << EFI_PAGE_SHIFT) - 1); | ||
662 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; | ||
663 | if (request_resource(&iomem_resource, res) < 0) | ||
664 | printk(KERN_ERR PFX "Failed to allocate res %s : " | ||
665 | "0x%llx-0x%llx\n", res->name, | ||
666 | (unsigned long long)res->start, | ||
667 | (unsigned long long)res->end); | ||
668 | /* | ||
669 | * We don't know which region contains kernel data so we try | ||
670 | * it repeatedly and let the resource manager test it. | ||
671 | */ | ||
672 | if (md->type == EFI_CONVENTIONAL_MEMORY) { | ||
673 | request_resource(res, code_resource); | ||
674 | request_resource(res, data_resource); | ||
675 | #ifdef CONFIG_KEXEC | ||
676 | request_resource(res, &crashk_res); | ||
677 | #endif | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /* | ||
683 | * Convenience functions to obtain memory types and attributes | ||
684 | */ | ||
685 | |||
686 | u32 efi_mem_type(unsigned long phys_addr) | ||
687 | { | ||
688 | efi_memory_desc_t *md; | ||
689 | void *p; | ||
690 | |||
691 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
692 | md = p; | ||
693 | if ((md->phys_addr <= phys_addr) && (phys_addr < | ||
694 | (md->phys_addr + (md-> num_pages << EFI_PAGE_SHIFT)) )) | ||
695 | return md->type; | ||
696 | } | ||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | u64 efi_mem_attributes(unsigned long phys_addr) | ||
701 | { | ||
702 | efi_memory_desc_t *md; | ||
703 | void *p; | ||
704 | |||
705 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
706 | md = p; | ||
707 | if ((md->phys_addr <= phys_addr) && (phys_addr < | ||
708 | (md->phys_addr + (md-> num_pages << EFI_PAGE_SHIFT)) )) | ||
709 | return md->attribute; | ||
710 | } | ||
711 | return 0; | ||
712 | } | ||