Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / efiemu / runtime / efiemu.c
blobd923e409f921384dfe29a9b241bb2feb7a0b0e38
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 /* This is an emulation of EFI runtime services.
20 This allows a more uniform boot on i386 machines.
21 As it emulates only runtime serviceit isn't able
22 to chainload EFI bootloader on non-EFI system (TODO) */
24 #ifdef __i386__
25 #include <grub/i386/types.h>
26 #else
27 #include <grub/x86_64/types.h>
28 #endif
30 #include <grub/symbol.h>
31 #include <grub/types.h>
32 #include <grub/efi/api.h>
33 #include <grub/efiemu/runtime.h>
35 grub_efi_status_t
36 efiemu_get_time (grub_efi_time_t *time,
37 grub_efi_time_capabilities_t *capabilities);
38 grub_efi_status_t
39 efiemu_set_time (grub_efi_time_t *time);
41 grub_efi_status_t
42 efiemu_get_wakeup_time (grub_efi_boolean_t *enabled,
43 grub_efi_boolean_t *pending,
44 grub_efi_time_t *time);
45 grub_efi_status_t
46 efiemu_set_wakeup_time (grub_efi_boolean_t enabled,
47 grub_efi_time_t *time);
49 #ifdef __APPLE__
50 #define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical")));
51 #else
52 #define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical")));
53 #endif
55 grub_efi_status_t
56 efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
57 grub_efi_uintn_t descriptor_size,
58 grub_efi_uint32_t descriptor_version,
59 grub_efi_memory_descriptor_t *virtual_map)
60 PHYSICAL_ATTRIBUTE;
62 grub_efi_status_t
63 efiemu_convert_pointer (grub_efi_uintn_t debug_disposition,
64 void **address)
65 PHYSICAL_ATTRIBUTE;
67 grub_efi_status_t
68 efiemu_get_variable (grub_efi_char16_t *variable_name,
69 const grub_efi_guid_t *vendor_guid,
70 grub_efi_uint32_t *attributes,
71 grub_efi_uintn_t *data_size,
72 void *data);
74 grub_efi_status_t
75 efiemu_get_next_variable_name (grub_efi_uintn_t *variable_name_size,
76 grub_efi_char16_t *variable_name,
77 grub_efi_guid_t *vendor_guid);
79 grub_efi_status_t
80 efiemu_set_variable (grub_efi_char16_t *variable_name,
81 const grub_efi_guid_t *vendor_guid,
82 grub_efi_uint32_t attributes,
83 grub_efi_uintn_t data_size,
84 void *data);
85 grub_efi_status_t
86 efiemu_get_next_high_monotonic_count (grub_efi_uint32_t *high_count);
87 void
88 efiemu_reset_system (grub_efi_reset_type_t reset_type,
89 grub_efi_status_t reset_status,
90 grub_efi_uintn_t data_size,
91 grub_efi_char16_t *reset_data);
93 grub_efi_status_t
94 EFI_FUNC (efiemu_set_virtual_address_map) (grub_efi_uintn_t,
95 grub_efi_uintn_t,
96 grub_efi_uint32_t,
97 grub_efi_memory_descriptor_t *)
98 PHYSICAL_ATTRIBUTE;
99 grub_efi_status_t
100 EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
101 void **address)
102 PHYSICAL_ATTRIBUTE;
103 static grub_uint32_t
104 efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
105 PHYSICAL_ATTRIBUTE;
106 static void
107 init_crc32_table (void)
108 PHYSICAL_ATTRIBUTE;
109 static grub_uint32_t
110 reflect (grub_uint32_t ref, int len)
111 PHYSICAL_ATTRIBUTE;
114 The log. It's used when examining memory dump
116 static grub_uint8_t loge[1000] = "EFIEMULOG";
117 static int logn = 9;
118 #define LOG(x) { if (logn<900) loge[logn++]=x; }
120 /* Interface with grub */
121 extern grub_uint8_t efiemu_ptv_relocated;
122 struct grub_efi_runtime_services efiemu_runtime_services;
123 struct grub_efi_system_table efiemu_system_table;
124 extern struct grub_efiemu_ptv_rel efiemu_ptv_relloc[];
125 extern grub_uint8_t efiemu_variables[];
126 extern grub_uint32_t efiemu_varsize;
127 extern grub_uint32_t efiemu_high_monotonic_count;
128 extern grub_int16_t efiemu_time_zone;
129 extern grub_uint8_t efiemu_time_daylight;
130 extern grub_uint32_t efiemu_time_accuracy;
132 /* Some standard functions because we need to be standalone */
133 static void
134 efiemu_memcpy (void *to, const void *from, int count)
136 int i;
137 for (i = 0; i < count; i++)
138 ((grub_uint8_t *) to)[i] = ((const grub_uint8_t *) from)[i];
141 static int
142 efiemu_str16equal (grub_uint16_t *a, grub_uint16_t *b)
144 grub_uint16_t *ptr1, *ptr2;
145 for (ptr1=a,ptr2=b; *ptr1 && *ptr2 == *ptr1; ptr1++, ptr2++);
146 return *ptr2 == *ptr1;
149 static grub_size_t
150 efiemu_str16len (grub_uint16_t *a)
152 grub_uint16_t *ptr1;
153 for (ptr1 = a; *ptr1; ptr1++);
154 return ptr1 - a;
157 static int
158 efiemu_memequal (const void *a, const void *b, grub_size_t n)
160 grub_uint8_t *ptr1, *ptr2;
161 for (ptr1 = (grub_uint8_t *) a, ptr2 = (grub_uint8_t *)b;
162 ptr1 < (grub_uint8_t *)a + n && *ptr2 == *ptr1; ptr1++, ptr2++);
163 return ptr1 == a + n;
166 static void
167 efiemu_memset (grub_uint8_t *a, grub_uint8_t b, grub_size_t n)
169 grub_uint8_t *ptr1;
170 for (ptr1=a; ptr1 < a + n; ptr1++)
171 *ptr1 = b;
174 static inline void
175 write_cmos (grub_uint8_t addr, grub_uint8_t val)
177 __asm__ __volatile__ ("outb %%al,$0x70\n"
178 "mov %%cl, %%al\n"
179 "outb %%al,$0x71": :"a" (addr), "c" (val));
182 static inline grub_uint8_t
183 read_cmos (grub_uint8_t addr)
185 grub_uint8_t ret;
186 __asm__ __volatile__ ("outb %%al, $0x70\n"
187 "inb $0x71, %%al": "=a"(ret) :"a" (addr));
188 return ret;
191 /* Needed by some gcc versions */
192 int __stack_chk_fail ()
194 return 0;
197 /* The function that implement runtime services as specified in
198 EFI specification */
199 static inline grub_uint8_t
200 bcd_to_hex (grub_uint8_t in)
202 return 10 * ((in & 0xf0) >> 4) + (in & 0x0f);
205 grub_efi_status_t
206 EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time,
207 grub_efi_time_capabilities_t *capabilities)
209 LOG ('a');
210 grub_uint8_t state;
211 state = read_cmos (0xb);
212 if (!(state & (1 << 2)))
214 time->year = 2000 + bcd_to_hex (read_cmos (0x9));
215 time->month = bcd_to_hex (read_cmos (0x8));
216 time->day = bcd_to_hex (read_cmos (0x7));
217 time->hour = bcd_to_hex (read_cmos (0x4));
218 if (time->hour >= 81)
219 time->hour -= 80 - 12;
220 if (time->hour == 24)
221 time->hour = 0;
222 time->minute = bcd_to_hex (read_cmos (0x2));
223 time->second = bcd_to_hex (read_cmos (0x0));
225 else
227 time->year = 2000 + read_cmos (0x9);
228 time->month = read_cmos (0x8);
229 time->day = read_cmos (0x7);
230 time->hour = read_cmos (0x4);
231 if (time->hour >= 0x81)
232 time->hour -= 0x80 - 12;
233 if (time->hour == 24)
234 time->hour = 0;
235 time->minute = read_cmos (0x2);
236 time->second = read_cmos (0x0);
238 time->nanosecond = 0;
239 time->pad1 = 0;
240 time->pad2 = 0;
241 time->time_zone = efiemu_time_zone;
242 time->daylight = efiemu_time_daylight;
243 capabilities->resolution = 1;
244 capabilities->accuracy = efiemu_time_accuracy;
245 capabilities->sets_to_zero = 0;
246 return GRUB_EFI_SUCCESS;
249 grub_efi_status_t
250 EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time)
252 LOG ('b');
253 grub_uint8_t state;
254 state = read_cmos (0xb);
255 write_cmos (0xb, state | 0x6);
256 write_cmos (0x9, time->year - 2000);
257 write_cmos (0x8, time->month);
258 write_cmos (0x7, time->day);
259 write_cmos (0x4, time->hour);
260 write_cmos (0x2, time->minute);
261 write_cmos (0x0, time->second);
262 efiemu_time_zone = time->time_zone;
263 efiemu_time_daylight = time->daylight;
264 return GRUB_EFI_SUCCESS;
267 /* Following 2 functions are vendor specific. So announce it as unsupported */
268 grub_efi_status_t
269 EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled,
270 grub_efi_boolean_t *pending,
271 grub_efi_time_t *time)
273 LOG ('c');
274 return GRUB_EFI_UNSUPPORTED;
277 grub_efi_status_t
278 EFI_FUNC (efiemu_set_wakeup_time) (grub_efi_boolean_t enabled,
279 grub_efi_time_t *time)
281 LOG ('d');
282 return GRUB_EFI_UNSUPPORTED;
285 static grub_uint32_t crc32_table [256];
287 static grub_uint32_t
288 reflect (grub_uint32_t ref, int len)
290 grub_uint32_t result = 0;
291 int i;
293 for (i = 1; i <= len; i++)
295 if (ref & 1)
296 result |= 1 << (len - i);
297 ref >>= 1;
300 return result;
303 static void
304 init_crc32_table (void)
306 grub_uint32_t polynomial = 0x04c11db7;
307 int i, j;
309 for(i = 0; i < 256; i++)
311 crc32_table[i] = reflect(i, 8) << 24;
312 for (j = 0; j < 8; j++)
313 crc32_table[i] = (crc32_table[i] << 1) ^
314 (crc32_table[i] & (1 << 31) ? polynomial : 0);
315 crc32_table[i] = reflect(crc32_table[i], 32);
319 static grub_uint32_t
320 efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
322 int i;
323 grub_uint8_t *data = buf;
325 if (! crc32_table[1])
326 init_crc32_table ();
328 crc^= 0xffffffff;
330 for (i = 0; i < size; i++)
332 crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data];
333 data++;
336 return crc ^ 0xffffffff;
340 grub_efi_status_t EFI_FUNC
341 (efiemu_set_virtual_address_map) (grub_efi_uintn_t memory_map_size,
342 grub_efi_uintn_t descriptor_size,
343 grub_efi_uint32_t descriptor_version,
344 grub_efi_memory_descriptor_t *virtual_map)
346 struct grub_efiemu_ptv_rel *cur_relloc;
348 LOG ('e');
350 /* Ensure that we are called only once */
351 if (efiemu_ptv_relocated)
352 return GRUB_EFI_UNSUPPORTED;
353 efiemu_ptv_relocated = 1;
355 /* Correct addresses using information supplied by grub */
356 for (cur_relloc = efiemu_ptv_relloc; cur_relloc->size;cur_relloc++)
358 grub_int64_t corr = 0;
359 grub_efi_memory_descriptor_t *descptr;
361 /* Compute correction */
362 for (descptr = virtual_map;
363 ((grub_uint8_t *) descptr - (grub_uint8_t *) virtual_map)
364 < memory_map_size;
365 descptr = (grub_efi_memory_descriptor_t *)
366 ((grub_uint8_t *) descptr + descriptor_size))
368 if (descptr->type == cur_relloc->plustype)
369 corr += descptr->virtual_start - descptr->physical_start;
370 if (descptr->type == cur_relloc->minustype)
371 corr -= descptr->virtual_start - descptr->physical_start;
374 /* Apply correction */
375 switch (cur_relloc->size)
377 case 8:
378 *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr;
379 break;
380 case 4:
381 *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr;
382 break;
383 case 2:
384 *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr;
385 break;
386 case 1:
387 *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr;
388 break;
392 /* Recompute crc32 of system table and runtime services */
393 efiemu_system_table.hdr.crc32 = 0;
394 efiemu_system_table.hdr.crc32 = efiemu_getcrc32
395 (0, &efiemu_system_table, sizeof (efiemu_system_table));
397 efiemu_runtime_services.hdr.crc32 = 0;
398 efiemu_runtime_services.hdr.crc32 = efiemu_getcrc32
399 (0, &efiemu_runtime_services, sizeof (efiemu_runtime_services));
401 return GRUB_EFI_SUCCESS;
404 /* since efiemu_set_virtual_address_map corrects all the pointers
405 we don't need efiemu_convert_pointer */
406 grub_efi_status_t
407 EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
408 void **address)
410 LOG ('f');
411 return GRUB_EFI_UNSUPPORTED;
414 /* Next comes variable services. Because we have no vendor-independent
415 way to store these variables we have no non-volatility */
417 /* Find variable by name and GUID. */
418 static struct efi_variable *
419 find_variable (const grub_efi_guid_t *vendor_guid,
420 grub_efi_char16_t *variable_name)
422 grub_uint8_t *ptr;
423 struct efi_variable *efivar;
425 for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
427 efivar = (struct efi_variable *) ptr;
428 if (!efivar->namelen)
429 return 0;
430 if (efiemu_str16equal((grub_efi_char16_t *)(efivar + 1), variable_name)
431 && efiemu_memequal (&(efivar->guid), vendor_guid,
432 sizeof (efivar->guid)))
433 return efivar;
434 ptr += efivar->namelen + efivar->size + sizeof (*efivar);
436 return 0;
439 grub_efi_status_t
440 EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name,
441 const grub_efi_guid_t *vendor_guid,
442 grub_efi_uint32_t *attributes,
443 grub_efi_uintn_t *data_size,
444 void *data)
446 struct efi_variable *efivar;
447 LOG ('g');
448 efivar = find_variable (vendor_guid, variable_name);
449 if (!efivar)
450 return GRUB_EFI_NOT_FOUND;
451 if (*data_size < efivar->size)
453 *data_size = efivar->size;
454 return GRUB_EFI_BUFFER_TOO_SMALL;
456 *data_size = efivar->size;
457 efiemu_memcpy (data, (grub_uint8_t *)(efivar + 1) + efivar->namelen,
458 efivar->size);
459 *attributes = efivar->attributes;
461 return GRUB_EFI_SUCCESS;
464 grub_efi_status_t EFI_FUNC
465 (efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size,
466 grub_efi_char16_t *variable_name,
467 grub_efi_guid_t *vendor_guid)
469 struct efi_variable *efivar;
470 LOG ('l');
472 if (!variable_name_size || !variable_name || !vendor_guid)
473 return GRUB_EFI_INVALID_PARAMETER;
474 if (variable_name[0])
476 efivar = find_variable (vendor_guid, variable_name);
477 if (!efivar)
478 return GRUB_EFI_NOT_FOUND;
479 efivar = (struct efi_variable *)((grub_uint8_t *)efivar
480 + efivar->namelen
481 + efivar->size + sizeof (*efivar));
483 else
484 efivar = (struct efi_variable *) (efiemu_variables);
486 LOG ('m');
487 if ((grub_uint8_t *)efivar >= efiemu_variables + efiemu_varsize
488 || !efivar->namelen)
489 return GRUB_EFI_NOT_FOUND;
490 if (*variable_name_size < efivar->namelen)
492 *variable_name_size = efivar->namelen;
493 return GRUB_EFI_BUFFER_TOO_SMALL;
496 efiemu_memcpy (variable_name, efivar + 1, efivar->namelen);
497 efiemu_memcpy (vendor_guid, &(efivar->guid),
498 sizeof (efivar->guid));
500 LOG('h');
501 return GRUB_EFI_SUCCESS;
504 grub_efi_status_t
505 EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name,
506 const grub_efi_guid_t *vendor_guid,
507 grub_efi_uint32_t attributes,
508 grub_efi_uintn_t data_size,
509 void *data)
511 struct efi_variable *efivar;
512 grub_uint8_t *ptr;
513 LOG('i');
514 if (!variable_name[0])
515 return GRUB_EFI_INVALID_PARAMETER;
516 efivar = find_variable (vendor_guid, variable_name);
518 /* Delete variable if any */
519 if (efivar)
521 efiemu_memcpy (efivar, (grub_uint8_t *)(efivar + 1)
522 + efivar->namelen + efivar->size,
523 (efiemu_variables + efiemu_varsize)
524 - ((grub_uint8_t *)(efivar + 1)
525 + efivar->namelen + efivar->size));
526 efiemu_memset (efiemu_variables + efiemu_varsize
527 - (sizeof (*efivar) + efivar->namelen + efivar->size),
528 0, (sizeof (*efivar) + efivar->namelen + efivar->size));
531 if (!data_size)
532 return GRUB_EFI_SUCCESS;
534 for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
536 efivar = (struct efi_variable *) ptr;
537 ptr += efivar->namelen + efivar->size + sizeof (*efivar);
538 if (!efivar->namelen)
539 break;
541 if ((grub_uint8_t *)(efivar + 1) + data_size
542 + 2 * (efiemu_str16len (variable_name) + 1)
543 >= efiemu_variables + efiemu_varsize)
544 return GRUB_EFI_OUT_OF_RESOURCES;
546 efiemu_memcpy (&(efivar->guid), vendor_guid, sizeof (efivar->guid));
547 efivar->namelen = 2 * (efiemu_str16len (variable_name) + 1);
548 efivar->size = data_size;
549 efivar->attributes = attributes;
550 efiemu_memcpy (efivar + 1, variable_name,
551 2 * (efiemu_str16len (variable_name) + 1));
552 efiemu_memcpy ((grub_uint8_t *)(efivar + 1)
553 + 2 * (efiemu_str16len (variable_name) + 1),
554 data, data_size);
556 return GRUB_EFI_SUCCESS;
559 grub_efi_status_t EFI_FUNC
560 (efiemu_get_next_high_monotonic_count) (grub_efi_uint32_t *high_count)
562 LOG ('j');
563 if (!high_count)
564 return GRUB_EFI_INVALID_PARAMETER;
565 *high_count = ++efiemu_high_monotonic_count;
566 return GRUB_EFI_SUCCESS;
569 /* To implement it with APM we need to go to real mode. It's too much hassle
570 Besides EFI specification says that this function shouldn't be used
571 on systems supporting ACPI
573 void
574 EFI_FUNC (efiemu_reset_system) (grub_efi_reset_type_t reset_type,
575 grub_efi_status_t reset_status,
576 grub_efi_uintn_t data_size,
577 grub_efi_char16_t *reset_data)
579 LOG ('k');
582 struct grub_efi_runtime_services efiemu_runtime_services =
584 .hdr =
586 .signature = GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE,
587 .revision = 0x0001000a,
588 .header_size = sizeof (struct grub_efi_runtime_services),
589 .crc32 = 0, /* filled later*/
590 .reserved = 0
592 .get_time = efiemu_get_time,
593 .set_time = efiemu_set_time,
594 .get_wakeup_time = efiemu_get_wakeup_time,
595 .set_wakeup_time = efiemu_set_wakeup_time,
597 .set_virtual_address_map = efiemu_set_virtual_address_map,
598 .convert_pointer = efiemu_convert_pointer,
600 .get_variable = efiemu_get_variable,
601 .get_next_variable_name = efiemu_get_next_variable_name,
602 .set_variable = efiemu_set_variable,
603 .get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count,
605 .reset_system = efiemu_reset_system
609 static grub_uint16_t efiemu_vendor[] =
610 {'G', 'R', 'U', 'B', ' ', 'E', 'F', 'I', ' ',
611 'R', 'U', 'N', 'T', 'I', 'M', 'E', 0};
613 struct grub_efi_system_table efiemu_system_table =
615 .hdr =
617 .signature = GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE,
618 .revision = 0x0001000a,
619 .header_size = sizeof (struct grub_efi_system_table),
620 .crc32 = 0, /* filled later*/
621 .reserved = 0
623 .firmware_vendor = efiemu_vendor,
624 .firmware_revision = 0x0001000a,
625 .console_in_handler = 0,
626 .con_in = 0,
627 .console_out_handler = 0,
628 .con_out = 0,
629 .standard_error_handle = 0,
630 .std_err = 0,
631 .runtime_services = &efiemu_runtime_services,
632 .boot_services = 0,
633 .num_table_entries = 0,
634 .configuration_table = 0