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
  • Loading branch information
francescolavra committed Dec 13, 2023
1 parent c86ca74 commit b0d4838
Show file tree
Hide file tree
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
Expand Up @@ -400,26 +400,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. */
Expand Down Expand Up @@ -490,7 +470,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;
}

Expand All @@ -507,7 +486,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;
Expand Down Expand Up @@ -618,3 +597,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
Expand Up @@ -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>

Expand Down
104 changes: 103 additions & 1 deletion src/kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -473,7 +576,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");
Expand Down
5 changes: 5 additions & 0 deletions src/kernel/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/kernel/region.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand Down
3 changes: 3 additions & 0 deletions src/riscv64/kernel_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
8 changes: 8 additions & 0 deletions src/runtime/memops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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)
{
Expand Down
1 change: 0 additions & 1 deletion src/virtio/virtio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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, '=');
Expand Down Expand Up @@ -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));
}

Expand Down
3 changes: 3 additions & 0 deletions src/x86_64/kernel_machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit b0d4838

Please sign in to comment.