Re: [PATCH -v7 1/3] x86 boot: setup data

From: Jeremy Fitzhardinge
Date: Tue Oct 23 2007 - 18:08:48 EST


Huang, Ying wrote:
> This patch add a field of 64-bit physical pointer to NULL terminated
> single linked list of struct setup_data to real-mode kernel
> header. This is used as a more extensible boot parameters passing
> mechanism.
>

As a general comment, I can't say I'm thrilled about sticking the copied
setup data at the end of the initial pagetables. This is already a
fairly complex area, and changing it touches a surprisingly large number
of places. I wonder if there isn't a better way to deal with this
(possibly by fixing up the generation of the initial pagetables in the
process).

Or more simply, define a max size for this data, and copy it into an
initdata area (which, being initdata, can be quite large without wasting
lots of memory).

> Signed-off-by: Huang Ying <ying.huang@xxxxxxxxx>
>
> ---
>
> arch/x86/boot/header.S | 6 +++++-
> arch/x86/kernel/e820_64.c | 6 +++---
> arch/x86/kernel/head64.c | 26 ++++++++++++++++++++++++++
> arch/x86/kernel/head_32.S | 37 ++++++++++++++++++++++++++++++++++++-
> arch/x86/kernel/setup_32.c | 25 +++++++++++++++++++++++--
> arch/x86/kernel/setup_64.c | 22 ++++++++++++++++++++--
> arch/x86/mm/discontig_32.c | 3 ++-
> include/asm-x86/bootparam.h | 12 ++++++++++++
> include/asm-x86/e820_64.h | 1 +
> include/asm-x86/setup_32.h | 7 +++++++
> include/asm-x86/setup_64.h | 2 ++
> 11 files changed, 137 insertions(+), 10 deletions(-)
>
> Index: linux-2.6/include/asm-x86/bootparam.h
> ===================================================================
> --- linux-2.6.orig/include/asm-x86/bootparam.h 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/include/asm-x86/bootparam.h 2007-10-23 10:48:48.000000000 +0800
> @@ -9,6 +9,17 @@
> #include <asm/ist.h>
> #include <video/edid.h>
>
> +/* setup data types */
> +#define SETUP_NONE 0
> +
> +/* extensible setup data list node */
> +struct setup_data {
> + u64 next;
> + u32 type;
> + u32 len;
> + u8 data[0];
> +};
>

What are the alignment rules for this structure? Is it always 64-bit
aligned? What about the relationship of len and data?

> +
> struct setup_header {
> u8 setup_sects;
> u16 root_flags;
> @@ -46,6 +57,7 @@
> u32 cmdline_size;
> u32 hardware_subarch;
> u64 hardware_subarch_data;
> + u64 setup_data;
> } __attribute__((packed));
>
> struct sys_desc_table {
> Index: linux-2.6/arch/x86/boot/header.S
> ===================================================================
> --- linux-2.6.orig/arch/x86/boot/header.S 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/boot/header.S 2007-10-23 13:51:29.000000000 +0800
> @@ -119,7 +119,7 @@
> # Part 2 of the header, from the old setup.S
>
> .ascii "HdrS" # header signature
> - .word 0x0207 # header version number (>= 0x0105)
> + .word 0x0208 # header version number (>= 0x0105)
> # or else old loadlin-1.5 will fail)
> .globl realmode_swtch
> realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
> @@ -219,6 +219,10 @@
>
> hardware_subarch_data: .quad 0
>
> +setup_data: .quad 0 # 64-bit physical pointer to
> + # single linked list of
> + # struct setup_data
> +
> # End of setup header #####################################################
>
> .section ".inittext", "ax"
> Index: linux-2.6/arch/x86/kernel/setup_64.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/setup_64.c 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/kernel/setup_64.c 2007-10-23 10:48:48.000000000 +0800
> @@ -247,6 +247,22 @@
> ebda_size = 64*1024;
> }
>
> +static void __init parse_setup_data(void)
> +{
> + struct setup_data *data;
> +
> + if (boot_params.hdr.version < 0x0207)
> + return;
> + for (data = __va(boot_params.hdr.setup_data);
> + data != __va(0);
> + data = __va(data->next)) {
> + switch (data->type) {
> + default:
> + break;
> + }
> + }
> +}
> +
> void __init setup_arch(char **cmdline_p)
> {
> printk(KERN_INFO "Command line: %s\n", boot_command_line);
> @@ -282,6 +298,8 @@
> strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
> *cmdline_p = command_line;
>
> + parse_setup_data();
> +
> parse_early_param();
>
> finish_e820_parsing();
> @@ -340,9 +358,9 @@
> reserve_bootmem_generic(table_start << PAGE_SHIFT,
> (table_end - table_start) << PAGE_SHIFT);
>
> - /* reserve kernel */
> + /* reserve kernel and setup data */
> reserve_bootmem_generic(__pa_symbol(&_text),
> - __pa_symbol(&_end) - __pa_symbol(&_text));
> + setup_data_end - __pa_symbol(&_text));
>
> /*
> * reserve physical page 0 - it's a special BIOS page on many boxes,
> Index: linux-2.6/arch/x86/kernel/setup_32.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/setup_32.c 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/kernel/setup_32.c 2007-10-23 13:51:05.000000000 +0800
> @@ -66,6 +66,8 @@
> address, and must not be in the .bss segment! */
> unsigned long init_pg_tables_end __initdata = ~0UL;
>
> +unsigned long setup_data_len __initdata;
> +
> int disable_pse __devinitdata = 0;
>
> /*
> @@ -327,7 +329,7 @@
> * partially used pages are not usable - thus
> * we are rounding upwards:
> */
> - min_low_pfn = PFN_UP(init_pg_tables_end);
> + min_low_pfn = PFN_UP(init_pg_tables_end+setup_data_len);
>
> find_max_pfn();
>
> @@ -532,6 +534,22 @@
> return machine_specific_memory_setup();
> }
>
> +static void __init parse_setup_data(void)
> +{
> + struct setup_data *data;
> +
> + if (boot_params.hdr.version < 0x0207)
> + return;
> + for (data = __va(boot_params.hdr.setup_data);
> + data != __va(0);
> + data = __va(data->next)) {
> + switch (data->type) {
> + default:
> + break;
> + }
> + }
> +}
>

Please don't add new duplicated code. Put this into a common place.

> +
> /*
> * Determine if we were loaded by an EFI loader. If so, then we have also been
> * passed the efi memmap, systab, etc., so we should use these data structures
> @@ -579,6 +597,9 @@
> rd_prompt = ((boot_params.hdr.ram_size & RAMDISK_PROMPT_FLAG) != 0);
> rd_doload = ((boot_params.hdr.ram_size & RAMDISK_LOAD_FLAG) != 0);
> #endif
> +
> + parse_setup_data();
> +
> ARCH_SETUP
> if (efi_enabled)
> efi_init();
> @@ -594,7 +615,7 @@
> init_mm.start_code = (unsigned long) _text;
> init_mm.end_code = (unsigned long) _etext;
> init_mm.end_data = (unsigned long) _edata;
> - init_mm.brk = init_pg_tables_end + PAGE_OFFSET;
> + init_mm.brk = init_pg_tables_end + setup_data_len + PAGE_OFFSET;
>
> code_resource.start = virt_to_phys(_text);
> code_resource.end = virt_to_phys(_etext)-1;
> Index: linux-2.6/arch/x86/kernel/e820_64.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/e820_64.c 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/kernel/e820_64.c 2007-10-23 10:01:39.000000000 +0800
> @@ -79,9 +79,9 @@
> }
> }
> #endif
> - /* kernel code */
> - if (last >= __pa_symbol(&_text) && addr < __pa_symbol(&_end)) {
> - *addrp = PAGE_ALIGN(__pa_symbol(&_end));
> + /* kernel code and setup data */
> + if (last >= __pa_symbol(&_text) && addr < setup_data_end) {
> + *addrp = PAGE_ALIGN(setup_data_end);
> return 1;
> }
>
> Index: linux-2.6/arch/x86/kernel/head64.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/head64.c 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/kernel/head64.c 2007-10-23 10:01:39.000000000 +0800
> @@ -19,6 +19,9 @@
> #include <asm/pgtable.h>
> #include <asm/tlbflush.h>
> #include <asm/sections.h>
> +#include <asm/io.h>
> +
> +unsigned long __initdata setup_data_end;
>
> static void __init zap_identity_mappings(void)
> {
> @@ -38,12 +41,35 @@
> static void __init copy_bootdata(char *real_mode_data)
> {
> char * command_line;
> + void *sdata_dst;
> + struct setup_data *sdata;
> + u64 *ppa_next;
> + unsigned long sdata_len;
>
> memcpy(&boot_params, real_mode_data, sizeof boot_params);
> if (boot_params.hdr.cmd_line_ptr) {
> command_line = __va(boot_params.hdr.cmd_line_ptr);
> memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
> }
> +
> +#define SETUP_DATA_ALIGN(addr) \
> + (((unsigned long)(addr)+(SETUP_DATA_ALIGN_SIZE-1))& \
> + ~(SETUP_DATA_ALIGN_SIZE-1))
> +
> + sdata_dst = __va(SETUP_DATA_ALIGN(__pa_symbol(&_end)));
> + ppa_next = &boot_params.hdr.setup_data;
> + while (*ppa_next) {
> + sdata = early_ioremap(*ppa_next, sizeof(*sdata));
> + sdata_len = sizeof(*sdata) + sdata->len;
> + early_iounmap(sdata, sizeof(*sdata));
> + sdata = early_ioremap(*ppa_next, sdata_len);
> + memcpy(sdata_dst, sdata, sdata_len);
> + early_iounmap(sdata, sdata_len);
> + *ppa_next = __pa(sdata_dst);
> + ppa_next = &((struct setup_data *)sdata_dst)->next;
> + sdata_dst = (void *)SETUP_DATA_ALIGN(sdata_dst+sdata_len);
> + }
> + setup_data_end = __pa(sdata_dst);
> }
>
> void __init x86_64_start_kernel(char * real_mode_data)
> Index: linux-2.6/include/asm-x86/e820_64.h
> ===================================================================
> --- linux-2.6.orig/include/asm-x86/e820_64.h 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/include/asm-x86/e820_64.h 2007-10-23 10:01:39.000000000 +0800
> @@ -56,6 +56,7 @@
>
> extern unsigned ebda_addr, ebda_size;
> extern unsigned long nodemap_addr, nodemap_size;
> +extern unsigned long setup_data_end;
> #endif/*!__ASSEMBLY__*/
>
> #endif/*__E820_HEADER*/
> Index: linux-2.6/arch/x86/kernel/head_32.S
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/head_32.S 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/kernel/head_32.S 2007-10-23 10:01:39.000000000 +0800
> @@ -135,6 +135,21 @@
> rep
> movsl
> 1:
> + movl boot_params - __PAGE_OFFSET + SETUP_DATA_POINTER,%ebp
>

You need to check the boot protocol version before looking at this pointer.

> + xorl %ecx,%ecx
> +2:
> + andl %ebp,%ebp
> + jz 1f
> + movl $(SETUP_DATA_HEADER_LEN+SETUP_DATA_ALIGN_SIZE-1),%eax
> + addl (SETUP_DATA_LEN)(%ebp),%eax
> + andl $~(SETUP_DATA_ALIGN_SIZE-1),%eax
> + addl %eax, %ecx
> + movl SETUP_DATA_NEXT(%ebp),%ebp
> + jmp 2b
> +1:
> + addl $(PAGE_SIZE_asm-1),%ecx
> + andl $~(PAGE_SIZE_asm-1),%ecx
> + movl %ecx,(setup_data_len - __PAGE_OFFSET)
>
> #ifdef CONFIG_PARAVIRT
> cmpw $0x207, (boot_params + BP_version - __PAGE_OFFSET)
> @@ -191,13 +206,33 @@
> stosl
> addl $0x1000,%eax
> loop 11b
> - /* End condition: we must map up to and including INIT_MAP_BEYOND_END */
> + /* End condition: we must map up to and including INIT_MAP_BEYOND_END + setup_data_len */
> /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
> leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp
> + addl (setup_data_len - __PAGE_OFFSET),%ebp
> cmpl %ebp,%eax
> jb 10b
> movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
>
> + /* Copy setup data */
> + leal (boot_params - __PAGE_OFFSET + SETUP_DATA_POINTER),%ebx
> +2:
> + movl (%ebx),%ebp
> + andl %ebp,%ebp
> + jz 1f
> + movl $SETUP_DATA_HEADER_LEN,%ecx
> + addl SETUP_DATA_LEN(%ebp),%ecx
> + addl $(SETUP_DATA_ALIGN_SIZE-1),%edi
> + andl $~(SETUP_DATA_ALIGN_SIZE-1),%edi
> + movl %edi,(%ebx)
> + movl %ebp,%esi
> + rep
> + movsb
> + movl (%ebx),%ebx
> + leal SETUP_DATA_NEXT(%ebx),%ebx
> + jmp 2b
> +1:
> +
> xorl %ebx,%ebx /* This is the boot CPU (BSP) */
> jmp 3f
> /*
> Index: linux-2.6/arch/x86/mm/discontig_32.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/mm/discontig_32.c 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/arch/x86/mm/discontig_32.c 2007-10-23 10:01:39.000000000 +0800
> @@ -282,7 +282,8 @@
> kva_pages = calculate_numa_remap_pages();
>
> /* partially used pages are not usable - thus round upwards */
> - system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end);
> + system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end +
> + setup_data_len);
>
> kva_start_pfn = find_max_low_pfn() - kva_pages;
>
> Index: linux-2.6/include/asm-x86/setup_32.h
> ===================================================================
> --- linux-2.6.orig/include/asm-x86/setup_32.h 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/include/asm-x86/setup_32.h 2007-10-23 10:01:39.000000000 +0800
> @@ -25,6 +25,13 @@
> #define OLD_CL_OFFSET 0x90022
> #define NEW_CL_POINTER 0x228 /* Relative to real mode data */
>
> +#define SETUP_DATA_POINTER 0x248 /* Relative to real mode data */
> +
> +#define SETUP_DATA_NEXT 0x0
> +#define SETUP_DATA_LEN 0xc
> +#define SETUP_DATA_HEADER_LEN 0x10
> +#define SETUP_DATA_ALIGN_SIZE 0x8
>

No, use asm-offsets(_32|_64).c

> +
> #ifndef __ASSEMBLY__
>
> #include <asm/bootparam.h>
> Index: linux-2.6/include/asm-x86/setup_64.h
> ===================================================================
> --- linux-2.6.orig/include/asm-x86/setup_64.h 2007-10-23 10:01:35.000000000 +0800
> +++ linux-2.6/include/asm-x86/setup_64.h 2007-10-23 10:03:25.000000000 +0800
> @@ -4,7 +4,9 @@
> #define COMMAND_LINE_SIZE 2048
>
> #ifdef __KERNEL__
> +#include <linux/const.h>
>
> +#define SETUP_DATA_ALIGN_SIZE __AC(0x8, UL)
> #ifndef __ASSEMBLY__
> #include <asm/bootparam.h>
>
> -
> 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/
>

-
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/