|  | From 12a40099310177c4a494654b592e7f76ec4045f4 Mon Sep 17 00:00:00 2001 | 
|  | From: Matthew Garrett <mjg59@coreos.com> | 
|  | Date: Thu, 9 Apr 2015 13:25:00 -0700 | 
|  | Subject: [PATCH 1/4] x86: Allow built-in command line to work in early kernel | 
|  | init | 
|  |  | 
|  | The kernel supports having a command line built into it. Unfortunately this | 
|  | doesn't work in all cases - the built-in command line is only appended | 
|  | after we've jumped to the kernel proper, but various parts of the early | 
|  | boot process also pay attention to the command line. | 
|  |  | 
|  | This patch moves the command line override code from the kernel itself to | 
|  | the early init code. Unfortunately the kernel can be executed by jumping | 
|  | to the 16-bit entry point, the UEFI entry point, directly to the 32-bit | 
|  | entry point or even to the entry point of the uncompressed image, and | 
|  | there is no guarantee that any of these will have access to data held in | 
|  | the others. As a result, four copies of the command line will be embedded | 
|  | in the kernel. | 
|  |  | 
|  | This patch also defines a new field in boot_params in order to allow the | 
|  | earlier entry points to inform the generic setup code that the command line | 
|  | has already been appended and so shouldn't be added once more. | 
|  |  | 
|  | Updated for Linux 4.19 by Lorenz Brun <lorenz@nexantic.com> | 
|  | Updated for Linux 5.4 by Leopold Schabel <leo@nexantic.com> | 
|  | Updated for Linux 5.6 by Lorenz Brun <lorenz@nexantic.com> | 
|  |  | 
|  | Signed-off-by: Matthew Garrett <mjg59@coreos.com> | 
|  | --- | 
|  | Documentation/x86/zero-page.rst               |  1 + | 
|  | arch/x86/boot/boot.h                          | 10 ++++ | 
|  | arch/x86/boot/cmdline.c                       | 37 ++++++++++++++ | 
|  | arch/x86/boot/compressed/cmdline.c            | 10 ++++ | 
|  | arch/x86/boot/compressed/eboot.c              |  3 ++ | 
|  | arch/x86/boot/compressed/misc.c               |  2 + | 
|  | arch/x86/boot/compressed/misc.h               |  1 + | 
|  | arch/x86/boot/main.c                          |  3 ++ | 
|  | arch/x86/include/uapi/asm/bootparam.h         |  5 +- | 
|  | arch/x86/kernel/setup.c                       | 16 +++--- | 
|  | .../firmware/efi/libstub/efi-stub-helper.c    | 51 +++++++++++++++++-- | 
|  | 11 files changed, 127 insertions(+), 12 deletions(-) | 
|  |  | 
|  | diff --git a/Documentation/x86/zero-page.rst b/Documentation/x86/zero-page.rst | 
|  | index f088f5881666..5339329af8d2 100644 | 
|  | --- a/Documentation/x86/zero-page.rst | 
|  | +++ b/Documentation/x86/zero-page.rst | 
|  | @@ -16,6 +16,7 @@ Offset/Size	Proto	Name			Meaning | 
|  | 000/040		ALL	screen_info		Text mode or frame buffer information | 
|  | (struct screen_info) | 
|  | 040/014		ALL	apm_bios_info		APM BIOS information (struct apm_bios_info) | 
|  | +054/004		ALL	setup_flags			Flags passed from early kernel setup | 
|  | 058/008		ALL	tboot_addr      	Physical address of tboot shared page | 
|  | 060/010		ALL	ist_info		Intel SpeedStep (IST) BIOS support information | 
|  | (struct ist_info) | 
|  | diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h | 
|  | index ca866f1cca2e..a97502baba73 100644 | 
|  | --- a/arch/x86/boot/boot.h | 
|  | +++ b/arch/x86/boot/boot.h | 
|  | @@ -269,6 +269,7 @@ void intcall(u8 int_no, const struct biosregs *ireg, struct biosregs *oreg); | 
|  | /* cmdline.c */ | 
|  | int __cmdline_find_option(unsigned long cmdline_ptr, const char *option, char *buffer, int bufsize); | 
|  | int __cmdline_find_option_bool(unsigned long cmdline_ptr, const char *option); | 
|  | +int __cmdline_init(unsigned long cmdline_ptr, struct boot_params *params); | 
|  | static inline int cmdline_find_option(const char *option, char *buffer, int bufsize) | 
|  | { | 
|  | unsigned long cmd_line_ptr = boot_params.hdr.cmd_line_ptr; | 
|  | @@ -289,6 +290,15 @@ static inline int cmdline_find_option_bool(const char *option) | 
|  | return __cmdline_find_option_bool(cmd_line_ptr, option); | 
|  | } | 
|  |  | 
|  | +static inline int cmdline_init(void) | 
|  | +{ | 
|  | +	unsigned long cmd_line_ptr = boot_params.hdr.cmd_line_ptr; | 
|  | + | 
|  | +	if (cmd_line_ptr >= 0x100000) | 
|  | +		return -1;      /* inaccessible */ | 
|  | + | 
|  | +	return __cmdline_init(cmd_line_ptr, &boot_params); | 
|  | +} | 
|  | /* cpu.c, cpucheck.c */ | 
|  | int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr); | 
|  | int check_knl_erratum(void); | 
|  | diff --git a/arch/x86/boot/cmdline.c b/arch/x86/boot/cmdline.c | 
|  | index 4ff01176c1cc..b5dfbe0b3209 100644 | 
|  | --- a/arch/x86/boot/cmdline.c | 
|  | +++ b/arch/x86/boot/cmdline.c | 
|  | @@ -12,6 +12,10 @@ | 
|  |  | 
|  | #include "boot.h" | 
|  |  | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +static char builtin_cmdline[] = CONFIG_CMDLINE; | 
|  | +#endif | 
|  | + | 
|  | static inline int myisspace(u8 c) | 
|  | { | 
|  | return c <= ' ';	/* Close enough approximation */ | 
|  | @@ -154,3 +158,36 @@ int __cmdline_find_option_bool(unsigned long cmdline_ptr, const char *option) | 
|  |  | 
|  | return 0;	/* Buffer overrun */ | 
|  | } | 
|  | + | 
|  | +int __cmdline_init(unsigned long cmdline_ptr, struct boot_params *params) | 
|  | +{ | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +	addr_t cptr; | 
|  | +	int i = 0; | 
|  | + | 
|  | +	if (!cmdline_ptr) | 
|  | +		return -1;      /* No command line */ | 
|  | + | 
|  | +	set_fs(cmdline_ptr >> 4); | 
|  | +	cptr = cmdline_ptr & 0xf; | 
|  | + | 
|  | +#ifndef CONFIG_CMDLINE_OVERRIDE | 
|  | +	while (cptr < 0x10000) { | 
|  | +		char c = rdfs8(cptr); | 
|  | +		if (!c) { | 
|  | +			wrfs8(' ', cptr++); | 
|  | +			break; | 
|  | +		} | 
|  | +		cptr++; | 
|  | +	} | 
|  | +#endif /* !CONFIG_CMDLINE_OVERRIDE */ | 
|  | +	while (builtin_cmdline[i] && cptr < 0xffff) | 
|  | +		wrfs8(builtin_cmdline[i++], cptr++); | 
|  | + | 
|  | +	wrfs8('\0', cptr); | 
|  | + | 
|  | +	params->setup_flags |= SETUP_CMDLINE_APPENDED; | 
|  | +#endif /* CONFIG_CMDLINE_BOOL */ | 
|  | + | 
|  | +	return 0; | 
|  | +} | 
|  | diff --git a/arch/x86/boot/compressed/cmdline.c b/arch/x86/boot/compressed/cmdline.c | 
|  | index f1add5d85da9..c69b27a5e76d 100644 | 
|  | --- a/arch/x86/boot/compressed/cmdline.c | 
|  | +++ b/arch/x86/boot/compressed/cmdline.c | 
|  | @@ -11,6 +11,10 @@ static inline char rdfs8(addr_t addr) | 
|  | { | 
|  | return *((char *)(fs + addr)); | 
|  | } | 
|  | +static inline void wrfs8(u8 v, addr_t addr) | 
|  | +{ | 
|  | +	*((char *)(fs + addr)) = v; | 
|  | +} | 
|  | #include "../cmdline.c" | 
|  | unsigned long get_cmd_line_ptr(void) | 
|  | { | 
|  | @@ -28,3 +32,9 @@ int cmdline_find_option_bool(const char *option) | 
|  | { | 
|  | return __cmdline_find_option_bool(get_cmd_line_ptr(), option); | 
|  | } | 
|  | +int cmdline_init(void) | 
|  | +{ | 
|  | +	if (!(boot_params->setup_flags & SETUP_CMDLINE_APPENDED)) | 
|  | +		return __cmdline_init(get_cmd_line_ptr(), boot_params); | 
|  | +	return 0; | 
|  | +} | 
|  | diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c | 
|  | index 287393d725f0..6854fe44ac2c 100644 | 
|  | --- a/arch/x86/boot/compressed/eboot.c | 
|  | +++ b/arch/x86/boot/compressed/eboot.c | 
|  | @@ -413,6 +413,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, | 
|  | /* Fill in upper bits of command line address, NOP on 32 bit  */ | 
|  | boot_params->ext_cmd_line_ptr = (u64)(unsigned long)cmdline_ptr >> 32; | 
|  |  | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +	boot_params->setup_flags |= SETUP_CMDLINE_APPENDED; | 
|  | +#endif | 
|  | hdr->ramdisk_image = 0; | 
|  | hdr->ramdisk_size = 0; | 
|  |  | 
|  | diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c | 
|  | index 9652d5c2afda..05fd4372b630 100644 | 
|  | --- a/arch/x86/boot/compressed/misc.c | 
|  | +++ b/arch/x86/boot/compressed/misc.c | 
|  | @@ -366,6 +366,8 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, | 
|  | lines = boot_params->screen_info.orig_video_lines; | 
|  | cols = boot_params->screen_info.orig_video_cols; | 
|  |  | 
|  | +	cmdline_init(); | 
|  | + | 
|  | console_init(); | 
|  |  | 
|  | /* | 
|  | diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h | 
|  | index c8181392f70d..e2f962e7c0d4 100644 | 
|  | --- a/arch/x86/boot/compressed/misc.h | 
|  | +++ b/arch/x86/boot/compressed/misc.h | 
|  | @@ -68,6 +68,7 @@ static inline void debug_puthex(const char *s) | 
|  | /* cmdline.c */ | 
|  | int cmdline_find_option(const char *option, char *buffer, int bufsize); | 
|  | int cmdline_find_option_bool(const char *option); | 
|  | +int cmdline_init(void); | 
|  |  | 
|  | struct mem_vector { | 
|  | unsigned long long start; | 
|  | diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c | 
|  | index e3add857c2c9..e20e81253a7d 100644 | 
|  | --- a/arch/x86/boot/main.c | 
|  | +++ b/arch/x86/boot/main.c | 
|  | @@ -136,6 +136,9 @@ void main(void) | 
|  | /* First, copy the boot header into the "zeropage" */ | 
|  | copy_boot_params(); | 
|  |  | 
|  | +	/* Handle built-in command line */ | 
|  | +	cmdline_init(); | 
|  | + | 
|  | /* Initialize the early-boot console */ | 
|  | console_init(); | 
|  | if (cmdline_find_option_bool("debug")) | 
|  | diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h | 
|  | index 8669c6bdbb84..e11a678d4afa 100644 | 
|  | --- a/arch/x86/include/uapi/asm/bootparam.h | 
|  | +++ b/arch/x86/include/uapi/asm/bootparam.h | 
|  | @@ -37,6 +37,9 @@ | 
|  | #define XLF_5LEVEL			(1<<5) | 
|  | #define XLF_5LEVEL_ENABLED		(1<<6) | 
|  |  | 
|  | +/* setup_flags */ | 
|  | +#define SETUP_CMDLINE_APPENDED		(1<<0) | 
|  | + | 
|  | #ifndef __ASSEMBLY__ | 
|  |  | 
|  | #include <linux/types.h> | 
|  | @@ -175,7 +178,7 @@ struct jailhouse_setup_data { | 
|  | struct boot_params { | 
|  | struct screen_info screen_info;			/* 0x000 */ | 
|  | struct apm_bios_info apm_bios_info;		/* 0x040 */ | 
|  | -	__u8  _pad2[4];					/* 0x054 */ | 
|  | +	__u32 setup_flags;				/* 0x054 */ | 
|  | __u64  tboot_addr;				/* 0x058 */ | 
|  | struct ist_info ist_info;			/* 0x060 */ | 
|  | __u64 acpi_rsdp_addr;				/* 0x070 */ | 
|  | diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c | 
|  | index a74262c71484..6dcc6722ca73 100644 | 
|  | --- a/arch/x86/kernel/setup.c | 
|  | +++ b/arch/x86/kernel/setup.c | 
|  | @@ -903,16 +903,18 @@ void __init setup_arch(char **cmdline_p) | 
|  | bss_resource.end = __pa_symbol(__bss_stop)-1; | 
|  |  | 
|  | #ifdef CONFIG_CMDLINE_BOOL | 
|  | +	if (!(boot_params.setup_flags & SETUP_CMDLINE_APPENDED)) { | 
|  | #ifdef CONFIG_CMDLINE_OVERRIDE | 
|  | -	strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); | 
|  | -#else | 
|  | -	if (builtin_cmdline[0]) { | 
|  | -		/* append boot loader cmdline to builtin */ | 
|  | -		strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE); | 
|  | -		strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE); | 
|  | strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); | 
|  | -	} | 
|  | +#else | 
|  | +		if (builtin_cmdline[0]) { | 
|  | +			/* append boot loader cmdline to builtin */ | 
|  | +			strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE); | 
|  | +			strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE); | 
|  | +			strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); | 
|  | +		} | 
|  | #endif | 
|  | +	} | 
|  | #endif | 
|  |  | 
|  | strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); | 
|  | diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c | 
|  | index 74ddfb496140..5b066819befb 100644 | 
|  | --- a/drivers/firmware/efi/libstub/efi-stub-helper.c | 
|  | +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c | 
|  | @@ -9,9 +9,14 @@ | 
|  |  | 
|  | #include <linux/efi.h> | 
|  | #include <asm/efi.h> | 
|  | +#include <asm/setup.h> | 
|  |  | 
|  | #include "efistub.h" | 
|  |  | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +static char builtin_cmdline[] = CONFIG_CMDLINE; | 
|  | +#endif | 
|  | + | 
|  | /* | 
|  | * Some firmware implementations have problems reading files in one go. | 
|  | * A read chunk size of 1MB seems to work for most platforms. | 
|  | @@ -814,6 +819,20 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) | 
|  | #ifndef MAX_CMDLINE_ADDRESS | 
|  | #define MAX_CMDLINE_ADDRESS	ULONG_MAX | 
|  | #endif | 
|  | +static size_t efi_strlcat(char *dest, const char *src, size_t count) | 
|  | +{ | 
|  | +	size_t dsize = strlen(dest); | 
|  | +	size_t len = strlen(src); | 
|  | +	size_t res = dsize + len; | 
|  | + | 
|  | +	dest += dsize; | 
|  | +	count -= dsize; | 
|  | +	if (len >= count) | 
|  | +		len = count-1; | 
|  | +	memcpy(dest, src, len); | 
|  | +	dest[len] = 0; | 
|  | +	return res; | 
|  | +} | 
|  |  | 
|  | /* | 
|  | * Convert the unicode UEFI command line to ASCII to pass to kernel. | 
|  | @@ -823,14 +842,16 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) | 
|  | char *efi_convert_cmdline(efi_loaded_image_t *image, | 
|  | int *cmd_line_len) | 
|  | { | 
|  | +	unsigned long cmdline_addr = 0; | 
|  | +	int i; | 
|  | +	efi_status_t status; | 
|  | +#ifndef CONFIG_CMDLINE_OVERRIDE | 
|  | const u16 *s2; | 
|  | u8 *s1 = NULL; | 
|  | -	unsigned long cmdline_addr = 0; | 
|  | int load_options_chars = image->load_options_size / 2; /* UTF-16 */ | 
|  | const u16 *options = image->load_options; | 
|  | int options_bytes = 0;  /* UTF-8 bytes */ | 
|  | int options_chars = 0;  /* UTF-16 chars */ | 
|  | -	efi_status_t status; | 
|  | u16 zero = 0; | 
|  |  | 
|  | if (options) { | 
|  | @@ -849,8 +870,13 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, | 
|  |  | 
|  | options_bytes++;	/* NUL termination */ | 
|  |  | 
|  | -	status = efi_high_alloc(options_bytes, 0, &cmdline_addr, | 
|  | -				MAX_CMDLINE_ADDRESS); | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +	/* Add length of the built-in command line, plus a space */ | 
|  | +	options_bytes += strlen(builtin_cmdline); | 
|  | +	options_bytes++; | 
|  | +#endif | 
|  | +	status = efi_high_alloc(options_bytes, 0, | 
|  | +		&cmdline_addr, MAX_CMDLINE_ADDRESS); | 
|  | if (status != EFI_SUCCESS) | 
|  | return NULL; | 
|  |  | 
|  | @@ -860,7 +886,24 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, | 
|  | s1 = efi_utf16_to_utf8(s1, s2, options_chars); | 
|  | *s1 = '\0'; | 
|  |  | 
|  | +#ifdef CONFIG_CMDLINE_BOOL | 
|  | +	efi_strlcat((char *)cmdline_addr, " ", COMMAND_LINE_SIZE); | 
|  | +	efi_strlcat((char *)cmdline_addr, builtin_cmdline, COMMAND_LINE_SIZE); | 
|  | +#endif | 
|  | *cmd_line_len = options_bytes; | 
|  | +#else /* CONFIG_CMDLINE_OVERRIDE */ | 
|  | +	status = efi_high_alloc(strlen(builtin_cmdline), 0, | 
|  | +			       &cmdline_addr, MAX_CMDLINE_ADDRESS); | 
|  | +	if (status != EFI_SUCCESS) | 
|  | +		return NULL; | 
|  | +	while (builtin_cmdline[i] && i < COMMAND_LINE_SIZE) { | 
|  | +		((char *)cmdline_addr)[i] = builtin_cmdline[i]; | 
|  | +		i++; | 
|  | +	} | 
|  | +	((char *)cmdline_addr)[i] = '\0'; | 
|  | +	*cmd_line_len = strlen(builtin_cmdline); | 
|  | +#endif /* CONFIG_CMDLINE_OVERRIDE */ | 
|  | + | 
|  | return (char *)cmdline_addr; | 
|  | } | 
|  |  | 
|  | -- | 
|  | 2.20.1 | 
|  |  |