Merge branch 'makefile' into haiku
[grub2/phcoder.git] / efiemu / runtime / efiemu.c
blob085e75d0cd04d49c624bdba51655cf0302828e5b
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 #include <grub/symbol.h>
25 #include <grub/types.h>
26 #include <grub/efi/api.h>
27 #include <grub/efiemu/runtime.h>
29 grub_efi_status_t
30 efiemu_get_time (grub_efi_time_t *time,
31 grub_efi_time_capabilities_t *capabilities);
32 grub_efi_status_t
33 efiemu_set_time (grub_efi_time_t *time);
35 grub_efi_status_t
36 efiemu_get_wakeup_time (grub_efi_boolean_t *enabled,
37 grub_efi_boolean_t *pending,
38 grub_efi_time_t *time);
39 grub_efi_status_t
40 efiemu_set_wakeup_time (grub_efi_boolean_t enabled,
41 grub_efi_time_t *time);
43 #ifdef APPLE_CC
44 #define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical")));
45 #else
46 #define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical")));
47 #endif
49 grub_efi_status_t
50 efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
51 grub_efi_uintn_t descriptor_size,
52 grub_efi_uint32_t descriptor_version,
53 grub_efi_memory_descriptor_t *virtual_map)
54 PHYSICAL_ATTRIBUTE;
56 grub_efi_status_t
57 efiemu_convert_pointer (grub_efi_uintn_t debug_disposition,
58 void **address)
59 PHYSICAL_ATTRIBUTE;
61 grub_efi_status_t
62 efiemu_get_variable (grub_efi_char16_t *variable_name,
63 grub_efi_guid_t *vendor_guid,
64 grub_efi_uint32_t *attributes,
65 grub_efi_uintn_t *data_size,
66 void *data);
68 grub_efi_status_t
69 efiemu_get_next_variable_name (grub_efi_uintn_t *variable_name_size,
70 grub_efi_char16_t *variable_name,
71 grub_efi_guid_t *vendor_guid);
73 grub_efi_status_t
74 efiemu_set_variable (grub_efi_char16_t *variable_name,
75 grub_efi_guid_t *vendor_guid,
76 grub_efi_uint32_t attributes,
77 grub_efi_uintn_t data_size,
78 void *data);
79 grub_efi_status_t
80 efiemu_get_next_high_monotonic_count (grub_efi_uint32_t *high_count);
81 void
82 efiemu_reset_system (grub_efi_reset_type_t reset_type,
83 grub_efi_status_t reset_status,
84 grub_efi_uintn_t data_size,
85 grub_efi_char16_t *reset_data);
87 grub_efi_status_t
88 EFI_FUNC (efiemu_set_virtual_address_map) (grub_efi_uintn_t,
89 grub_efi_uintn_t,
90 grub_efi_uint32_t,
91 grub_efi_memory_descriptor_t *)
92 PHYSICAL_ATTRIBUTE;
93 grub_efi_status_t
94 EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
95 void **address)
96 PHYSICAL_ATTRIBUTE;
97 static grub_uint32_t
98 efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
99 PHYSICAL_ATTRIBUTE;
100 static void
101 init_crc32_table (void)
102 PHYSICAL_ATTRIBUTE;
103 static grub_uint32_t
104 reflect (grub_uint32_t ref, int len)
105 PHYSICAL_ATTRIBUTE;
108 The log. It's used when examining memory dump
110 static grub_uint8_t loge[1000] = "EFIEMULOG";
111 static int logn = 9;
112 #define LOG(x) { if (logn<900) loge[logn++]=x; }
114 static int ptv_relocated = 0;
116 /* Interface with grub */
117 struct grub_efi_runtime_services efiemu_runtime_services;
118 struct grub_efi_system_table efiemu_system_table;
119 extern struct grub_efiemu_ptv_rel efiemu_ptv_relloc[];
120 extern grub_uint8_t efiemu_variables[];
121 extern grub_uint32_t efiemu_varsize;
122 extern grub_uint32_t efiemu_high_monotonic_count;
123 extern grub_int16_t efiemu_time_zone;
124 extern grub_uint8_t efiemu_time_daylight;
125 extern grub_uint32_t efiemu_time_accuracy;
127 /* Some standard functions because we need to be standalone */
128 static void
129 efiemu_memcpy (void *to, void *from, int count)
131 int i;
132 for (i = 0; i < count; i++)
133 ((grub_uint8_t *) to)[i] = ((grub_uint8_t *) from)[i];
136 static int
137 efiemu_str16equal (grub_uint16_t *a, grub_uint16_t *b)
139 grub_uint16_t *ptr1, *ptr2;
140 for (ptr1=a,ptr2=b; *ptr1 && *ptr2 == *ptr1; ptr1++, ptr2++);
141 return *ptr2 == *ptr1;
144 static grub_size_t
145 efiemu_str16len (grub_uint16_t *a)
147 grub_uint16_t *ptr1;
148 for (ptr1 = a; *ptr1; ptr1++);
149 return ptr1 - a;
152 static int
153 efiemu_memequal (void *a, void *b, grub_size_t n)
155 grub_uint8_t *ptr1, *ptr2;
156 for (ptr1 = (grub_uint8_t *) a, ptr2 = (grub_uint8_t *)b;
157 ptr1 < (grub_uint8_t *)a + n && *ptr2 == *ptr1; ptr1++, ptr2++);
158 return ptr1 == a + n;
161 static void
162 efiemu_memset (grub_uint8_t *a, grub_uint8_t b, grub_size_t n)
164 grub_uint8_t *ptr1;
165 for (ptr1=a; ptr1 < a + n; ptr1++)
166 *ptr1 = b;
169 static inline void
170 write_cmos (grub_uint8_t addr, grub_uint8_t val)
172 __asm__ __volatile__ ("outb %%al,$0x70\n"
173 "mov %%cl, %%al\n"
174 "outb %%al,$0x71": :"a" (addr), "c" (val));
177 static inline grub_uint8_t
178 read_cmos (grub_uint8_t addr)
180 grub_uint8_t ret;
181 __asm__ __volatile__ ("outb %%al, $0x70\n"
182 "inb $0x71, %%al": "=a"(ret) :"a" (addr));
183 return ret;
186 /* Needed by some gcc versions */
187 int __stack_chk_fail ()
189 return 0;
192 /* The function that implement runtime services as specified in
193 EFI specification */
194 static inline grub_uint8_t
195 bcd_to_hex (grub_uint8_t in)
197 return 10 * ((in & 0xf0) >> 4) + (in & 0x0f);
200 grub_efi_status_t
201 EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time,
202 grub_efi_time_capabilities_t *capabilities)
204 LOG ('a');
205 grub_uint8_t state;
206 state = read_cmos (0xb);
207 if (!(state & (1 << 2)))
209 time->year = 2000 + bcd_to_hex (read_cmos (0x9));
210 time->month = bcd_to_hex (read_cmos (0x8));
211 time->day = bcd_to_hex (read_cmos (0x7));
212 time->hour = bcd_to_hex (read_cmos (0x4));
213 if (time->hour >= 81)
214 time->hour -= 80 - 12;
215 if (time->hour == 24)
216 time->hour = 0;
217 time->minute = bcd_to_hex (read_cmos (0x2));
218 time->second = bcd_to_hex (read_cmos (0x0));
220 else
222 time->year = 2000 + read_cmos (0x9);
223 time->month = read_cmos (0x8);
224 time->day = read_cmos (0x7);
225 time->hour = read_cmos (0x4);
226 if (time->hour >= 0x81)
227 time->hour -= 0x80 - 12;
228 if (time->hour == 24)
229 time->hour = 0;
230 time->minute = read_cmos (0x2);
231 time->second = read_cmos (0x0);
233 time->nanosecond = 0;
234 time->pad1 = 0;
235 time->pad2 = 0;
236 time->time_zone = efiemu_time_zone;
237 time->daylight = efiemu_time_daylight;
238 capabilities->resolution = 1;
239 capabilities->accuracy = efiemu_time_accuracy;
240 capabilities->sets_to_zero = 0;
241 return GRUB_EFI_SUCCESS;
244 grub_efi_status_t
245 EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time)
247 LOG ('b');
248 grub_uint8_t state;
249 state = read_cmos (0xb);
250 write_cmos (0xb, state | 0x6);
251 write_cmos (0x9, time->year - 2000);
252 write_cmos (0x8, time->month);
253 write_cmos (0x7, time->day);
254 write_cmos (0x4, time->hour);
255 write_cmos (0x2, time->minute);
256 write_cmos (0x0, time->second);
257 efiemu_time_zone = time->time_zone;
258 efiemu_time_daylight = time->daylight;
259 return GRUB_EFI_SUCCESS;
262 /* Following 2 functions are vendor specific. So announce it as unsupported */
263 grub_efi_status_t
264 EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled,
265 grub_efi_boolean_t *pending,
266 grub_efi_time_t *time)
268 LOG ('c');
269 return GRUB_EFI_UNSUPPORTED;
272 grub_efi_status_t
273 EFI_FUNC (efiemu_set_wakeup_time) (grub_efi_boolean_t enabled,
274 grub_efi_time_t *time)
276 LOG ('d');
277 return GRUB_EFI_UNSUPPORTED;
280 static grub_uint32_t crc32_table [256];
282 static grub_uint32_t
283 reflect (grub_uint32_t ref, int len)
285 grub_uint32_t result = 0;
286 int i;
288 for (i = 1; i <= len; i++)
290 if (ref & 1)
291 result |= 1 << (len - i);
292 ref >>= 1;
295 return result;
298 static void
299 init_crc32_table (void)
301 grub_uint32_t polynomial = 0x04c11db7;
302 int i, j;
304 for(i = 0; i < 256; i++)
306 crc32_table[i] = reflect(i, 8) << 24;
307 for (j = 0; j < 8; j++)
308 crc32_table[i] = (crc32_table[i] << 1) ^
309 (crc32_table[i] & (1 << 31) ? polynomial : 0);
310 crc32_table[i] = reflect(crc32_table[i], 32);
314 static grub_uint32_t
315 efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
317 int i;
318 grub_uint8_t *data = buf;
320 if (! crc32_table[1])
321 init_crc32_table ();
323 crc^= 0xffffffff;
325 for (i = 0; i < size; i++)
327 crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data];
328 data++;
331 return crc ^ 0xffffffff;
335 grub_efi_status_t EFI_FUNC
336 (efiemu_set_virtual_address_map) (grub_efi_uintn_t memory_map_size,
337 grub_efi_uintn_t descriptor_size,
338 grub_efi_uint32_t descriptor_version,
339 grub_efi_memory_descriptor_t *virtual_map)
341 struct grub_efiemu_ptv_rel *cur_relloc;
343 LOG ('e');
345 /* Ensure that we are called only once */
346 if (ptv_relocated)
347 return GRUB_EFI_UNSUPPORTED;
348 ptv_relocated = 1;
350 /* Correct addresses using information supplied by grub */
351 for (cur_relloc = efiemu_ptv_relloc; cur_relloc->size;cur_relloc++)
353 grub_int64_t corr = 0;
354 grub_efi_memory_descriptor_t *descptr;
356 /* Compute correction */
357 for (descptr = virtual_map;
358 ((grub_uint8_t *) descptr - (grub_uint8_t *) virtual_map)
359 < memory_map_size;
360 descptr = (grub_efi_memory_descriptor_t *)
361 ((grub_uint8_t *) descptr + descriptor_size))
363 if (descptr->type == cur_relloc->plustype)
364 corr += descptr->virtual_start - descptr->physical_start;
365 if (descptr->type == cur_relloc->minustype)
366 corr -= descptr->virtual_start - descptr->physical_start;
369 /* Apply correction */
370 switch (cur_relloc->size)
372 case 8:
373 *((grub_uint64_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
374 break;
375 case 4:
376 *((grub_uint32_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
377 break;
378 case 2:
379 *((grub_uint16_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
380 break;
381 case 1:
382 *((grub_uint8_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
383 break;
387 /* Recompute crc32 of system table and runtime services */
388 efiemu_system_table.hdr.crc32 = 0;
389 efiemu_system_table.hdr.crc32 = efiemu_getcrc32
390 (0, &efiemu_system_table, sizeof (efiemu_system_table));
392 efiemu_runtime_services.hdr.crc32 = 0;
393 efiemu_runtime_services.hdr.crc32 = efiemu_getcrc32
394 (0, &efiemu_runtime_services, sizeof (efiemu_runtime_services));
396 return GRUB_EFI_SUCCESS;
399 /* since efiemu_set_virtual_address_map corrects all the pointers
400 we don't need efiemu_convert_pointer */
401 grub_efi_status_t
402 EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
403 void **address)
405 LOG ('f');
406 return GRUB_EFI_UNSUPPORTED;
409 /* Next comes variable services. Because we have no vendor-independent
410 way to store these variables we have no non-volatility */
412 /* Find variable by name and GUID. */
413 static struct efi_variable *
414 find_variable (grub_efi_guid_t *vendor_guid,
415 grub_efi_char16_t *variable_name)
417 grub_uint8_t *ptr;
418 struct efi_variable *efivar;
420 for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
422 efivar = (struct efi_variable *) ptr;
423 if (!efivar->namelen)
424 return 0;
425 if (efiemu_str16equal((grub_efi_char16_t *)(efivar + 1), variable_name)
426 && efiemu_memequal (&(efivar->guid), vendor_guid,
427 sizeof (efivar->guid)))
428 return efivar;
429 ptr += efivar->namelen + efivar->size + sizeof (*efivar);
431 return 0;
434 grub_efi_status_t
435 EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name,
436 grub_efi_guid_t *vendor_guid,
437 grub_efi_uint32_t *attributes,
438 grub_efi_uintn_t *data_size,
439 void *data)
441 struct efi_variable *efivar;
442 LOG ('g');
443 efivar = find_variable (vendor_guid, variable_name);
444 if (!efivar)
445 return GRUB_EFI_NOT_FOUND;
446 if (*data_size < efivar->size)
448 *data_size = efivar->size;
449 return GRUB_EFI_BUFFER_TOO_SMALL;
451 *data_size = efivar->size;
452 efiemu_memcpy (data, (grub_uint8_t *)(efivar + 1) + efivar->namelen,
453 efivar->size);
454 *attributes = efivar->attributes;
456 return GRUB_EFI_SUCCESS;
459 grub_efi_status_t EFI_FUNC
460 (efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size,
461 grub_efi_char16_t *variable_name,
462 grub_efi_guid_t *vendor_guid)
464 struct efi_variable *efivar;
465 LOG ('l');
467 if (!variable_name_size || !variable_name || !vendor_guid)
468 return GRUB_EFI_INVALID_PARAMETER;
469 if (variable_name[0])
471 efivar = find_variable (vendor_guid, variable_name);
472 if (!efivar)
473 return GRUB_EFI_NOT_FOUND;
474 efivar = (struct efi_variable *)((grub_uint8_t *)efivar
475 + efivar->namelen
476 + efivar->size + sizeof (*efivar));
478 else
479 efivar = (struct efi_variable *) (efiemu_variables);
481 LOG ('m');
482 if ((grub_uint8_t *)efivar >= efiemu_variables + efiemu_varsize
483 || !efivar->namelen)
484 return GRUB_EFI_NOT_FOUND;
485 if (*variable_name_size < efivar->namelen)
487 *variable_name_size = efivar->namelen;
488 return GRUB_EFI_BUFFER_TOO_SMALL;
491 efiemu_memcpy (variable_name, efivar + 1, efivar->namelen);
492 efiemu_memcpy (vendor_guid, &(efivar->guid),
493 sizeof (efivar->guid));
495 LOG('h');
496 return GRUB_EFI_SUCCESS;
499 grub_efi_status_t
500 EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name,
501 grub_efi_guid_t *vendor_guid,
502 grub_efi_uint32_t attributes,
503 grub_efi_uintn_t data_size,
504 void *data)
506 struct efi_variable *efivar;
507 grub_uint8_t *ptr;
508 LOG('i');
509 if (!variable_name[0])
510 return GRUB_EFI_INVALID_PARAMETER;
511 efivar = find_variable (vendor_guid, variable_name);
513 /* Delete variable if any */
514 if (efivar)
516 efiemu_memcpy (efivar, (grub_uint8_t *)(efivar + 1)
517 + efivar->namelen + efivar->size,
518 (efiemu_variables + efiemu_varsize)
519 - ((grub_uint8_t *)(efivar + 1)
520 + efivar->namelen + efivar->size));
521 efiemu_memset (efiemu_variables + efiemu_varsize
522 - (sizeof (*efivar) + efivar->namelen + efivar->size),
523 0, (sizeof (*efivar) + efivar->namelen + efivar->size));
526 if (!data_size)
527 return GRUB_EFI_SUCCESS;
529 for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
531 efivar = (struct efi_variable *) ptr;
532 ptr += efivar->namelen + efivar->size + sizeof (*efivar);
533 if (!efivar->namelen)
534 break;
536 if ((grub_uint8_t *)(efivar + 1) + data_size
537 + 2 * (efiemu_str16len (variable_name) + 1)
538 >= efiemu_variables + efiemu_varsize)
539 return GRUB_EFI_OUT_OF_RESOURCES;
541 efiemu_memcpy (&(efivar->guid), vendor_guid, sizeof (efivar->guid));
542 efivar->namelen = 2 * (efiemu_str16len (variable_name) + 1);
543 efivar->size = data_size;
544 efivar->attributes = attributes;
545 efiemu_memcpy (efivar + 1, variable_name,
546 2 * (efiemu_str16len (variable_name) + 1));
547 efiemu_memcpy ((grub_uint8_t *)(efivar + 1)
548 + 2 * (efiemu_str16len (variable_name) + 1),
549 data, data_size);
551 return GRUB_EFI_SUCCESS;
554 grub_efi_status_t EFI_FUNC
555 (efiemu_get_next_high_monotonic_count) (grub_efi_uint32_t *high_count)
557 LOG ('j');
558 if (!high_count)
559 return GRUB_EFI_INVALID_PARAMETER;
560 *high_count = ++efiemu_high_monotonic_count;
561 return GRUB_EFI_SUCCESS;
564 /* To implement it with APM we need to go to real mode. It's too much hassle
565 Besides EFI specification says that this function shouldn't be used
566 on systems supporting ACPI
568 void
569 EFI_FUNC (efiemu_reset_system) (grub_efi_reset_type_t reset_type,
570 grub_efi_status_t reset_status,
571 grub_efi_uintn_t data_size,
572 grub_efi_char16_t *reset_data)
574 LOG ('k');
577 struct grub_efi_runtime_services efiemu_runtime_services =
579 .hdr =
581 .signature = GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE,
582 .revision = 0x0001000a,
583 .header_size = sizeof (struct grub_efi_runtime_services),
584 .crc32 = 0, /* filled later*/
585 .reserved = 0
587 .get_time = efiemu_get_time,
588 .set_time = efiemu_set_time,
589 .get_wakeup_time = efiemu_get_wakeup_time,
590 .set_wakeup_time = efiemu_set_wakeup_time,
592 .set_virtual_address_map = efiemu_set_virtual_address_map,
593 .convert_pointer = efiemu_convert_pointer,
595 .get_variable = efiemu_get_variable,
596 .get_next_variable_name = efiemu_get_next_variable_name,
597 .set_variable = efiemu_set_variable,
598 .get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count,
600 .reset_system = efiemu_reset_system
604 static grub_uint16_t efiemu_vendor[] =
605 {'G', 'R', 'U', 'B', ' ', 'E', 'F', 'I', ' ',
606 'R', 'U', 'N', 'T', 'I', 'M', 'E', 0};
608 struct grub_efi_system_table efiemu_system_table =
610 .hdr =
612 .signature = GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE,
613 .revision = 0x0001000a,
614 .header_size = sizeof (struct grub_efi_system_table),
615 .crc32 = 0, /* filled later*/
616 .reserved = 0
618 .firmware_vendor = efiemu_vendor,
619 .firmware_revision = 0x0001000a,
620 .console_in_handler = 0,
621 .con_in = 0,
622 .console_out_handler = 0,
623 .con_out = 0,
624 .standard_error_handle = 0,
625 .std_err = 0,
626 .runtime_services = &efiemu_runtime_services,
627 .boot_services = 0,
628 .num_table_entries = 0,
629 .configuration_table = 0