Commit 0a2fe19c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pul x86/efi changes from Ingo Molnar:
 "This tree adds an EFI bootloader handover protocol, which, once
  supported on the bootloader side, will make bootup faster and might
  result in simpler bootloaders.

  The other change activates the EFI wall clock time accessors on x86-64
  as well, instead of the legacy RTC readout."

* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, efi: Handover Protocol
  x86-64/efi: Use EFI to deal with platform wall clock
parents c1b669b7 9ca8f72a
...@@ -54,6 +54,9 @@ Protocol 2.10: (Kernel 2.6.31) Added a protocol for relaxed alignment ...@@ -54,6 +54,9 @@ Protocol 2.10: (Kernel 2.6.31) Added a protocol for relaxed alignment
beyond the kernel_alignment added, new init_size and beyond the kernel_alignment added, new init_size and
pref_address fields. Added extended boot loader IDs. pref_address fields. Added extended boot loader IDs.
Protocol 2.11: (Kernel 3.6) Added a field for offset of EFI handover
protocol entry point.
**** MEMORY LAYOUT **** MEMORY LAYOUT
The traditional memory map for the kernel loader, used for Image or The traditional memory map for the kernel loader, used for Image or
...@@ -189,6 +192,7 @@ Offset Proto Name Meaning ...@@ -189,6 +192,7 @@ Offset Proto Name Meaning
of struct setup_data of struct setup_data
0258/8 2.10+ pref_address Preferred loading address 0258/8 2.10+ pref_address Preferred loading address
0260/4 2.10+ init_size Linear memory required during initialization 0260/4 2.10+ init_size Linear memory required during initialization
0264/4 2.11+ handover_offset Offset of handover entry point
(1) For backwards compatibility, if the setup_sects field contains 0, the (1) For backwards compatibility, if the setup_sects field contains 0, the
real value is 4. real value is 4.
...@@ -693,6 +697,16 @@ Offset/size: 0x260/4 ...@@ -693,6 +697,16 @@ Offset/size: 0x260/4
else else
runtime_start = pref_address runtime_start = pref_address
Field name: handover_offset
Type: read
Offset/size: 0x264/4
This field is the offset from the beginning of the kernel image to
the EFI handover protocol entry point. Boot loaders using the EFI
handover protocol to boot the kernel should jump to this offset.
See EFI HANDOVER PROTOCOL below for more details.
**** THE IMAGE CHECKSUM **** THE IMAGE CHECKSUM
...@@ -1013,3 +1027,30 @@ segment; __BOOS_CS must have execute/read permission, and __BOOT_DS ...@@ -1013,3 +1027,30 @@ segment; __BOOS_CS must have execute/read permission, and __BOOT_DS
must have read/write permission; CS must be __BOOT_CS and DS, ES, SS must have read/write permission; CS must be __BOOT_CS and DS, ES, SS
must be __BOOT_DS; interrupt must be disabled; %esi must hold the base must be __BOOT_DS; interrupt must be disabled; %esi must hold the base
address of the struct boot_params; %ebp, %edi and %ebx must be zero. address of the struct boot_params; %ebp, %edi and %ebx must be zero.
**** EFI HANDOVER PROTOCOL
This protocol allows boot loaders to defer initialisation to the EFI
boot stub. The boot loader is required to load the kernel/initrd(s)
from the boot media and jump to the EFI handover protocol entry point
which is hdr->handover_offset bytes from the beginning of
startup_{32,64}.
The function prototype for the handover entry point looks like this,
efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp)
'handle' is the EFI image handle passed to the boot loader by the EFI
firmware, 'table' is the EFI system table - these are the first two
arguments of the "handoff state" as described in section 2.3 of the
UEFI specification. 'bp' is the boot loader-allocated boot params.
The boot loader *must* fill out the following fields in bp,
o hdr.code32_start
o hdr.cmd_line_ptr
o hdr.cmdline_size
o hdr.ramdisk_image (if applicable)
o hdr.ramdisk_size (if applicable)
All other fields should be zero.
...@@ -729,32 +729,68 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, ...@@ -729,32 +729,68 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
* need to create one ourselves (usually the bootloader would create * need to create one ourselves (usually the bootloader would create
* one for us). * one for us).
*/ */
static efi_status_t make_boot_params(struct boot_params *boot_params, struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
efi_loaded_image_t *image,
void *handle)
{ {
struct efi_info *efi = &boot_params->efi_info; struct boot_params *boot_params;
struct apm_bios_info *bi = &boot_params->apm_bios_info; struct sys_desc_table *sdt;
struct sys_desc_table *sdt = &boot_params->sys_desc_table; struct apm_bios_info *bi;
struct e820entry *e820_map = &boot_params->e820_map[0]; struct setup_header *hdr;
struct e820entry *prev = NULL; struct efi_info *efi;
struct setup_header *hdr = &boot_params->hdr; efi_loaded_image_t *image;
unsigned long size, key, desc_size, _size; void *options;
efi_memory_desc_t *mem_map; u32 load_options_size;
void *options = image->load_options; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
u32 load_options_size = image->load_options_size / 2; /* ASCII */
int options_size = 0; int options_size = 0;
efi_status_t status; efi_status_t status;
__u32 desc_version;
unsigned long cmdline; unsigned long cmdline;
u8 nr_entries;
u16 *s2; u16 *s2;
u8 *s1; u8 *s1;
int i; int i;
sys_table = _table;
/* Check if we were booted by the EFI firmware */
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
return NULL;
status = efi_call_phys3(sys_table->boottime->handle_protocol,
handle, &proto, (void *)&image);
if (status != EFI_SUCCESS) {
efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
return NULL;
}
status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
if (status != EFI_SUCCESS) {
efi_printk("Failed to alloc lowmem for boot params\n");
return NULL;
}
memset(boot_params, 0x0, 0x4000);
hdr = &boot_params->hdr;
efi = &boot_params->efi_info;
bi = &boot_params->apm_bios_info;
sdt = &boot_params->sys_desc_table;
/* Copy the second sector to boot_params */
memcpy(&hdr->jump, image->image_base + 512, 512);
/*
* Fill out some of the header fields ourselves because the
* EFI firmware loader doesn't load the first sector.
*/
hdr->root_flags = 1;
hdr->vid_mode = 0xffff;
hdr->boot_flag = 0xAA55;
hdr->code32_start = (__u64)(unsigned long)image->image_base;
hdr->type_of_loader = 0x21; hdr->type_of_loader = 0x21;
/* Convert unicode cmdline to ascii */ /* Convert unicode cmdline to ascii */
options = image->load_options;
load_options_size = image->load_options_size / 2; /* ASCII */
cmdline = 0; cmdline = 0;
s2 = (u16 *)options; s2 = (u16 *)options;
...@@ -791,18 +827,36 @@ static efi_status_t make_boot_params(struct boot_params *boot_params, ...@@ -791,18 +827,36 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
hdr->ramdisk_image = 0; hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0; hdr->ramdisk_size = 0;
status = handle_ramdisks(image, hdr);
if (status != EFI_SUCCESS)
goto free_cmdline;
setup_graphics(boot_params);
/* Clear APM BIOS info */ /* Clear APM BIOS info */
memset(bi, 0, sizeof(*bi)); memset(bi, 0, sizeof(*bi));
memset(sdt, 0, sizeof(*sdt)); memset(sdt, 0, sizeof(*sdt));
memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); status = handle_ramdisks(image, hdr);
if (status != EFI_SUCCESS)
goto fail2;
return boot_params;
fail2:
if (options_size)
low_free(options_size, hdr->cmd_line_ptr);
fail:
low_free(0x4000, (unsigned long)boot_params);
return NULL;
}
static efi_status_t exit_boot(struct boot_params *boot_params,
void *handle)
{
struct efi_info *efi = &boot_params->efi_info;
struct e820entry *e820_map = &boot_params->e820_map[0];
struct e820entry *prev = NULL;
unsigned long size, key, desc_size, _size;
efi_memory_desc_t *mem_map;
efi_status_t status;
__u32 desc_version;
u8 nr_entries;
int i;
size = sizeof(*mem_map) * 32; size = sizeof(*mem_map) * 32;
...@@ -811,7 +865,7 @@ static efi_status_t make_boot_params(struct boot_params *boot_params, ...@@ -811,7 +865,7 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
_size = size; _size = size;
status = low_alloc(size, 1, (unsigned long *)&mem_map); status = low_alloc(size, 1, (unsigned long *)&mem_map);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto free_cmdline; return status;
status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
mem_map, &key, &desc_size, &desc_version); mem_map, &key, &desc_size, &desc_version);
...@@ -823,6 +877,7 @@ static efi_status_t make_boot_params(struct boot_params *boot_params, ...@@ -823,6 +877,7 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto free_mem_map; goto free_mem_map;
memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
efi->efi_systab = (unsigned long)sys_table; efi->efi_systab = (unsigned long)sys_table;
efi->efi_memdesc_size = desc_size; efi->efi_memdesc_size = desc_size;
efi->efi_memdesc_version = desc_version; efi->efi_memdesc_version = desc_version;
...@@ -906,61 +961,13 @@ static efi_status_t make_boot_params(struct boot_params *boot_params, ...@@ -906,61 +961,13 @@ static efi_status_t make_boot_params(struct boot_params *boot_params,
free_mem_map: free_mem_map:
low_free(_size, (unsigned long)mem_map); low_free(_size, (unsigned long)mem_map);
free_cmdline:
if (options_size)
low_free(options_size, hdr->cmd_line_ptr);
fail:
return status; return status;
} }
/* static efi_status_t relocate_kernel(struct setup_header *hdr)
* On success we return a pointer to a boot_params structure, and NULL
* on failure.
*/
struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
{ {
struct boot_params *boot_params;
unsigned long start, nr_pages; unsigned long start, nr_pages;
struct desc_ptr *gdt, *idt;
efi_loaded_image_t *image;
struct setup_header *hdr;
efi_status_t status; efi_status_t status;
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
struct desc_struct *desc;
sys_table = _table;
/* Check if we were booted by the EFI firmware */
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
goto fail;
status = efi_call_phys3(sys_table->boottime->handle_protocol,
handle, &proto, (void *)&image);
if (status != EFI_SUCCESS) {
efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
goto fail;
}
status = low_alloc(0x4000, 1, (unsigned long *)&boot_params);
if (status != EFI_SUCCESS) {
efi_printk("Failed to alloc lowmem for boot params\n");
goto fail;
}
memset(boot_params, 0x0, 0x4000);
hdr = &boot_params->hdr;
/* Copy the second sector to boot_params */
memcpy(&hdr->jump, image->image_base + 512, 512);
/*
* Fill out some of the header fields ourselves because the
* EFI firmware loader doesn't load the first sector.
*/
hdr->root_flags = 1;
hdr->vid_mode = 0xffff;
hdr->boot_flag = 0xAA55;
/* /*
* The EFI firmware loader could have placed the kernel image * The EFI firmware loader could have placed the kernel image
...@@ -978,16 +985,40 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table) ...@@ -978,16 +985,40 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
status = low_alloc(hdr->init_size, hdr->kernel_alignment, status = low_alloc(hdr->init_size, hdr->kernel_alignment,
&start); &start);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS)
efi_printk("Failed to alloc mem for kernel\n"); efi_printk("Failed to alloc mem for kernel\n");
goto fail;
}
} }
if (status == EFI_SUCCESS)
memcpy((void *)start, (void *)(unsigned long)hdr->code32_start,
hdr->init_size);
hdr->pref_address = hdr->code32_start;
hdr->code32_start = (__u32)start; hdr->code32_start = (__u32)start;
hdr->pref_address = (__u64)(unsigned long)image->image_base;
memcpy((void *)start, image->image_base, image->image_size); return status;
}
/*
* On success we return a pointer to a boot_params structure, and NULL
* on failure.
*/
struct boot_params *efi_main(void *handle, efi_system_table_t *_table,
struct boot_params *boot_params)
{
struct desc_ptr *gdt, *idt;
efi_loaded_image_t *image;
struct setup_header *hdr = &boot_params->hdr;
efi_status_t status;
struct desc_struct *desc;
sys_table = _table;
/* Check if we were booted by the EFI firmware */
if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
goto fail;
setup_graphics(boot_params);
status = efi_call_phys3(sys_table->boottime->allocate_pool, status = efi_call_phys3(sys_table->boottime->allocate_pool,
EFI_LOADER_DATA, sizeof(*gdt), EFI_LOADER_DATA, sizeof(*gdt),
...@@ -1015,7 +1046,18 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table) ...@@ -1015,7 +1046,18 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table)
idt->size = 0; idt->size = 0;
idt->address = 0; idt->address = 0;
status = make_boot_params(boot_params, image, handle); /*
* If the kernel isn't already loaded at the preferred load
* address, relocate it.
*/
if (hdr->pref_address != hdr->code32_start) {
status = relocate_kernel(hdr);
if (status != EFI_SUCCESS)
goto fail;
}
status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto fail; goto fail;
......
...@@ -42,6 +42,16 @@ ENTRY(startup_32) ...@@ -42,6 +42,16 @@ ENTRY(startup_32)
*/ */
add $0x4, %esp add $0x4, %esp
call make_boot_params
cmpl $0, %eax
je 1f
movl 0x4(%esp), %esi
movl (%esp), %ecx
pushl %eax
pushl %esi
pushl %ecx
.org 0x30,0x90
call efi_main call efi_main
cmpl $0, %eax cmpl $0, %eax
movl %eax, %esi movl %eax, %esi
......
...@@ -209,6 +209,16 @@ ENTRY(startup_64) ...@@ -209,6 +209,16 @@ ENTRY(startup_64)
.org 0x210 .org 0x210
mov %rcx, %rdi mov %rcx, %rdi
mov %rdx, %rsi mov %rdx, %rsi
pushq %rdi
pushq %rsi
call make_boot_params
cmpq $0,%rax
je 1f
mov %rax, %rdx
popq %rsi
popq %rdi
.org 0x230,0x90
call efi_main call efi_main
movq %rax,%rsi movq %rax,%rsi
cmpq $0,%rax cmpq $0,%rax
......
...@@ -283,7 +283,7 @@ _start: ...@@ -283,7 +283,7 @@ _start:
# Part 2 of the header, from the old setup.S # Part 2 of the header, from the old setup.S
.ascii "HdrS" # header signature .ascii "HdrS" # header signature
.word 0x020a # header version number (>= 0x0105) .word 0x020b # header version number (>= 0x0105)
# or else old loadlin-1.5 will fail) # or else old loadlin-1.5 will fail)
.globl realmode_swtch .globl realmode_swtch
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
...@@ -401,6 +401,8 @@ pref_address: .quad LOAD_PHYSICAL_ADDR # preferred load addr ...@@ -401,6 +401,8 @@ pref_address: .quad LOAD_PHYSICAL_ADDR # preferred load addr
#define INIT_SIZE VO_INIT_SIZE #define INIT_SIZE VO_INIT_SIZE
#endif #endif
init_size: .long INIT_SIZE # kernel initialization size init_size: .long INIT_SIZE # kernel initialization size
handover_offset: .long 0x30 # offset to the handover
# protocol entry point
# End of setup header ##################################################### # End of setup header #####################################################
......
...@@ -66,6 +66,7 @@ struct setup_header { ...@@ -66,6 +66,7 @@ struct setup_header {
__u64 setup_data; __u64 setup_data;
__u64 pref_address; __u64 pref_address;
__u32 init_size; __u32 init_size;
__u32 handover_offset;
} __attribute__((packed)); } __attribute__((packed));
struct sys_desc_table { struct sys_desc_table {
......
...@@ -919,11 +919,13 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, ...@@ -919,11 +919,13 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
/* /*
* On success we use clflush, when the CPU supports it to * On success we use clflush, when the CPU supports it to
* avoid the wbindv. If the CPU does not support it and in the * avoid the wbindv. If the CPU does not support it, in the
* error case we fall back to cpa_flush_all (which uses * error case, and during early boot (for EFI) we fall back
* wbindv): * to cpa_flush_all (which uses wbinvd):
*/ */
if (!ret && cpu_has_clflush) { if (early_boot_irqs_disabled)
__cpa_flush_all((void *)(long)cache);
else if (!ret && cpu_has_clflush) {
if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) {
cpa_flush_array(addr, numpages, cache, cpa_flush_array(addr, numpages, cache,
cpa.flags, pages); cpa.flags, pages);
......
...@@ -234,22 +234,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map( ...@@ -234,22 +234,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
return status; return status;
} }
static efi_status_t __init phys_efi_get_time(efi_time_t *tm, static int efi_set_rtc_mmss(unsigned long nowtime)
efi_time_cap_t *tc)
{
unsigned long flags;
efi_status_t status;
spin_lock_irqsave(&rtc_lock, flags);
efi_call_phys_prelog();
status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm),
virt_to_phys(tc));
efi_call_phys_epilog();
spin_unlock_irqrestore(&rtc_lock, flags);
return status;
}
int efi_set_rtc_mmss(unsigned long nowtime)
{ {
int real_seconds, real_minutes; int real_seconds, real_minutes;
efi_status_t status; efi_status_t status;
...@@ -278,7 +263,7 @@ int efi_set_rtc_mmss(unsigned long nowtime) ...@@ -278,7 +263,7 @@ int efi_set_rtc_mmss(unsigned long nowtime)
return 0; return 0;
} }
unsigned long efi_get_time(void) static unsigned long efi_get_time(void)
{ {
efi_status_t status; efi_status_t status;
efi_time_t eft; efi_time_t eft;
...@@ -621,18 +606,13 @@ static int __init efi_runtime_init(void) ...@@ -621,18 +606,13 @@ static int __init efi_runtime_init(void)
} }
/* /*
* We will only need *early* access to the following * We will only need *early* access to the following
* two EFI runtime services before set_virtual_address_map * EFI runtime service before set_virtual_address_map
* is invoked. * is invoked.
*/ */
efi_phys.get_time = (efi_get_time_t *)runtime->get_time;
efi_phys.set_virtual_address_map = efi_phys.set_virtual_address_map =
(efi_set_virtual_address_map_t *) (efi_set_virtual_address_map_t *)
runtime->set_virtual_address_map; runtime->set_virtual_address_map;
/*
* Make efi_get_time can be called before entering
* virtual mode.
*/
efi.get_time = phys_efi_get_time;
early_iounmap(runtime, sizeof(efi_runtime_services_t)); early_iounmap(runtime, sizeof(efi_runtime_services_t));
return 0; return 0;
...@@ -720,12 +700,10 @@ void __init efi_init(void) ...@@ -720,12 +700,10 @@ void __init efi_init(void)
efi_enabled = 0; efi_enabled = 0;
return; return;
} }
#ifdef CONFIG_X86_32
if (efi_native) { if (efi_native) {
x86_platform.get_wallclock = efi_get_time; x86_platform.get_wallclock = efi_get_time;
x86_platform.set_wallclock = efi_set_rtc_mmss; x86_platform.set_wallclock = efi_set_rtc_mmss;
} }
#endif
#if EFI_DEBUG #if EFI_DEBUG
print_efi_memmap(); print_efi_memmap();
......
...@@ -503,8 +503,6 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); ...@@ -503,8 +503,6 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
extern int __init efi_uart_console_only (void); extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource, extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource); struct resource *data_resource, struct resource *bss_resource);
extern unsigned long efi_get_time(void);
extern int efi_set_rtc_mmss(unsigned long nowtime);
extern void efi_reserve_boot_services(void); extern void efi_reserve_boot_services(void);
extern struct efi_memory_map memmap; extern struct efi_memory_map memmap;
......
...@@ -461,6 +461,10 @@ static void __init mm_init(void) ...@@ -461,6 +461,10 @@ static void __init mm_init(void)
percpu_init_late(); percpu_init_late();
pgtable_cache_init(); pgtable_cache_init();
vmalloc_init(); vmalloc_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
} }
asmlinkage void __init start_kernel(void) asmlinkage void __init start_kernel(void)
...@@ -602,10 +606,6 @@ asmlinkage void __init start_kernel(void) ...@@ -602,10 +606,6 @@ asmlinkage void __init start_kernel(void)
calibrate_delay(); calibrate_delay();
pidmap_init(); pidmap_init();
anon_vma_init(); anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
thread_info_cache_init(); thread_info_cache_init();
cred_init(); cred_init();
fork_init(totalram_pages); fork_init(totalram_pages);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment