Skip to content

Commit

Permalink
Command line: add support for arbitrary configuration settings
Browse files Browse the repository at this point in the history
This patch adds support for changing arbitrary settings in the root
tuple via the kernel command line (which is retrieved by the kernel
when booting under AWS Firecracker on x86).
The PC platform code has been amended by adding a new
REGION_CMDLINE region type holding the location and size of the
command line, which can be accessed later in the boot process. The
existing parsing of the virtio_mmio command line options has been
moved to kernel/init.c, so that it can be called by
virtio_mmio_enum_devs() instead of platform-specific code. The only
platform-specific code is the code that retrieves (and potentially
updates) the command line, while the parsing implementation is in
kernel/init.c.
With these changes, it is possible for example to override the
network settings when starting an instance from a given image
(without modifying the image itself) by specifying those settings
in the kernel command line ("boot_args" parameter in the
Firecracker configuration file), as in the following example:
"en1.ipaddr=10.3.3.6 en1.netmask=255.255.0.0 en1.gateway=10.3.0.1".
In the above example, "en1" identifies the first network interface;
if multiple interfaces are used (en2, en3, etc.), each of them can
be configured independently.
Example to configure a static IPv6 address on the first network
interface:
"en1.ip6addr=20::A8FC:FF:7600:AA"
Example to add an environment variable (or override its value if
the variable is already present in the image):
"environment.VAR_NAME=VAR_VALUE"
Example to modify the program arguments:
"arguments.0=/bin/my-program arguments.1=--my-option"

Closes #1976
francescolavra committed Dec 14, 2023
1 parent 9509c87 commit efd391b
Showing 11 changed files with 153 additions and 26 deletions.
43 changes: 21 additions & 22 deletions platform/pc/service.c
Original file line number Diff line number Diff line change
@@ -389,26 +389,6 @@ static inline void jump_to_virtual(void) {
1: \n");
}

static void cmdline_parse(const char *cmdline)
{
early_init_debug("parsing cmdline");
const char *opt_end, *prefix_end;
while (*cmdline) {
opt_end = runtime_strchr(cmdline, ' ');
if (!opt_end)
opt_end = cmdline + runtime_strlen(cmdline);
prefix_end = runtime_strchr(cmdline, '.');
if (prefix_end && (prefix_end < opt_end)) {
int prefix_len = prefix_end - cmdline;
if ((prefix_len == sizeof("virtio_mmio") - 1) &&
!runtime_memcmp(cmdline, "virtio_mmio", prefix_len))
virtio_mmio_parse(get_kernel_heaps(), prefix_end + 1,
opt_end - (prefix_end + 1));
}
cmdline = opt_end + 1;
}
}

extern void *READONLY_END;

/* Returns the number of pages have been allocated from the temporary page table region. */
@@ -479,7 +459,6 @@ void init_service(u64 rdi, u64 rsi)
*/
assert(u64_from_pointer(params) + cmdline_size < MBR_ADDRESS);
runtime_memcpy(params, cmdline, cmdline_size);
params[cmdline_size] = '\0';
cmdline = (char *)params;
}

@@ -496,7 +475,7 @@ void init_service(u64 rdi, u64 rsi)
init_page_initial_map(pointer_from_u64(PAGES_BASE), initial_pages);
init_kernel_heaps();
if (cmdline)
cmdline_parse(cmdline);
create_region(u64_from_pointer(cmdline), cmdline_size, REGION_CMDLINE);
u64 stack_size = 32*PAGESIZE;
u64 stack_location = allocate_u64((heap)heap_page_backed(get_kernel_heaps()), stack_size);
stack_location += stack_size - STACK_ALIGNMENT;
@@ -607,3 +586,23 @@ void detect_devices(kernel_heaps kh, storage_attach sa)
init_virtio_balloon(kh);
init_virtio_rng(kh);
}

void cmdline_consume(const char *opt_name, cmdline_handler h)
{
for_regions(e) {
if (e->type == REGION_CMDLINE) {
e->length = cmdline_parse(pointer_from_u64(e->base), e->length, opt_name, h);
return;
}
}
}

void boot_params_apply(tuple t)
{
for_regions(e) {
if (e->type == REGION_CMDLINE) {
cmdline_apply(pointer_from_u64(e->base), e->length, t);
return;
}
}
}
3 changes: 3 additions & 0 deletions src/aarch64/kernel_machine.h
Original file line number Diff line number Diff line change
@@ -205,6 +205,9 @@ static inline void wait_for_interrupt(void)
enable_interrupts();
}

#define cmdline_consume(o, h) (void)(h)
#define boot_params_apply(t)

/* locking constructs */
#include <mutex.h>

104 changes: 103 additions & 1 deletion src/kernel/init.c
Original file line number Diff line number Diff line change
@@ -193,6 +193,8 @@ closure_function(2, 2, void, fsstarted,
wrapped_root = tuple_notifier_wrap(fs_root, true);
assert(wrapped_root != INVALID_ADDRESS);
tuple root = (tuple)wrapped_root;
boot_params_apply(root);
reclaim_regions(); /* for pc: no accessing regions after this point */
tuple mounts = get_tuple(root, sym(mounts));
if (mounts)
storage_set_mountpoints(mounts);
@@ -231,6 +233,107 @@ closure_function(2, 2, void, fsstarted,
config_console(root);
}

static char *cmdline_next_option(char *cmdline_start, int cmdline_len, int *opt_len)
{
while (cmdline_len > 0) {
if (*cmdline_start == ' ') {
cmdline_start++;
cmdline_len--;
} else {
break;
}
}
char *opt_end = runtime_memchr(cmdline_start, ' ', cmdline_len);
if (!opt_end)
opt_end = cmdline_start + cmdline_len;
if (opt_end > cmdline_start) {
*opt_len = opt_end - cmdline_start;
return cmdline_start;
}
return 0;
}

static int cmdline_get_prefix(char *opt_start, int opt_len)
{
char *prefix_end = runtime_memchr(opt_start, '.', opt_len);
return (prefix_end ? (prefix_end - opt_start) : 0);
}

/* Removes consumed options from command line; returns updated command line length. */
int cmdline_parse(char *cmdline_start, int cmdline_len, const char *opt_name, cmdline_handler h)
{
init_debug("%s (%d): option %s", __func__, cmdline_len, opt_name);
char *cmdline_end = cmdline_start + cmdline_len;
char *opt_start;
int opt_len;
int name_len = runtime_strlen(opt_name);
char *p = cmdline_start;
while ((opt_start = cmdline_next_option(p, cmdline_end - p, &opt_len))) {
char *opt_end = opt_start + opt_len;
int prefix_len = cmdline_get_prefix(opt_start, opt_len);
if ((prefix_len == name_len) && !runtime_memcmp(opt_start, opt_name, prefix_len)) {
char *value_start = opt_start + prefix_len + 1;
apply(h, value_start, opt_end - value_start);

/* consume parsed option */
runtime_memcpy(opt_start, opt_end, cmdline_end - opt_end);
cmdline_len -= opt_len;
cmdline_end -= opt_len;
p = opt_start;
} else {
p = opt_end;
}
}
init_debug("%s: updated length %d", __func__, cmdline_len);
return cmdline_len;
}

static void cmdline_apply_option(char *opt_start, int opt_len, value cfg)
{
char *name_end = runtime_memchr(opt_start, '=', opt_len);
if (!name_end)
return;
char *opt_end = opt_start + opt_len;
int prefix_len = cmdline_get_prefix(opt_start, opt_len);
if (prefix_len && (prefix_len < name_end - opt_start)) {
symbol s = intern(alloca_wrap_buffer(opt_start, prefix_len));
value child = get(cfg, s);
if (!child) {
child = allocate_tuple();
assert(child != INVALID_ADDRESS);
set(cfg, s, child);
}
if (is_composite(child)) {
char *value_start = opt_start + prefix_len + 1;
cmdline_apply_option(value_start, opt_end - value_start, child);
}
} else {
char *value_start = name_end + 1;
symbol name = intern(alloca_wrap_buffer(opt_start, name_end - opt_start));
string val;
if (value_start < opt_end) {
val = allocate_string(opt_end - value_start);
assert(val != INVALID_ADDRESS);
buffer_append(val, value_start, opt_end - value_start);
} else {
val = 0;
}
set(cfg, name, val);
}
}

void cmdline_apply(char *cmdline_start, int cmdline_len, tuple t)
{
char *opt_start;
int opt_len;
while ((opt_start = cmdline_next_option(cmdline_start, cmdline_len, &opt_len))) {
cmdline_apply_option(opt_start, opt_len, t);
char *opt_end = opt_start + opt_len;
cmdline_len -= opt_end - cmdline_start;
cmdline_start = opt_end;
}
}

#ifdef MM_DEBUG
#define mm_debug(x, ...) do {tprintf(sym(mm), 0, x, ##__VA_ARGS__);} while(0)
#else
@@ -481,7 +584,6 @@ void kernel_runtime_init(kernel_heaps kh)
init_platform_devices(kh);
init_symtab(kh);
read_kernel_syms();
reclaim_regions(); /* for pc: no accessing regions after this point */
shutdown_completions = allocate_vector(locked, SHUTDOWN_COMPLETIONS_SIZE);

init_debug("init_kernel_contexts");
5 changes: 5 additions & 0 deletions src/kernel/kernel.h
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
#include <debug.h>
#endif

typedef closure_type(cmdline_handler, void, const char *, int);

#ifdef KERNEL
void runloop_target(void) __attribute__((noreturn));
#endif
@@ -751,6 +753,9 @@ void kernel_runtime_init(kernel_heaps kh);
void read_kernel_syms(void);
void reclaim_regions(void);

int cmdline_parse(char *cmdline_start, int cmdline_len, const char *opt_name, cmdline_handler h);
void cmdline_apply(char *cmdline_start, int cmdline_len, tuple t);

boolean breakpoint_insert(heap h, u64 a, u8 type, u8 length, thunk completion);
boolean breakpoint_remove(heap h, u32 a, thunk completion);
void destruct_context(context c);
1 change: 1 addition & 0 deletions src/kernel/region.h
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ typedef struct region *region;
#define REGION_PHYSICAL 1 /* available physical memory */
#define REGION_DEVICE 2 /* e820 physical region configured for i/o */
#define REGION_INITIAL_PAGES 10 /* for page table allocations in stage2 and early stage3 */
#define REGION_CMDLINE 11 /* kernel command line */
#define REGION_FILESYSTEM 12 /* offset on disk for the filesystem, see if we can get disk info from the bios */
#define REGION_KERNIMAGE 13 /* location of kernel elf image loaded by stage2 */
#define REGION_RECLAIM 14 /* areas to be unmapped and reclaimed in stage3 (only stage2 stack presently) */
3 changes: 3 additions & 0 deletions src/riscv64/kernel_machine.h
Original file line number Diff line number Diff line change
@@ -135,6 +135,9 @@ static inline void wait_for_interrupt(void)
disable_interrupts();
}

#define cmdline_consume(o, h) (void)(h)
#define boot_params_apply(t)

/* locking constructs */
#include <mutex.h>

8 changes: 8 additions & 0 deletions src/runtime/memops.c
Original file line number Diff line number Diff line change
@@ -204,3 +204,11 @@ int runtime_memcmp(const void *a, const void *b, bytes len)
}
return memcmp_8(a + len - end_len, p_long_b, end_len);
}

void *runtime_memchr(const void *a, int c, bytes len)
{
for (const char *p = a; len > 0; p++, len--)
if (*p == c)
return (void *)p;
return 0;
}
1 change: 1 addition & 0 deletions src/runtime/runtime.h
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@ void runtime_memcpy(void *a, const void *b, bytes len);
void runtime_memset(u8 *a, u8 b, bytes len);

int runtime_memcmp(const void *a, const void *b, bytes len);
void *runtime_memchr(const void *a, int c, bytes len);

static inline int runtime_strlen(const char *a)
{
1 change: 0 additions & 1 deletion src/virtio/virtio.h
Original file line number Diff line number Diff line change
@@ -6,5 +6,4 @@ void init_virtio_rng(kernel_heaps kh);
void init_virtio_scsi(kernel_heaps kh, storage_attach a);
void init_virtio_socket(kernel_heaps kh);

void virtio_mmio_parse(kernel_heaps kh, const char *str, int len);
void virtio_mmio_enum_devs(kernel_heaps kh);
7 changes: 5 additions & 2 deletions src/virtio/virtio_mmio.c
Original file line number Diff line number Diff line change
@@ -51,7 +51,9 @@ closure_function(1, 1, void, vtmmio_new_dev,
list_push_back(&vtmmio_devices, &dev->l);
}

void virtio_mmio_parse(kernel_heaps kh, const char *str, int len)
closure_function(1, 2, void, vtmmio_cmdline_parse,
kernel_heaps, kh,
const char *, str, int, len)
{
buffer b = alloca_wrap_buffer(str, len);
int optname_len = buffer_strchr(b, '=');
@@ -86,12 +88,13 @@ void virtio_mmio_parse(kernel_heaps kh, const char *str, int len)
.memsize = memsize,
.irq = irq,
};
apply(stack_closure(vtmmio_new_dev, kh), &adev);
apply(stack_closure(vtmmio_new_dev, bound(kh)), &adev);
}
}

void virtio_mmio_enum_devs(kernel_heaps kh)
{
cmdline_consume("virtio_mmio", stack_closure(vtmmio_cmdline_parse, kh));
acpi_get_vtmmio_devs(stack_closure(vtmmio_new_dev, kh));
}

3 changes: 3 additions & 0 deletions src/x86_64/kernel_machine.h
Original file line number Diff line number Diff line change
@@ -170,6 +170,9 @@ void install_gdt64_and_tss(void *tss_desc, void *tss, void *gdt, void *gdt_point
#ifdef KERNEL
/* locking constructs */
#include <mutex.h>

void cmdline_consume(const char *opt_name, cmdline_handler h);
void boot_params_apply(tuple t);
#endif

/* device mmio region access */

0 comments on commit efd391b

Please sign in to comment.