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>
30 efiemu_get_time (grub_efi_time_t
*time
,
31 grub_efi_time_capabilities_t
*capabilities
);
33 efiemu_set_time (grub_efi_time_t
*time
);
36 efiemu_get_wakeup_time (grub_efi_boolean_t
*enabled
,
37 grub_efi_boolean_t
*pending
,
38 grub_efi_time_t
*time
);
40 efiemu_set_wakeup_time (grub_efi_boolean_t enabled
,
41 grub_efi_time_t
*time
);
44 #define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical")));
46 #define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical")));
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
)
57 efiemu_convert_pointer (grub_efi_uintn_t debug_disposition
,
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
,
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
);
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
,
80 efiemu_get_next_high_monotonic_count (grub_efi_uint32_t
*high_count
);
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
);
88 EFI_FUNC (efiemu_set_virtual_address_map
) (grub_efi_uintn_t
,
91 grub_efi_memory_descriptor_t
*)
94 EFI_FUNC (efiemu_convert_pointer
) (grub_efi_uintn_t debug_disposition
,
98 efiemu_getcrc32 (grub_uint32_t crc
, void *buf
, int size
)
101 init_crc32_table (void)
104 reflect (grub_uint32_t ref
, int len
)
108 The log. It's used when examining memory dump
110 static grub_uint8_t loge
[1000] = "EFIEMULOG";
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 */
129 efiemu_memcpy (void *to
, void *from
, int count
)
132 for (i
= 0; i
< count
; i
++)
133 ((grub_uint8_t
*) to
)[i
] = ((grub_uint8_t
*) from
)[i
];
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
;
145 efiemu_str16len (grub_uint16_t
*a
)
148 for (ptr1
= a
; *ptr1
; ptr1
++);
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
;
162 efiemu_memset (grub_uint8_t
*a
, grub_uint8_t b
, grub_size_t n
)
165 for (ptr1
=a
; ptr1
< a
+ n
; ptr1
++)
170 write_cmos (grub_uint8_t addr
, grub_uint8_t val
)
172 __asm__
__volatile__ ("outb %%al,$0x70\n"
174 "outb %%al,$0x71": :"a" (addr
), "c" (val
));
177 static inline grub_uint8_t
178 read_cmos (grub_uint8_t addr
)
181 __asm__
__volatile__ ("outb %%al, $0x70\n"
182 "inb $0x71, %%al": "=a"(ret
) :"a" (addr
));
186 /* Needed by some gcc versions */
187 int __stack_chk_fail ()
192 /* The function that implement runtime services as specified in
194 static inline grub_uint8_t
195 bcd_to_hex (grub_uint8_t in
)
197 return 10 * ((in
& 0xf0) >> 4) + (in
& 0x0f);
201 EFI_FUNC (efiemu_get_time
) (grub_efi_time_t
*time
,
202 grub_efi_time_capabilities_t
*capabilities
)
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)
217 time
->minute
= bcd_to_hex (read_cmos (0x2));
218 time
->second
= bcd_to_hex (read_cmos (0x0));
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)
230 time
->minute
= read_cmos (0x2);
231 time
->second
= read_cmos (0x0);
233 time
->nanosecond
= 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
;
245 EFI_FUNC (efiemu_set_time
) (grub_efi_time_t
*time
)
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 */
264 EFI_FUNC (efiemu_get_wakeup_time
) (grub_efi_boolean_t
*enabled
,
265 grub_efi_boolean_t
*pending
,
266 grub_efi_time_t
*time
)
269 return GRUB_EFI_UNSUPPORTED
;
273 EFI_FUNC (efiemu_set_wakeup_time
) (grub_efi_boolean_t enabled
,
274 grub_efi_time_t
*time
)
277 return GRUB_EFI_UNSUPPORTED
;
280 static grub_uint32_t crc32_table
[256];
283 reflect (grub_uint32_t ref
, int len
)
285 grub_uint32_t result
= 0;
288 for (i
= 1; i
<= len
; i
++)
291 result
|= 1 << (len
- i
);
299 init_crc32_table (void)
301 grub_uint32_t polynomial
= 0x04c11db7;
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);
315 efiemu_getcrc32 (grub_uint32_t crc
, void *buf
, int size
)
318 grub_uint8_t
*data
= buf
;
320 if (! crc32_table
[1])
325 for (i
= 0; i
< size
; i
++)
327 crc
= (crc
>> 8) ^ crc32_table
[(crc
& 0xFF) ^ *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
;
345 /* Ensure that we are called only once */
347 return GRUB_EFI_UNSUPPORTED
;
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
)
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
)
373 *((grub_uint64_t
*) UINT_TO_PTR (cur_relloc
->addr
)) += corr
;
376 *((grub_uint32_t
*) UINT_TO_PTR (cur_relloc
->addr
)) += corr
;
379 *((grub_uint16_t
*) UINT_TO_PTR (cur_relloc
->addr
)) += corr
;
382 *((grub_uint8_t
*) UINT_TO_PTR (cur_relloc
->addr
)) += corr
;
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 */
402 EFI_FUNC (efiemu_convert_pointer
) (grub_efi_uintn_t debug_disposition
,
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
)
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
)
425 if (efiemu_str16equal((grub_efi_char16_t
*)(efivar
+ 1), variable_name
)
426 && efiemu_memequal (&(efivar
->guid
), vendor_guid
,
427 sizeof (efivar
->guid
)))
429 ptr
+= efivar
->namelen
+ efivar
->size
+ sizeof (*efivar
);
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
,
441 struct efi_variable
*efivar
;
443 efivar
= find_variable (vendor_guid
, variable_name
);
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
,
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
;
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
);
473 return GRUB_EFI_NOT_FOUND
;
474 efivar
= (struct efi_variable
*)((grub_uint8_t
*)efivar
476 + efivar
->size
+ sizeof (*efivar
));
479 efivar
= (struct efi_variable
*) (efiemu_variables
);
482 if ((grub_uint8_t
*)efivar
>= efiemu_variables
+ efiemu_varsize
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
));
496 return GRUB_EFI_SUCCESS
;
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
,
506 struct efi_variable
*efivar
;
509 if (!variable_name
[0])
510 return GRUB_EFI_INVALID_PARAMETER
;
511 efivar
= find_variable (vendor_guid
, variable_name
);
513 /* Delete variable if any */
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
));
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
)
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),
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
)
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
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
)
577 struct grub_efi_runtime_services efiemu_runtime_services
=
581 .signature
= GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE
,
582 .revision
= 0x0001000a,
583 .header_size
= sizeof (struct grub_efi_runtime_services
),
584 .crc32
= 0, /* filled later*/
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
=
612 .signature
= GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE
,
613 .revision
= 0x0001000a,
614 .header_size
= sizeof (struct grub_efi_system_table
),
615 .crc32
= 0, /* filled later*/
618 .firmware_vendor
= efiemu_vendor
,
619 .firmware_revision
= 0x0001000a,
620 .console_in_handler
= 0,
622 .console_out_handler
= 0,
624 .standard_error_handle
= 0,
626 .runtime_services
= &efiemu_runtime_services
,
628 .num_table_entries
= 0,
629 .configuration_table
= 0