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