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
/* Interface with grub */
115
extern grub_uint8_t efiemu_ptv_relocated;
116
struct grub_efi_runtime_services efiemu_runtime_services;
117
struct grub_efi_system_table efiemu_system_table;
118
extern struct grub_efiemu_ptv_rel efiemu_ptv_relloc[];
119
extern grub_uint8_t efiemu_variables[];
120
extern grub_uint32_t efiemu_varsize;
121
extern grub_uint32_t efiemu_high_monotonic_count;
122
extern grub_int16_t efiemu_time_zone;
123
extern grub_uint8_t efiemu_time_daylight;
124
extern grub_uint32_t efiemu_time_accuracy;
126
/* Some standard functions because we need to be standalone */
128
efiemu_memcpy (void *to, void *from, int count)
131
for (i = 0; i < count; i++)
132
((grub_uint8_t *) to)[i] = ((grub_uint8_t *) from)[i];
136
efiemu_str16equal (grub_uint16_t *a, grub_uint16_t *b)
138
grub_uint16_t *ptr1, *ptr2;
139
for (ptr1=a,ptr2=b; *ptr1 && *ptr2 == *ptr1; ptr1++, ptr2++);
140
return *ptr2 == *ptr1;
144
efiemu_str16len (grub_uint16_t *a)
147
for (ptr1 = a; *ptr1; ptr1++);
152
efiemu_memequal (void *a, void *b, grub_size_t n)
154
grub_uint8_t *ptr1, *ptr2;
155
for (ptr1 = (grub_uint8_t *) a, ptr2 = (grub_uint8_t *)b;
156
ptr1 < (grub_uint8_t *)a + n && *ptr2 == *ptr1; ptr1++, ptr2++);
157
return ptr1 == a + n;
161
efiemu_memset (grub_uint8_t *a, grub_uint8_t b, grub_size_t n)
164
for (ptr1=a; ptr1 < a + n; ptr1++)
169
write_cmos (grub_uint8_t addr, grub_uint8_t val)
171
__asm__ __volatile__ ("outb %%al,$0x70\n"
173
"outb %%al,$0x71": :"a" (addr), "c" (val));
176
static inline grub_uint8_t
177
read_cmos (grub_uint8_t addr)
180
__asm__ __volatile__ ("outb %%al, $0x70\n"
181
"inb $0x71, %%al": "=a"(ret) :"a" (addr));
185
/* Needed by some gcc versions */
186
int __stack_chk_fail ()
191
/* The function that implement runtime services as specified in
193
static inline grub_uint8_t
194
bcd_to_hex (grub_uint8_t in)
196
return 10 * ((in & 0xf0) >> 4) + (in & 0x0f);
200
EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time,
201
grub_efi_time_capabilities_t *capabilities)
205
state = read_cmos (0xb);
206
if (!(state & (1 << 2)))
208
time->year = 2000 + bcd_to_hex (read_cmos (0x9));
209
time->month = bcd_to_hex (read_cmos (0x8));
210
time->day = bcd_to_hex (read_cmos (0x7));
211
time->hour = bcd_to_hex (read_cmos (0x4));
212
if (time->hour >= 81)
213
time->hour -= 80 - 12;
214
if (time->hour == 24)
216
time->minute = bcd_to_hex (read_cmos (0x2));
217
time->second = bcd_to_hex (read_cmos (0x0));
221
time->year = 2000 + read_cmos (0x9);
222
time->month = read_cmos (0x8);
223
time->day = read_cmos (0x7);
224
time->hour = read_cmos (0x4);
225
if (time->hour >= 0x81)
226
time->hour -= 0x80 - 12;
227
if (time->hour == 24)
229
time->minute = read_cmos (0x2);
230
time->second = read_cmos (0x0);
232
time->nanosecond = 0;
235
time->time_zone = efiemu_time_zone;
236
time->daylight = efiemu_time_daylight;
237
capabilities->resolution = 1;
238
capabilities->accuracy = efiemu_time_accuracy;
239
capabilities->sets_to_zero = 0;
240
return GRUB_EFI_SUCCESS;
244
EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time)
248
state = read_cmos (0xb);
249
write_cmos (0xb, state | 0x6);
250
write_cmos (0x9, time->year - 2000);
251
write_cmos (0x8, time->month);
252
write_cmos (0x7, time->day);
253
write_cmos (0x4, time->hour);
254
write_cmos (0x2, time->minute);
255
write_cmos (0x0, time->second);
256
efiemu_time_zone = time->time_zone;
257
efiemu_time_daylight = time->daylight;
258
return GRUB_EFI_SUCCESS;
261
/* Following 2 functions are vendor specific. So announce it as unsupported */
263
EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled,
264
grub_efi_boolean_t *pending,
265
grub_efi_time_t *time)
268
return GRUB_EFI_UNSUPPORTED;
272
EFI_FUNC (efiemu_set_wakeup_time) (grub_efi_boolean_t enabled,
273
grub_efi_time_t *time)
276
return GRUB_EFI_UNSUPPORTED;
279
static grub_uint32_t crc32_table [256];
282
reflect (grub_uint32_t ref, int len)
284
grub_uint32_t result = 0;
287
for (i = 1; i <= len; i++)
290
result |= 1 << (len - i);
298
init_crc32_table (void)
300
grub_uint32_t polynomial = 0x04c11db7;
303
for(i = 0; i < 256; i++)
305
crc32_table[i] = reflect(i, 8) << 24;
306
for (j = 0; j < 8; j++)
307
crc32_table[i] = (crc32_table[i] << 1) ^
308
(crc32_table[i] & (1 << 31) ? polynomial : 0);
309
crc32_table[i] = reflect(crc32_table[i], 32);
314
efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
317
grub_uint8_t *data = buf;
319
if (! crc32_table[1])
324
for (i = 0; i < size; i++)
326
crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data];
330
return crc ^ 0xffffffff;
334
grub_efi_status_t EFI_FUNC
335
(efiemu_set_virtual_address_map) (grub_efi_uintn_t memory_map_size,
336
grub_efi_uintn_t descriptor_size,
337
grub_efi_uint32_t descriptor_version,
338
grub_efi_memory_descriptor_t *virtual_map)
340
struct grub_efiemu_ptv_rel *cur_relloc;
344
/* Ensure that we are called only once */
345
if (efiemu_ptv_relocated)
346
return GRUB_EFI_UNSUPPORTED;
347
efiemu_ptv_relocated = 1;
349
/* Correct addresses using information supplied by grub */
350
for (cur_relloc = efiemu_ptv_relloc; cur_relloc->size;cur_relloc++)
352
grub_int64_t corr = 0;
353
grub_efi_memory_descriptor_t *descptr;
355
/* Compute correction */
356
for (descptr = virtual_map;
357
((grub_uint8_t *) descptr - (grub_uint8_t *) virtual_map)
359
descptr = (grub_efi_memory_descriptor_t *)
360
((grub_uint8_t *) descptr + descriptor_size))
362
if (descptr->type == cur_relloc->plustype)
363
corr += descptr->virtual_start - descptr->physical_start;
364
if (descptr->type == cur_relloc->minustype)
365
corr -= descptr->virtual_start - descptr->physical_start;
368
/* Apply correction */
369
switch (cur_relloc->size)
372
*((grub_uint64_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
375
*((grub_uint32_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
378
*((grub_uint16_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
381
*((grub_uint8_t *) UINT_TO_PTR (cur_relloc->addr)) += corr;
386
/* Recompute crc32 of system table and runtime services */
387
efiemu_system_table.hdr.crc32 = 0;
388
efiemu_system_table.hdr.crc32 = efiemu_getcrc32
389
(0, &efiemu_system_table, sizeof (efiemu_system_table));
391
efiemu_runtime_services.hdr.crc32 = 0;
392
efiemu_runtime_services.hdr.crc32 = efiemu_getcrc32
393
(0, &efiemu_runtime_services, sizeof (efiemu_runtime_services));
395
return GRUB_EFI_SUCCESS;
398
/* since efiemu_set_virtual_address_map corrects all the pointers
399
we don't need efiemu_convert_pointer */
401
EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
405
return GRUB_EFI_UNSUPPORTED;
408
/* Next comes variable services. Because we have no vendor-independent
409
way to store these variables we have no non-volatility */
411
/* Find variable by name and GUID. */
412
static struct efi_variable *
413
find_variable (grub_efi_guid_t *vendor_guid,
414
grub_efi_char16_t *variable_name)
417
struct efi_variable *efivar;
419
for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
421
efivar = (struct efi_variable *) ptr;
422
if (!efivar->namelen)
424
if (efiemu_str16equal((grub_efi_char16_t *)(efivar + 1), variable_name)
425
&& efiemu_memequal (&(efivar->guid), vendor_guid,
426
sizeof (efivar->guid)))
428
ptr += efivar->namelen + efivar->size + sizeof (*efivar);
434
EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name,
435
grub_efi_guid_t *vendor_guid,
436
grub_efi_uint32_t *attributes,
437
grub_efi_uintn_t *data_size,
440
struct efi_variable *efivar;
442
efivar = find_variable (vendor_guid, variable_name);
444
return GRUB_EFI_NOT_FOUND;
445
if (*data_size < efivar->size)
447
*data_size = efivar->size;
448
return GRUB_EFI_BUFFER_TOO_SMALL;
450
*data_size = efivar->size;
451
efiemu_memcpy (data, (grub_uint8_t *)(efivar + 1) + efivar->namelen,
453
*attributes = efivar->attributes;
455
return GRUB_EFI_SUCCESS;
458
grub_efi_status_t EFI_FUNC
459
(efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size,
460
grub_efi_char16_t *variable_name,
461
grub_efi_guid_t *vendor_guid)
463
struct efi_variable *efivar;
466
if (!variable_name_size || !variable_name || !vendor_guid)
467
return GRUB_EFI_INVALID_PARAMETER;
468
if (variable_name[0])
470
efivar = find_variable (vendor_guid, variable_name);
472
return GRUB_EFI_NOT_FOUND;
473
efivar = (struct efi_variable *)((grub_uint8_t *)efivar
475
+ efivar->size + sizeof (*efivar));
478
efivar = (struct efi_variable *) (efiemu_variables);
481
if ((grub_uint8_t *)efivar >= efiemu_variables + efiemu_varsize
483
return GRUB_EFI_NOT_FOUND;
484
if (*variable_name_size < efivar->namelen)
486
*variable_name_size = efivar->namelen;
487
return GRUB_EFI_BUFFER_TOO_SMALL;
490
efiemu_memcpy (variable_name, efivar + 1, efivar->namelen);
491
efiemu_memcpy (vendor_guid, &(efivar->guid),
492
sizeof (efivar->guid));
495
return GRUB_EFI_SUCCESS;
499
EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name,
500
grub_efi_guid_t *vendor_guid,
501
grub_efi_uint32_t attributes,
502
grub_efi_uintn_t data_size,
505
struct efi_variable *efivar;
508
if (!variable_name[0])
509
return GRUB_EFI_INVALID_PARAMETER;
510
efivar = find_variable (vendor_guid, variable_name);
512
/* Delete variable if any */
515
efiemu_memcpy (efivar, (grub_uint8_t *)(efivar + 1)
516
+ efivar->namelen + efivar->size,
517
(efiemu_variables + efiemu_varsize)
518
- ((grub_uint8_t *)(efivar + 1)
519
+ efivar->namelen + efivar->size));
520
efiemu_memset (efiemu_variables + efiemu_varsize
521
- (sizeof (*efivar) + efivar->namelen + efivar->size),
522
0, (sizeof (*efivar) + efivar->namelen + efivar->size));
526
return GRUB_EFI_SUCCESS;
528
for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
530
efivar = (struct efi_variable *) ptr;
531
ptr += efivar->namelen + efivar->size + sizeof (*efivar);
532
if (!efivar->namelen)
535
if ((grub_uint8_t *)(efivar + 1) + data_size
536
+ 2 * (efiemu_str16len (variable_name) + 1)
537
>= efiemu_variables + efiemu_varsize)
538
return GRUB_EFI_OUT_OF_RESOURCES;
540
efiemu_memcpy (&(efivar->guid), vendor_guid, sizeof (efivar->guid));
541
efivar->namelen = 2 * (efiemu_str16len (variable_name) + 1);
542
efivar->size = data_size;
543
efivar->attributes = attributes;
544
efiemu_memcpy (efivar + 1, variable_name,
545
2 * (efiemu_str16len (variable_name) + 1));
546
efiemu_memcpy ((grub_uint8_t *)(efivar + 1)
547
+ 2 * (efiemu_str16len (variable_name) + 1),
550
return GRUB_EFI_SUCCESS;
553
grub_efi_status_t EFI_FUNC
554
(efiemu_get_next_high_monotonic_count) (grub_efi_uint32_t *high_count)
558
return GRUB_EFI_INVALID_PARAMETER;
559
*high_count = ++efiemu_high_monotonic_count;
560
return GRUB_EFI_SUCCESS;
563
/* To implement it with APM we need to go to real mode. It's too much hassle
564
Besides EFI specification says that this function shouldn't be used
565
on systems supporting ACPI
568
EFI_FUNC (efiemu_reset_system) (grub_efi_reset_type_t reset_type,
569
grub_efi_status_t reset_status,
570
grub_efi_uintn_t data_size,
571
grub_efi_char16_t *reset_data)
576
struct grub_efi_runtime_services efiemu_runtime_services =
580
.signature = GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE,
581
.revision = 0x0001000a,
582
.header_size = sizeof (struct grub_efi_runtime_services),
583
.crc32 = 0, /* filled later*/
586
.get_time = efiemu_get_time,
587
.set_time = efiemu_set_time,
588
.get_wakeup_time = efiemu_get_wakeup_time,
589
.set_wakeup_time = efiemu_set_wakeup_time,
591
.set_virtual_address_map = efiemu_set_virtual_address_map,
592
.convert_pointer = efiemu_convert_pointer,
594
.get_variable = efiemu_get_variable,
595
.get_next_variable_name = efiemu_get_next_variable_name,
596
.set_variable = efiemu_set_variable,
597
.get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count,
599
.reset_system = efiemu_reset_system
603
static grub_uint16_t efiemu_vendor[] =
604
{'G', 'R', 'U', 'B', ' ', 'E', 'F', 'I', ' ',
605
'R', 'U', 'N', 'T', 'I', 'M', 'E', 0};
607
struct grub_efi_system_table efiemu_system_table =
611
.signature = GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE,
612
.revision = 0x0001000a,
613
.header_size = sizeof (struct grub_efi_system_table),
614
.crc32 = 0, /* filled later*/
617
.firmware_vendor = efiemu_vendor,
618
.firmware_revision = 0x0001000a,
619
.console_in_handler = 0,
621
.console_out_handler = 0,
623
.standard_error_handle = 0,
625
.runtime_services = &efiemu_runtime_services,
627
.num_table_entries = 0,
628
.configuration_table = 0