Changeset View
Standalone View
sys/dev/efidev/efirt.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <machine/vmparam.h> | #include <machine/vmparam.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_map.h> | #include <vm/vm_map.h> | ||||
static struct efi_systbl *efi_systbl; | static struct efi_systbl *efi_systbl; | ||||
static eventhandler_tag efi_shutdown_tag; | static eventhandler_tag efi_shutdown_tag; | ||||
static struct efi_map_header *efihdr; | |||||
kib: You do not need = NULL;. | |||||
static size_t efi_hdr_sz; | |||||
static struct efi_md *efi_map; | |||||
static int efi_map_ndesc; | |||||
/* | /* | ||||
* The following pointers point to tables in the EFI runtime service data pages. | * The following pointers point to tables in the EFI runtime service data pages. | ||||
* Care should be taken to make sure that we've properly entered the EFI runtime | * Care should be taken to make sure that we've properly entered the EFI runtime | ||||
* environment (efi_enter()) before dereferencing them. | * environment (efi_enter()) before dereferencing them. | ||||
*/ | */ | ||||
static struct efi_cfgtbl *efi_cfgtbl; | static struct efi_cfgtbl *efi_cfgtbl; | ||||
static struct efi_rt *efi_runtime; | static struct efi_rt *efi_runtime; | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p, | ||||
if (addr >= (uintptr_t)p->md_virt && | if (addr >= (uintptr_t)p->md_virt && | ||||
addr < (uintptr_t)p->md_virt + p->md_pages * PAGE_SIZE) | addr < (uintptr_t)p->md_virt + p->md_pages * PAGE_SIZE) | ||||
return (true); | return (true); | ||||
} | } | ||||
return (false); | return (false); | ||||
} | } | ||||
static int | |||||
efi_map_compare(const void *key, const void *member) | |||||
Not Done Inline Actionsefi_map_bsearch_compare() kib: efi_map_bsearch_compare() | |||||
{ | |||||
uint64_t pa; | |||||
Not Done Inline ActionsI believe you should use vm_paddr_t there. You pass a pointer to vm_paddr_t, which is uint64_t only by coincidense. kib: I believe you should use vm_paddr_t there. You pass a pointer to vm_paddr_t, which is uint64_t… | |||||
const struct efi_md *md; | |||||
pa = *(const uint64_t *)key; | |||||
md = (const struct efi_md *)member; | |||||
Done Inline ActionsExtra blank line. kib: Extra blank line. | |||||
Done Inline ActionsThis should be in a helper function. kib: This should be in a helper function. | |||||
if (pa >= (uintptr_t)md->md_phys && | |||||
pa < (uintptr_t)md->md_phys + md->md_pages * PAGE_SIZE) | |||||
return (0); | |||||
if (pa < (uintptr_t)md->md_phys) | |||||
return (-1); | |||||
return (1); | |||||
Done Inline Actionsreturn (efihdr != NULL); kib: `return (efihdr != NULL);` | |||||
} | |||||
Done Inline ActionsAnother extra. kib: Another extra. | |||||
static bool | |||||
efi_get_memory_map(void) | |||||
{ | |||||
caddr_t kmdp; | |||||
if (efi_map != NULL) | |||||
return (true); | |||||
kmdp = preload_search_by_type("elf kernel"); | |||||
if (kmdp == NULL) | |||||
Not Done Inline Actionstrailing ws emaste: trailing ws | |||||
kmdp = preload_search_by_type("elf64 kernel"); | |||||
efihdr = (struct efi_map_header *)preload_search_info(kmdp, | |||||
MODINFO_METADATA | MODINFOMD_EFI_MAP); | |||||
if (efihdr == NULL) | |||||
Done Inline ActionsIt looks like this could be replaced with roundup2(sizeof(struct efi_map_header), 16) andrew: It looks like this could be replaced with
`roundup2(sizeof(struct efi_map_header), 16)` | |||||
return (false); | |||||
efi_hdr_sz = roundup2(sizeof(struct efi_map_header), 16); | |||||
efi_map = (struct efi_md *)((uint8_t *)efihdr + efi_hdr_sz); | |||||
if (efihdr->descriptor_size == 0) | |||||
return (0); | |||||
efi_map_ndesc = efihdr->memory_size / efihdr->descriptor_size; | |||||
return (true); | |||||
Done Inline ActionsNo need for (). kib: No need for (). | |||||
Done Inline ActionsIndeed, that was just a copy/paste from the old code. manu: Indeed, that was just a copy/paste from the old code. | |||||
} | |||||
uint64_t | |||||
efi_memory_attribute(vm_paddr_t pa) | |||||
{ | |||||
Done Inline ActionsIs it reasonable to return 0 if the address is not covered by the map ? Could EFI_MEMORY_UC be a better return value in this case ? kib: Is it reasonable to return 0 if the address is not covered by the map ?
Could EFI_MEMORY_UC be… | |||||
Done Inline ActionsThis is something I'm really not sure about. manu: This is something I'm really not sure about.
We really need to have a consistent way of… | |||||
struct efi_md *p; | |||||
if (!efi_get_memory_map()) | |||||
return (0); | |||||
p = bsearch(&pa, efi_map, efi_map_ndesc, efihdr->descriptor_size, | |||||
efi_map_compare); | |||||
return (p == NULL ? EFI_MD_ATTR_UC : p->md_attr); | |||||
} | |||||
static void | static void | ||||
efi_shutdown_final(void *dummy __unused, int howto) | efi_shutdown_final(void *dummy __unused, int howto) | ||||
{ | { | ||||
/* | /* | ||||
* On some systems, ACPI S5 is missing or does not function properly. | * On some systems, ACPI S5 is missing or does not function properly. | ||||
* When present, shutdown via EFI Runtime Services instead, unless | * When present, shutdown via EFI Runtime Services instead, unless | ||||
* disabled. | * disabled. | ||||
*/ | */ | ||||
if ((howto & RB_POWEROFF) != 0 && efi_poweroff) | if ((howto & RB_POWEROFF) != 0 && efi_poweroff) | ||||
(void)efi_reset_system(EFI_RESET_SHUTDOWN); | (void)efi_reset_system(EFI_RESET_SHUTDOWN); | ||||
} | } | ||||
static int | static int | ||||
efi_init(void) | efi_init(void) | ||||
{ | { | ||||
Not Done Inline ActionsI think that the semantic of *out should be 'a byte after the end of the segment'. If we assume that pa is exactly equal to the p->md_phys, then this means that -1 is not needed. If pa is greater than md_phys, then you need to adjust the formula to correctly return number of pages left after pa in the segment. kib: I think that the semantic of *out should be 'a byte after the end of the segment'.
If we… | |||||
struct efi_map_header *efihdr; | |||||
struct efi_md *map; | |||||
struct efi_rt *rtdm; | struct efi_rt *rtdm; | ||||
caddr_t kmdp; | int rt_disabled; | ||||
size_t efisz; | |||||
int ndesc, rt_disabled; | |||||
rt_disabled = 0; | rt_disabled = 0; | ||||
Not Done Inline ActionsWhy do you need bsearch ? After the map was sorted, we know for sure that the next segment in phys address order starts after the current one. You should check for a gap and last segment, but otherwise you could use the next segment without looking it up. kib: Why do you need bsearch ? After the map was sorted, we know for sure that the next segment in… | |||||
TUNABLE_INT_FETCH("efi.rt.disabled", &rt_disabled); | TUNABLE_INT_FETCH("efi.rt.disabled", &rt_disabled); | ||||
if (rt_disabled == 1) | if (rt_disabled == 1) | ||||
return (0); | return (0); | ||||
mtx_init(&efi_lock, "efi", NULL, MTX_DEF); | mtx_init(&efi_lock, "efi", NULL, MTX_DEF); | ||||
if (efi_systbl_phys == 0) { | if (efi_systbl_phys == 0) { | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("EFI systbl not available\n"); | printf("EFI systbl not available\n"); | ||||
Show All 9 Lines | efi_init(void) | ||||
} | } | ||||
efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL : | efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL : | ||||
(struct efi_cfgtbl *)efi_systbl->st_cfgtbl; | (struct efi_cfgtbl *)efi_systbl->st_cfgtbl; | ||||
if (efi_cfgtbl == NULL) { | if (efi_cfgtbl == NULL) { | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("EFI config table is not present\n"); | printf("EFI config table is not present\n"); | ||||
} | } | ||||
kmdp = preload_search_by_type("elf kernel"); | if (!efi_get_memory_map()) { | ||||
if (kmdp == NULL) | |||||
kmdp = preload_search_by_type("elf64 kernel"); | |||||
efihdr = (struct efi_map_header *)preload_search_info(kmdp, | |||||
MODINFO_METADATA | MODINFOMD_EFI_MAP); | |||||
if (efihdr == NULL) { | |||||
if (bootverbose) | if (bootverbose) | ||||
printf("EFI map is not present\n"); | printf("EFI map is not present\n"); | ||||
Done Inline ActionsSee note above about a helper. kib: See note above about a helper. | |||||
return (0); | return (0); | ||||
} | } | ||||
efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; | |||||
map = (struct efi_md *)((uint8_t *)efihdr + efisz); | |||||
if (efihdr->descriptor_size == 0) | |||||
return (ENOMEM); | |||||
ndesc = efihdr->memory_size / efihdr->descriptor_size; | if (!efi_create_1t1_map(efi_map, efi_map_ndesc, efihdr->descriptor_size)) { | ||||
if (!efi_create_1t1_map(map, ndesc, efihdr->descriptor_size)) { | |||||
if (bootverbose) | if (bootverbose) | ||||
printf("EFI cannot create runtime map\n"); | printf("EFI cannot create runtime map\n"); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
efi_runtime = (efi_systbl->st_rt == 0) ? NULL : | efi_runtime = (efi_systbl->st_rt == 0) ? NULL : | ||||
(struct efi_rt *)efi_systbl->st_rt; | (struct efi_rt *)efi_systbl->st_rt; | ||||
if (efi_runtime == NULL) { | if (efi_runtime == NULL) { | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("EFI runtime services table is not present\n"); | printf("EFI runtime services table is not present\n"); | ||||
efi_destroy_1t1_map(); | efi_destroy_1t1_map(); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
#if defined(__aarch64__) || defined(__amd64__) | #if defined(__aarch64__) || defined(__amd64__) | ||||
/* | /* | ||||
* Some UEFI implementations have multiple implementations of the | * Some UEFI implementations have multiple implementations of the | ||||
* RS->GetTime function. They switch from one we can only use early | * RS->GetTime function. They switch from one we can only use early | ||||
* in the boot process to one valid as a RunTime service only when we | * in the boot process to one valid as a RunTime service only when we | ||||
* call RS->SetVirtualAddressMap. As this is not always the case, e.g. | * call RS->SetVirtualAddressMap. As this is not always the case, e.g. | ||||
* with an old loader.efi, check if the RS->GetTime function is within | * with an old loader.efi, check if the RS->GetTime function is within | ||||
* the EFI map, and fail to attach if not. | * the EFI map, and fail to attach if not. | ||||
*/ | */ | ||||
rtdm = (struct efi_rt *)efi_phys_to_kva((uintptr_t)efi_runtime); | rtdm = (struct efi_rt *)efi_phys_to_kva((uintptr_t)efi_runtime); | ||||
if (rtdm == NULL || !efi_is_in_map(map, ndesc, efihdr->descriptor_size, | if (rtdm == NULL || !efi_is_in_map(efi_map, efi_map_ndesc, | ||||
(vm_offset_t)rtdm->rt_gettime)) { | efihdr->descriptor_size, (vm_offset_t)rtdm->rt_gettime)) { | ||||
if (bootverbose) | if (bootverbose) | ||||
printf( | printf( | ||||
"EFI runtime services table has an invalid pointer\n"); | "EFI runtime services table has an invalid pointer\n"); | ||||
efi_runtime = NULL; | efi_runtime = NULL; | ||||
efi_destroy_1t1_map(); | efi_destroy_1t1_map(); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
#endif | #endif | ||||
if (bootverbose) { | |||||
Done Inline ActionsPerhaps put this under bootverbose. kib: Perhaps put this under bootverbose. | |||||
Done Inline ActionsSorry it's not needed I just commited part of some debug code by mistake, will remove it. manu: Sorry it's not needed I just commited part of some debug code by mistake, will remove it. | |||||
Not Done Inline ActionsI find it useful, but it should not clutter normal boot messages. kib: I find it useful, but it should not clutter normal boot messages. | |||||
Done Inline ActionsOk I'll leave this line and remove the ones that prints the runtime function addresses (unless you want those too). manu: Ok I'll leave this line and remove the ones that prints the runtime function addresses (unless… | |||||
Done Inline ActionsI find addresses are useful too. kib: I find addresses are useful too. | |||||
printf("EFI RT Signature: %jx, Revision=%x, Size=%x, CRC32=%x\n", | |||||
Done Inline ActionsThe format specifiers there are still very LP64-specific. I would use %jx/uintmax_t for all non-pointers. kib: The format specifiers there are still very LP64-specific. I would use %jx/uintmax_t for all… | |||||
efi_runtime->rt_hdr.th_sig, | |||||
efi_runtime->rt_hdr.th_rev, | |||||
efi_runtime->rt_hdr.th_hdrsz, | |||||
efi_runtime->rt_hdr.th_crc32); | |||||
printf("EFI RT Get Time: %p\n", efi_runtime->rt_gettime); | |||||
Done Inline ActionsI think %p would be a better format specifier. kib: I think %p would be a better format specifier. | |||||
Done Inline ActionsOk, I'll change that. manu: Ok, I'll change that. | |||||
printf("EFI RT Set Time: %p\n", efi_runtime->rt_settime); | |||||
printf("EFI RT Get Wakeup Time: %p\n", | |||||
efi_runtime->rt_getwaketime); | |||||
printf("EFI RT Set Wakeup Time: %p\n", | |||||
efi_runtime->rt_setwaketime); | |||||
printf("EFI RT Get Var: %p\n", efi_runtime->rt_getvar); | |||||
printf("EFI RT Get Next Var: %p\n", efi_runtime->rt_scanvar); | |||||
printf("EFI RT Set Var: %p\n", efi_runtime->rt_setvar); | |||||
} | |||||
/* | /* | ||||
* We use SHUTDOWN_PRI_LAST - 1 to trigger after IPMI, but before ACPI. | * We use SHUTDOWN_PRI_LAST - 1 to trigger after IPMI, but before ACPI. | ||||
*/ | */ | ||||
efi_shutdown_tag = EVENTHANDLER_REGISTER(shutdown_final, | efi_shutdown_tag = EVENTHANDLER_REGISTER(shutdown_final, | ||||
efi_shutdown_final, NULL, SHUTDOWN_PRI_LAST - 1); | efi_shutdown_final, NULL, SHUTDOWN_PRI_LAST - 1); | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | ec->ec_efi_status = ((register_t (*)(register_t, register_t, | ||||
ec->ec_arg3); | ec->ec_arg3); | ||||
break; | break; | ||||
case 4: | case 4: | ||||
ec->ec_efi_status = ((register_t (*)(register_t, register_t, | ec->ec_efi_status = ((register_t (*)(register_t, register_t, | ||||
register_t, register_t))ec->ec_fptr)(ec->ec_arg1, | register_t, register_t))ec->ec_fptr)(ec->ec_arg1, | ||||
ec->ec_arg2, ec->ec_arg3, ec->ec_arg4); | ec->ec_arg2, ec->ec_arg3, ec->ec_arg4); | ||||
break; | break; | ||||
case 5: | case 5: | ||||
ec->ec_efi_status = ((register_t (*)(register_t, register_t, | ec->ec_efi_status = ((register_t (*)(register_t, register_t, | ||||
Done Inline ActionsSome debugging left ? kib: Some debugging left ? | |||||
register_t, register_t, register_t))ec->ec_fptr)( | register_t, register_t, register_t))ec->ec_fptr)( | ||||
ec->ec_arg1, ec->ec_arg2, ec->ec_arg3, ec->ec_arg4, | ec->ec_arg1, ec->ec_arg2, ec->ec_arg3, ec->ec_arg4, | ||||
ec->ec_arg5); | ec->ec_arg5); | ||||
break; | break; | ||||
default: | default: | ||||
panic("efi_rt_arch_call: %d args", (int)ec->ec_argcnt); | panic("efi_rt_arch_call: %d args", (int)ec->ec_argcnt); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
efi_call(struct efirt_callinfo *ecp) | efi_call(struct efirt_callinfo *ecp) | ||||
{ | { | ||||
int error; | int error; | ||||
error = efi_enter(); | error = efi_enter(); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = efi_rt_handle_faults ? efi_rt_arch_call(ecp) : | error = efi_rt_handle_faults ? efi_rt_arch_call(ecp) : | ||||
efi_rt_arch_call_nofault(ecp); | efi_rt_arch_call_nofault(ecp); | ||||
efi_leave(); | efi_leave(); | ||||
if (error == 0) | if (error == 0) | ||||
Done Inline ActionsAnd this. kib: And this. | |||||
error = efi_status_to_errno(ecp->ec_efi_status); | error = efi_status_to_errno(ecp->ec_efi_status); | ||||
else if (bootverbose) | else if (bootverbose) | ||||
printf("EFI %s call faulted, error %d\n", ecp->ec_name, error); | printf("EFI %s call faulted, error %d\n", ecp->ec_name, error); | ||||
return (error); | return (error); | ||||
} | } | ||||
#define EFI_RT_METHOD_PA(method) \ | #define EFI_RT_METHOD_PA(method) \ | ||||
((uintptr_t)((struct efi_rt *)efi_phys_to_kva((uintptr_t) \ | ((uintptr_t)((struct efi_rt *)efi_phys_to_kva((uintptr_t) \ | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | efi_var_set(efi_char *name, struct uuid *vendor, uint32_t attrib, | ||||
ec.ec_argcnt = 5; | ec.ec_argcnt = 5; | ||||
ec.ec_name = "rt_setvar"; | ec.ec_name = "rt_setvar"; | ||||
ec.ec_arg1 = (uintptr_t)name; | ec.ec_arg1 = (uintptr_t)name; | ||||
ec.ec_arg2 = (uintptr_t)vendor; | ec.ec_arg2 = (uintptr_t)vendor; | ||||
ec.ec_arg3 = (uintptr_t)attrib; | ec.ec_arg3 = (uintptr_t)attrib; | ||||
ec.ec_arg4 = (uintptr_t)datasize; | ec.ec_arg4 = (uintptr_t)datasize; | ||||
ec.ec_arg5 = (uintptr_t)data; | ec.ec_arg5 = (uintptr_t)data; | ||||
ec.ec_fptr = EFI_RT_METHOD_PA(rt_setvar); | ec.ec_fptr = EFI_RT_METHOD_PA(rt_setvar); | ||||
return (efi_call(&ec)); | return (efi_call(&ec)); | ||||
Done Inline ActionsAnd this. kib: And this. | |||||
} | } | ||||
static int | static int | ||||
efirt_modevents(module_t m, int event, void *arg __unused) | efirt_modevents(module_t m, int event, void *arg __unused) | ||||
{ | { | ||||
switch (event) { | switch (event) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
Show All 22 Lines |
You do not need = NULL;.