Re: [PATCH] efi: Retain boot service code until after switching tovirtual mode
From: Yinghai Lu
Date: Thu May 19 2011 - 16:26:32 EST
On Thu, May 19, 2011 at 10:32 AM, Matthew Garrett <mjg@xxxxxxxxxx> wrote:
> UEFI stands for "Unified Extensible Firmware Interface", where "Firmware"
> is an ancient African word meaning "Why do something right when you can
> do it so wrong that children will weep and brave adults will cower before
> you", and "UEI" is Celtic for "We missed DOS so we burned it into your
> ROMs". The UEFI specification provides for runtime services (ie, another
> way for the operating system to be forced to depend on the firmware) and
> we rely on these for certain trivial tasks such as setting up the
> bootloader. But some hardware fails to work if we attempt to use these
> runtime services from physical mode, and so we have to switch into virtual
> mode. So far so dreadful.
>
> The specification makes it clear that the operating system is free to do
> whatever it wants with boot services code after ExitBootServices() has been
> called. SetVirtualAddressMap() can't be called until ExitBootServices() has
> been. So, obviously, a whole bunch of EFI implementations call into boot
> services code when we do that. Since we've been charmingly naive and
> trusted that the specification may be somehow relevant to the real world,
> we've already stuffed a picture of a penguin or something in that address
> space. And just to make things more entertaining, we've also marked it
> non-executable.
>
> This patch allocates the boot services regions during EFI init and makes
> sure that they're executable. Then, after SetVirtualAddressMap(), it
> discards them and everyone lives happily ever after. Except for the ones
> who have to work on EFI, who live sad lives haunted by the knowledge that
> someone's eventually going to write yet another firmware specification.
>
> Signed-off-by: Matthew Garrett <mjg@xxxxxxxxxx>
> ---
> arch/x86/platform/efi/efi.c | 51 +++++++++++++++++++++++++++++++++++++++-
> arch/x86/platform/efi/efi_64.c | 5 ++-
> 2 files changed, 53 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
> index b30aa26..8237c6e 100644
> --- a/arch/x86/platform/efi/efi.c
> +++ b/arch/x86/platform/efi/efi.c
> @@ -304,6 +304,40 @@ static void __init print_efi_memmap(void)
> }
> #endif /* EFI_DEBUG */
>
> +static void __init reserve_efi_boot_services(void)
> +{
> + void *p;
> +
> + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
> + efi_memory_desc_t *md = p;
> + unsigned long long start = md->phys_addr;
> + unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
> +
> + if (md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> +
> + memblock_x86_reserve_range(start, start + size, "EFI Boot");
> + }
> +}
> +
> +static void __init free_efi_boot_services(void)
> +{
> + void *p;
> +
> + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
> + efi_memory_desc_t *md = p;
> + unsigned long long start = md->phys_addr;
> + unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
> +
> + if (md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> + continue;
> +
> + memblock_x86_free_range(start, start + size);
> + }
> +}
> +
> void __init efi_init(void)
> {
> efi_config_table_t *config_tables;
> @@ -441,6 +475,12 @@ void __init efi_init(void)
> printk(KERN_WARNING
> "Kernel-defined memdesc doesn't match the one from EFI!\n");
>
> + /*
> + * The EFI specification says that boot service code won't be called
> + * after ExitBootServices(). This is, in fact, a lie.
> + */
> + reserve_efi_boot_services();
> +
> if (add_efi_memmap)
> do_add_efi_memmap();
>
how many entries are listed in memmap?
Did you test this system that have lots of those entries ? like > 128?
because efi_init() is called before memblock_x86_fill(), that means
memblock array for reserved can not be doubled yet. --- no usable
entries in memblock array for ram.
> @@ -536,7 +576,9 @@ void __init efi_enter_virtual_mode(void)
>
> for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
> md = p;
> - if (!(md->attribute & EFI_MEMORY_RUNTIME))
> + if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
> + md->type != EFI_BOOT_SERVICES_CODE &&
> + md->type != EFI_BOOT_SERVICES_DATA)
> continue;
>
> size = md->num_pages << EFI_PAGE_SHIFT;
> @@ -593,6 +635,13 @@ void __init efi_enter_virtual_mode(void)
> }
>
> /*
> + * Thankfully, it does seem that no runtime services other than
> + * SetVirtualAddressMap() will touch boot services code, so we can
> + * get rid of it all at this point
> + */
> + free_efi_boot_services();
> +
No, at that point memblock is not used any more. after mm_init()/mem_init()
need to use free_bootmem_late() in free_efi_boot_services
Thanks
Yinghai
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/