Re: [PATCH 2.6.21 1/3] x86_64: EFI64 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 01 May 2007 11:59:46 -0700 Chandramouli Narayanan wrote:

> EFI x86_64 build option is added to the kernel configuration.


Hi Mouli,

Can you share EFI code as much as possible among ia64, i386,
and x86_64 instead of duplicating it?


A diffstat patch summary would be Good.
(see Documentation/SubmittingPatches)


> diff -uprN -X linux-2.6.21rc7-git2-orig/Documentation/dontdiff linux-2.6.21rc7-git2-orig/arch/x86_64/Kconfig linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/Kconfig
> --- linux-2.6.21rc7-git2-orig/arch/x86_64/Kconfig	2007-04-19 12:39:39.000000000 -0700
> +++ linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/Kconfig	2007-04-19 13:01:02.000000000 -0700
> @@ -254,6 +254,20 @@ config X86_HT
>  	depends on SMP && !MK8
>  	default y
>  
> +config EFI
> +	bool "Boot from EFI support (EXPERIMENTAL)"
> +	default n
> +	---help---
> +

No blank line above.
Indent following lines by 2 spaces:  i.e., <tab><space><space>
as in Documentation/CodingStyle.

> +	This enables the the kernel to boot on EFI platforms using
> +	system configuration information passed to it from the firmware.
> +	This also enables the kernel to use any EFI runtime services that are
> +	available (such as the EFI variable services).
> +	This option is only useful on systems that have EFI firmware
> +	and will result in a kernel image that is ~8k larger. However,
> +	even with this option, the resultant kernel should continue to
> +	boot on existing non-EFI platforms.
> +
>  config MATH_EMULATION
>  	bool


> diff -uprN -X linux-2.6.21rc7-git2-orig/Documentation/dontdiff linux-2.6.21rc7-git2-orig/include/asm-x86_64/bootsetup.h linux-2.6.21rc7-git2-uefi-finaltest/include/asm-x86_64/bootsetup.h
> --- linux-2.6.21rc7-git2-orig/include/asm-x86_64/bootsetup.h	2007-04-19 12:39:40.000000000 -0700
> +++ linux-2.6.21rc7-git2-uefi-finaltest/include/asm-x86_64/bootsetup.h	2007-04-19 13:01:02.000000000 -0700
> @@ -17,6 +17,12 @@ extern char x86_boot_params[BOOT_PARAM_S
>  #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
>  #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
>  #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
> +#define EFI_SYSTAB (*((unsigned long *)(PARAM+0x1b8)))
> +#define EFI_LOADER_SIG ((unsigned char *)(PARAM+0x1c0))
> +#define EFI_MEMDESC_SIZE (*((unsigned int *) (PARAM+0x1c4)))
> +#define EFI_MEMDESC_VERSION (*((unsigned int *) (PARAM+0x1c8)))
> +#define EFI_MEMMAP_SIZE (*((unsigned int *) (PARAM+0x1cc)))
> +#define EFI_MEMMAP (*((unsigned long *)(PARAM+0x1d0)))
>  #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
>  #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
>  #define SAVED_VIDEO_MODE (*(unsigned short *) (PARAM+0x1FA))
> diff -uprN -X linux-2.6.21rc7-git2-orig/Documentation/dontdiff linux-2.6.21rc7-git2-orig/arch/x86_64/kernel/efi.c linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/kernel/efi.c
> --- linux-2.6.21rc7-git2-orig/arch/x86_64/kernel/efi.c	1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/kernel/efi.c	2007-04-19 13:01:02.000000000 -0700
> @@ -0,0 +1,824 @@

> +extern unsigned long efi_call_phys(void *fp, u64 arg_num, ...);
> +struct efi efi;
> +EXPORT_SYMBOL(efi);
> +struct efi efi_phys __initdata;
> +struct efi_memory_map memmap ;

no space before ;

> +static efi_system_table_t efi_systab __initdata;
> +
> +static unsigned long efi_rt_eflags;
> +static spinlock_t efi_rt_lock = SPIN_LOCK_UNLOCKED;
> +static pgd_t save_pgd;
> +
> +/* Convert SysV calling convention to EFI x86_64 calling convention */
> +
> +static efi_status_t uefi_call_wrapper(void *fp, unsigned long va_num, ...)
> +{
> +	va_list ap;
> +	int i;
> +	unsigned long args[EFI_ARG_NUM_MAX];
> +	unsigned int arg_size,stack_adjust_size;

space after comma.

> +	efi_status_t status;
> +
> +	if (va_num > EFI_ARG_NUM_MAX || va_num<0)	{

	                                va_num < 0) {

> +		return EFI_LOAD_ERROR;
> +	}
> +	if (va_num==0)

	if (va_num == 0)

> +		/* There is no need to convert arguments for void argument. */
> +		__asm__ __volatile__("call *%0;ret;"::"r"(fp));
> +
> +	/* The EFI arguments is stored in an array. Then later on it will be 
> +	 * pushed into stack or passed to registers according to MS ABI.

passed _to_ registers?  passed via or thru registers?

> +	 */
> +	va_start(ap, va_num);
> +	for (i = 0; i < va_num; i++) {
> +		args[i] = va_arg(ap, unsigned long);
> +	}
> +	va_end(ap);
> +	arg_size = va_num*8;

	arg_size = va_num * 8;

> +	stack_adjust_size = (va_num > EFI_REG_ARG_NUM? EFI_REG_ARG_NUM : va_num)*8;

	Please re-read Documentation/CodingStyle.
> +
> +	/* Starting from here, assembly code makes sure all registers used are
> +	 * under controlled by our code itself instead of by gcc.
> +	 */
> +	/* Start converting SysV calling convention to MS calling convention. */
> +	__asm__ __volatile__(
> +		/* 0. Save preserved registers. EFI call may clobbered them. */
> +		"		pushq %%rbp;pushq %%rbx;pushq %%r12;"
> +		"		pushq %%r13;pushq %%r14;pushq %%r15;"
> +		/* 1. Push arguments passed by stack into stack. */
> +		"		mov %1, %%r12;"
> +		"		mov %3, %%r13;"
> +		"		mov %1, %%rax;"
> +		"		dec %%rax;"
> +		"		mov $8, %%bl;"
> +		"		mul %%bl;"
> +		"		add %%rax, %%r13;"
> +		"lstack:"
> +		"		cmp $4, %%r12;"
> +		"		jle lregister;" 
> +		"		pushq (%%r13);"
> +		"		sub $8, %%r13;"
> +		"		dec %%r12;" 
> +		"		jmp lstack;"
> +		/* 2. Move arguments passed by registers into registers.
> +		 *    rdi->rcx, rsi->rdx, rdx->r8, rcx->r9.
> +		 */
> +		"lregister:"	
> +		"		mov %3, %%r14;"
> +		"		mov $0, %%r12;"
> +		"lloadregister:"
> +		"		cmp %1, %%r12;"
> +		"		jge lcall;"
> +		"		mov (%%r14), %%rcx;"
> +		"		inc %%r12;"
> +		"		cmp %1, %%r12;"
> +		"		jge lcall;"
> +		"		mov 8(%%r14), %%rdx;"
> +		"		inc %%r12;"
> +		"		cmp %1, %%r12;"
> +		"		jge lcall;"
> +		"		mov 0x10(%%r14), %%r8;"
> +		"		inc %%r12;"
> +		"		cmp %1, %%r12;"
> +		"		jge lcall;"
> +		"		mov 0x18(%%r14), %%r9;"
> +		/* 3. Save stack space for those register arguments. */
> +		"lcall:				"
> +		"		sub %2, %%rsp;"
> +		/* 4. Save arg_size to r12 which is preserved in EFI call. */
> +		"		mov %4, %%r12;"
> +		/* 5. Call EFI function. */
> +		"		call *%5;"
> +		"		mov %%rax, %0;"
> +		/* 6. Restore stack space reserved for those register
> +		 * arguments.
> +		 */
> +		"		add %%r12, %%rsp;"
> +		/* 7. Restore preserved registers. */
> +		"		popq %%r15;popq %%r14;popq %%r13;"
> +		"		popq %%r12;popq %%rbx;popq %%rbp;"
> +		: "=r"(status)
> +		:"r"((unsigned long)va_num),
> +		 "r"((unsigned long)stack_adjust_size),
> +		 "r"(args),
> +		 "r"((unsigned long)arg_size),
> +		 "r"(fp)
> +		:"rsp","rbx","rax","r11","r12","r13","r14","rcx","rdx","r8","r9" 
> +		);
> +	return status;
> +}
> +
> +static efi_status_t
> +_efi_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending,
> +                                            efi_time_t *tm)

Move 'static efi_status_t' to same line as function name.
Move formal parameters down as needed.

> +{
> +	return uefi_call_wrapper((void*)efi.systab->runtime->get_wakeup_time,
> +					EFI_ARG_NUM_GET_WAKEUP_TIME, 
> +					(u64)enabled,
> +					(u64)pending,
> +					(u64)tm); 
> +}
> +
> +static efi_status_t
> +_efi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr,
> +                                         unsigned long *data_size, void *data)

ditto

> +{
> +	return uefi_call_wrapper((void*)efi.systab->runtime->get_variable,
> +					EFI_ARG_NUM_GET_VARIABLE, 
> +					(u64)name,
> +					(u64)vendor,
> +					(u64)attr,
> +					(u64)data_size,
> +					(u64)data); 
> +}
> +
> +static efi_status_t
> +_efi_get_next_variable(unsigned long *name_size, efi_char16_t *name,
> +                                              efi_guid_t *vendor)

ditto

> +{
> +	return uefi_call_wrapper((void*)efi.systab->runtime->get_next_variable,
> +					EFI_ARG_NUM_GET_NEXT_VARIABLE, 
> +					(u64)name_size,
> +					(u64)name,
> +					(u64)vendor); 
> +}
> +
> +static efi_status_t
> +phys_efi_set_virtual_address_map(unsigned long memory_map_size,
> +				 unsigned long descriptor_size,
> +				 u32 descriptor_version,
> +				 efi_memory_desc_t *virtual_map)

ditto

> +{
> +	efi_status_t status;
> +
> +	efi_call_phys_prelog();
> +	status = efi_call_phys(efi_phys.set_virtual_address_map,
> +					EFI_ARG_NUM_SET_VIRTUAL_ADDRESS_MAP,
> +					(unsigned long)memory_map_size,
> +					(unsigned long)descriptor_size,
> +					(unsigned long)descriptor_version, 
> +					(unsigned long)virtual_map);
> +	efi_call_phys_epilog();
> +	return status;
> +}
> +
> +efi_status_t
> +phys_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)

ditto

> +{
> +
> +	efi_status_t status;
> +
> +	efi_call_phys_prelog();
> +	status = efi_call_phys(efi_phys.get_time,
> +					EFI_ARG_NUM_GET_TIME,
> +					(unsigned long)tm,
> +					(unsigned long)tc);
> +	efi_call_phys_epilog();
> +	return status;
> +}
> +
> +inline int efi_set_rtc_mmss(unsigned long nowtime)
> +{
> +	int real_seconds, real_minutes;
> +	efi_status_t 	status;
> +	efi_time_t 	eft;
> +	efi_time_cap_t 	cap;
> +
> +	spin_lock(&efi_rt_lock);
> +	status = efi.get_time(&eft, &cap);
> +	spin_unlock(&efi_rt_lock);
> +	if (status != EFI_SUCCESS) {
> +		printk("Ooops: efitime: can't read time!\n");

Creative spelling of Ooops.  :)
(also below)

> +		return -1;
> +	}
> +
> +}
> +/*
> + * This should only be used during kernel init and before runtime
> + * services have been remapped, therefore, we'll need to call in physical
> + * mode.  Note, this call isn't used later, so mark it __init.
> + */

Confusing comments being adjacent as they are...

> +/*
> + * This is used during kernel init before runtime
> + * services have been remapped and also during suspend, therefore,
> + * we'll need to call both in physical and virtual modes.
> + */
> +inline unsigned long efi_get_time(void)
> +{
> +	efi_status_t status;
> +	efi_time_t eft;
> +	efi_time_cap_t cap;
> +
> +	if (efi.get_time) {
> +		/* if we are in virtual mode use remapped function */
> + 		status = efi.get_time(&eft, &cap);
> +	} else {
> +		/* we are in physical mode */
> +		status = phys_efi_get_time(&eft, &cap);
> +	}
> +	if (status != EFI_SUCCESS)
> +		printk("Oops: efitime: can't read time status: 0x%lx\n",status);
> +
> +	return mktime(eft.year, eft.month, eft.day, eft.hour,
> +			eft.minute, eft.second);
> +}
> +
> +
> +/* Make EFI runtime code executable */
> +static void
> +phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end)

Fix 'static void'.

> +{
> +	int i = pmd_index(address);
> +
> +	for (; i < PTRS_PER_PMD && address < end; i++, address += PMD_SIZE) {
> +		unsigned long entry;
> +		pmd_t *pmd = pmd_page + pmd_index(address);
> +
> +		entry = pmd_val(*pmd);
> +			entry &= ~_PAGE_NX;
> +		set_pmd(pmd, __pmd(entry));
> +	}
> +}
> +
> +static void
> +phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end)

ditto

> +{ 
> +	int i = pud_index(addr);
> +
> +	for (; i < PTRS_PER_PUD && addr < end; i++, addr += PUD_SIZE ) {
> +		pud_t *pud = pud_page + pud_index(addr);
> +		pmd_t *pmd;
> +
> +		if (pud_val(*pud)) {
> +			pmd = pmd_offset(pud,0);
> +			phys_pmd_init(pmd, addr, end);
> +		}
> +	}
> +}
> +
> +static void change_rt_pmd(unsigned long start, unsigned long end)
> +{ 
> +	unsigned long next; 
> +
> +	start = (unsigned long)__va(start);
> +	end = (unsigned long)__va(end);
> +
> +	for (; start < end; start = next) {
> +		pgd_t *pgd = pgd_offset_k(start);
> +		pud_t *pud;
> +
> +		pud = pud_offset(pgd, start & PGDIR_MASK);
> +		next = start + PGDIR_SIZE;
> +		if (next > end) 
> +			next = end; 
> +		phys_pud_init(pud, __pa(start), __pa(next));
> +	} 
> +	__flush_tlb_all();
> +}
> +/*
> + * We need to map the EFI memory map again after paging_init().
> + */
> +void __init efi_map_memmap(void)
> +{
> +        efi_memory_desc_t *md;
> +        void *p;

Use tab(s) instead of spaces to indent.

> +
> +	memmap.map = __va((unsigned long) memmap.phys_map);
> +	memmap.map_end = memmap.map + (memmap.nr_map * memmap.desc_size);
> +
> +	/* Make EFI runtime code executable */
> +        for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
> +                md = p;
> +               if (md->type == EFI_RUNTIME_SERVICES_CODE && 

Use tabs instead of spaces to indent.

> +			(__supported_pte_mask & _PAGE_NX))
> +			change_rt_pmd(md->phys_addr, md->phys_addr + 
> +					(md->num_pages << EFI_PAGE_SHIFT));
> +	}
> +}

> +/*
> + * This function will switch the EFI runtime services to virtual mode.
> + * Essentially, look through the EFI memmap and map every region that
> + * has the runtime attribute bit set in its memory descriptor and update
> + * that memory descriptor with the virtual address obtained from ioremap().
> + * This enables the runtime services to be called without having to
> + * thunk back into physical mode for every invocation.
> + */
> +void __init efi_enter_virtual_mode(void)
> +{
> +	efi_memory_desc_t *md;
> +	efi_status_t status;
> +	unsigned long end;
> +	void *p;
> +
> +	efi.systab = NULL;
> +	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
> +		md = p;
> +		if (!(md->attribute & EFI_MEMORY_RUNTIME))
> +			continue;
> +		if (md->attribute & EFI_MEMORY_WB)
> +			md->virt_addr = (unsigned long)__va(md->phys_addr);
> +		else if (md->attribute & (EFI_MEMORY_UC | EFI_MEMORY_WC))
> +			md->virt_addr = (unsigned long)ioremap(md->phys_addr,
> +					 md->num_pages << EFI_PAGE_SHIFT);
> +		end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT);
> +		if ((md->phys_addr <= (unsigned long)efi_phys.systab) &&
> +				((unsigned long)efi_phys.systab < end))
> +			efi.systab = (efi_system_table_t *) 
> +					(md->virt_addr - md->phys_addr +
> +					(unsigned long)efi_phys.systab);
> +	}
> +
> +	if (!efi.systab)
> +		BUG();
> +
> +	status = phys_efi_set_virtual_address_map(
> +			memmap.desc_size * memmap.nr_map,
> +			memmap.desc_size,
> +			memmap.desc_version,
> +		       	memmap.phys_map);
> +
> +	if (status != EFI_SUCCESS) {
> +		printk (KERN_ALERT "You are screwed! "
> +			"Unable to switch EFI into virtual mode "
> +			"(status=%lx)\n", status);
> +		panic("EFI call to SetVirtualAddressMap() failed!");
> +	}
> +
> +	/*
> +	 * Now that EFI is in virtual mode, update the function
> +	 * pointers in the runtime service table to the new virtual addresses.
> +	 *
> +	 * Since x86_64 EFI follows MS calling convention, we can not call

	                                                      cannot

> +	 * the services directly. We put a wrapper around the real service
> +	 * calls and call the wrapper directly.
> +	 */
> +
> +}
> +
> +void __init

Wrong line.

> +efi_initialize_iomem_resources(struct resource *code_resource,
> +			       struct resource *data_resource)
> +{
> +}

> diff -uprN -X linux-2.6.21rc7-git2-orig/Documentation/dontdiff linux-2.6.21rc7-git2-orig/arch/x86_64/kernel/efi_stub.S linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/kernel/efi_stub.S
> --- linux-2.6.21rc7-git2-orig/arch/x86_64/kernel/efi_stub.S	1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.21rc7-git2-uefi-finaltest/arch/x86_64/kernel/efi_stub.S	2007-04-19 13:01:02.000000000 -0700
> @@ -0,0 +1,101 @@

> +/*
> + * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
> + * So we'd better save all of them at the beginning of this function and restore
> + * at the end no matter how many we use, because we can not assure EFI runtime

	                                               cannot

> + * service functions will comply with gcc calling convention, too.
> + */

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux