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) */
25 #include <grub/i386/types.h>
27 #include <grub/x86_64/types.h>
30 #include <grub/symbol.h>
31 #include <grub/types.h>
32 #include <grub/efi/api.h>
33 #include <grub/efiemu/runtime.h>
36 efiemu_get_time (grub_efi_time_t
*time
,
37 grub_efi_time_capabilities_t
*capabilities
);
39 efiemu_set_time (grub_efi_time_t
*time
);
42 efiemu_get_wakeup_time (grub_efi_boolean_t
*enabled
,
43 grub_efi_boolean_t
*pending
,
44 grub_efi_time_t
*time
);
46 efiemu_set_wakeup_time (grub_efi_boolean_t enabled
,
47 grub_efi_time_t
*time
);
50 #define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical")));
52 #define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical")));
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
)
63 efiemu_convert_pointer (grub_efi_uintn_t debug_disposition
,
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
,
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
);
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
,
86 efiemu_get_next_high_monotonic_count (grub_efi_uint32_t
*high_count
);
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
);
94 EFI_FUNC (efiemu_set_virtual_address_map
) (grub_efi_uintn_t
,
97 grub_efi_memory_descriptor_t
*)
100 EFI_FUNC (efiemu_convert_pointer
) (grub_efi_uintn_t debug_disposition
,
104 efiemu_getcrc32 (grub_uint32_t crc
, void *buf
, int size
)
107 init_crc32_table (void)
110 reflect (grub_uint32_t ref
, int len
)
114 The log. It's used when examining memory dump
116 static grub_uint8_t loge
[1000] = "EFIEMULOG";
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 */
134 efiemu_memcpy (void *to
, const void *from
, int count
)
137 for (i
= 0; i
< count
; i
++)
138 ((grub_uint8_t
*) to
)[i
] = ((const grub_uint8_t
*) from
)[i
];
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
;
150 efiemu_str16len (grub_uint16_t
*a
)
153 for (ptr1
= a
; *ptr1
; ptr1
++);
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
;
167 efiemu_memset (grub_uint8_t
*a
, grub_uint8_t b
, grub_size_t n
)
170 for (ptr1
=a
; ptr1
< a
+ n
; ptr1
++)
175 write_cmos (grub_uint8_t addr
, grub_uint8_t val
)
177 __asm__
__volatile__ ("outb %%al,$0x70\n"
179 "outb %%al,$0x71": :"a" (addr
), "c" (val
));
182 static inline grub_uint8_t
183 read_cmos (grub_uint8_t addr
)
186 __asm__
__volatile__ ("outb %%al, $0x70\n"
187 "inb $0x71, %%al": "=a"(ret
) :"a" (addr
));
191 /* Needed by some gcc versions */
192 int __stack_chk_fail ()
197 /* The function that implement runtime services as specified in
199 static inline grub_uint8_t
200 bcd_to_hex (grub_uint8_t in
)
202 return 10 * ((in
& 0xf0) >> 4) + (in
& 0x0f);
206 EFI_FUNC (efiemu_get_time
) (grub_efi_time_t
*time
,
207 grub_efi_time_capabilities_t
*capabilities
)
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)
222 time
->minute
= bcd_to_hex (read_cmos (0x2));
223 time
->second
= bcd_to_hex (read_cmos (0x0));
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)
235 time
->minute
= read_cmos (0x2);
236 time
->second
= read_cmos (0x0);
238 time
->nanosecond
= 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
;
250 EFI_FUNC (efiemu_set_time
) (grub_efi_time_t
*time
)
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 */
269 EFI_FUNC (efiemu_get_wakeup_time
) (grub_efi_boolean_t
*enabled
,
270 grub_efi_boolean_t
*pending
,
271 grub_efi_time_t
*time
)
274 return GRUB_EFI_UNSUPPORTED
;
278 EFI_FUNC (efiemu_set_wakeup_time
) (grub_efi_boolean_t enabled
,
279 grub_efi_time_t
*time
)
282 return GRUB_EFI_UNSUPPORTED
;
285 static grub_uint32_t crc32_table
[256];
288 reflect (grub_uint32_t ref
, int len
)
290 grub_uint32_t result
= 0;
293 for (i
= 1; i
<= len
; i
++)
296 result
|= 1 << (len
- i
);
304 init_crc32_table (void)
306 grub_uint32_t polynomial
= 0x04c11db7;
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);
320 efiemu_getcrc32 (grub_uint32_t crc
, void *buf
, int size
)
323 grub_uint8_t
*data
= buf
;
325 if (! crc32_table
[1])
330 for (i
= 0; i
< size
; i
++)
332 crc
= (crc
>> 8) ^ crc32_table
[(crc
& 0xFF) ^ *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
;
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
)
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
)
378 *((grub_uint64_t
*) (grub_addr_t
) cur_relloc
->addr
) += corr
;
381 *((grub_uint32_t
*) (grub_addr_t
) cur_relloc
->addr
) += corr
;
384 *((grub_uint16_t
*) (grub_addr_t
) cur_relloc
->addr
) += corr
;
387 *((grub_uint8_t
*) (grub_addr_t
) cur_relloc
->addr
) += corr
;
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 */
407 EFI_FUNC (efiemu_convert_pointer
) (grub_efi_uintn_t debug_disposition
,
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
)
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
)
430 if (efiemu_str16equal((grub_efi_char16_t
*)(efivar
+ 1), variable_name
)
431 && efiemu_memequal (&(efivar
->guid
), vendor_guid
,
432 sizeof (efivar
->guid
)))
434 ptr
+= efivar
->namelen
+ efivar
->size
+ sizeof (*efivar
);
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
,
446 struct efi_variable
*efivar
;
448 efivar
= find_variable (vendor_guid
, variable_name
);
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
,
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
;
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
);
478 return GRUB_EFI_NOT_FOUND
;
479 efivar
= (struct efi_variable
*)((grub_uint8_t
*)efivar
481 + efivar
->size
+ sizeof (*efivar
));
484 efivar
= (struct efi_variable
*) (efiemu_variables
);
487 if ((grub_uint8_t
*)efivar
>= efiemu_variables
+ efiemu_varsize
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
));
501 return GRUB_EFI_SUCCESS
;
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
,
511 struct efi_variable
*efivar
;
514 if (!variable_name
[0])
515 return GRUB_EFI_INVALID_PARAMETER
;
516 efivar
= find_variable (vendor_guid
, variable_name
);
518 /* Delete variable if any */
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
));
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
)
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),
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
)
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
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
)
582 struct grub_efi_runtime_services efiemu_runtime_services
=
586 .signature
= GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE
,
587 .revision
= 0x0001000a,
588 .header_size
= sizeof (struct grub_efi_runtime_services
),
589 .crc32
= 0, /* filled later*/
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
=
617 .signature
= GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE
,
618 .revision
= 0x0001000a,
619 .header_size
= sizeof (struct grub_efi_system_table
),
620 .crc32
= 0, /* filled later*/
623 .firmware_vendor
= efiemu_vendor
,
624 .firmware_revision
= 0x0001000a,
625 .console_in_handler
= 0,
627 .console_out_handler
= 0,
629 .standard_error_handle
= 0,
631 .runtime_services
= &efiemu_runtime_services
,
633 .num_table_entries
= 0,
634 .configuration_table
= 0