blob: 2fe528418e9cf8b9e28e16f9c148007948ce0057 [file] [log] [blame]
From 73025c3f822aa19077abe4ece53b03b211faaf52 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/2] 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>
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 | 50 +++++++++++++++++--
11 files changed, 127 insertions(+), 11 deletions(-)
diff --git a/Documentation/x86/zero-page.rst b/Documentation/x86/zero-page.rst
index f088f5881666..3f84244dd573 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 82bc60c8acb2..a325c5dd2700 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -447,6 +447,9 @@ struct boot_params *make_boot_params(struct efi_config *c)
/* 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 c895df5482c5..bb3c2073a63a 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -32,6 +32,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>
@@ -154,7 +157,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 77ea96b794bd..7985d13f8e5b 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -957,16 +957,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 35dbc2791c97..87d1e390fb49 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.
@@ -799,6 +804,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.
@@ -809,14 +828,16 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
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) {
@@ -835,8 +856,14 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
options_bytes++; /* NUL termination */
+
+#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(sys_table_arg, options_bytes, 0,
- &cmdline_addr, MAX_CMDLINE_ADDRESS);
+ &cmdline_addr, MAX_CMDLINE_ADDRESS);
if (status != EFI_SUCCESS)
return NULL;
@@ -846,7 +873,24 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
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(sys_table_arg, 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.24.1