/* * Copyright (c) 2014 Travis Geiselbrecht * * Use of this source code is governed by a MIT-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/MIT */ #include #include #include #include #include #include #include #if WITH_KERNEL_VM #include #endif #define LOCAL_TRACE 0 /* saved by the main entry point in lk */ extern ulong lk_boot_args[4]; static bool boot_args_valid = false; static struct lk_boot_arg *boot_args; #define LK_BOOT_ARG_MAGIC 'lkbt' /* lk style boot args are spread across the 4 incoming argument pointers as follows: * * [0] = LK_BOOT_ARG_MAGIC * [1] = pointer to lk_boot_arg list * [2] = unused * [3] = xor of ULONG_MAX and the previous 3 */ #define LK_BOOT_ARG_TYPE_INITIAL 'args' #define LK_BOOT_ARG_TYPE_COMMAND_LINE 'cmdl' #define LK_BOOT_ARG_TYPE_BOOTIMAGE 'bimg' #define LK_BOOT_ARG_TYPE_END 0 struct lk_boot_arg { uint32_t type; uint32_t len; uint8_t data[]; }; struct lk_boot_arg_bootimage { uint64_t offset; size_t len; char device[]; }; static void *find_end(void *buf, size_t buf_len) { uint8_t *ptr = (uint8_t *)buf; struct lk_boot_arg *arg = (struct lk_boot_arg *)ptr; while (arg->type != LK_BOOT_ARG_TYPE_END) { ptr += sizeof(struct lk_boot_arg) + arg->len; if ((uintptr_t)ptr - (uintptr_t)buf > buf_len) return NULL; arg = (struct lk_boot_arg *)ptr; } return ptr; } status_t bootargs_start(void *buf, size_t buf_len) { if (buf_len < sizeof(struct lk_boot_arg)) return ERR_NO_MEMORY; memset(buf, 0, buf_len); struct lk_boot_arg *arg = (struct lk_boot_arg *)buf; arg->type = LK_BOOT_ARG_TYPE_INITIAL; arg->len = 0; return NO_ERROR; } status_t bootargs_add_command_line(void *buf, size_t buf_len, const char *str) { struct lk_boot_arg *arg = find_end(buf, buf_len); if (!arg) return ERR_NO_MEMORY; arg->type = LK_BOOT_ARG_TYPE_COMMAND_LINE; arg->len = ROUNDUP(strlen(str) + 1, 4); memset(arg->data, 0, arg->len); strcpy((char *)arg->data, str); return NO_ERROR; } status_t bootargs_add_bootimage_pointer(void *buf, size_t buf_len, const char *device, uint64_t offset, size_t len) { struct lk_boot_arg *arg = find_end(buf, buf_len); if (!arg) return ERR_NO_MEMORY; arg->type = LK_BOOT_ARG_TYPE_BOOTIMAGE; size_t string_len = device ? ROUNDUP(strlen(device) + 1, 4) : 0; arg->len = string_len + sizeof(struct lk_boot_arg_bootimage); struct lk_boot_arg_bootimage *bi = (struct lk_boot_arg_bootimage *)arg->data; bi->offset = offset; bi->len = len; if (device) { memset(bi->device, 0, string_len); memcpy(bi->device, device, strlen(device)); } return NO_ERROR; } void bootargs_generate_lk_arg_values(ulong buf, ulong args[4]) { args[0] = LK_BOOT_ARG_MAGIC; args[1] = buf; args[2] = 0; args[3] = ULONG_MAX ^ args[0] ^ args[1] ^ args[2]; } static void bootargs_init_hook(uint level) { LTRACE_ENTRY; /* see if there are any lk style boot arguments here */ if (lk_boot_args[0] != LK_BOOT_ARG_MAGIC) { LTRACEF("failed magic check\n"); return; } if (lk_boot_args[3] != (ULONG_MAX ^ lk_boot_args[0] ^ lk_boot_args[1] ^ lk_boot_args[2])) { LTRACEF("failed checksum\n"); return; } /* parse the boot arg pointer */ #if WITH_KERNEL_VM boot_args = paddr_to_kvaddr(lk_boot_args[1]); #else boot_args = (void *)lk_boot_args[1]; #endif if (!boot_args) { LTRACEF("null or invalid boot pointer\n"); return; } /* see if the initial entry is the right one */ if (boot_args[0].type != LK_BOOT_ARG_TYPE_INITIAL) { LTRACEF("bad initial arg\n"); return; } /* looks good */ boot_args_valid = true; LTRACEF("valid args found\n"); } bool bootargs_are_valid(void) { return boot_args_valid; } static struct lk_boot_arg *find_tag(uint32_t tag) { if (!boot_args_valid) return NULL; struct lk_boot_arg *arg = boot_args; while (arg->type != LK_BOOT_ARG_TYPE_END) { if (arg->type == tag) return arg; arg = (struct lk_boot_arg *)((uintptr_t)arg + sizeof(struct lk_boot_arg) + arg->len); } return NULL; } const char *bootargs_get_command_line(void) { struct lk_boot_arg *arg = find_tag(LK_BOOT_ARG_TYPE_COMMAND_LINE); if (!arg) return NULL; // XXX validate it return (const char *)arg->data; } status_t bootargs_get_bootimage_pointer(uint64_t *offset, size_t *len, const char **device) { struct lk_boot_arg *arg = find_tag(LK_BOOT_ARG_TYPE_BOOTIMAGE); if (!arg) return ERR_NOT_FOUND; // XXX validate it struct lk_boot_arg_bootimage *bi = (struct lk_boot_arg_bootimage *)arg->data; if (device) { if (arg->len != sizeof(struct lk_boot_arg_bootimage)) { /* string is present */ *device = bi->device; } else { *device = NULL; } } if (offset) *offset = bi->offset; if (len) *len = bi->len; return NO_ERROR; } LK_INIT_HOOK(bootargs, bootargs_init_hook, LK_INIT_LEVEL_THREADING);