blob: 9febf84caa6b32e0d8b44b9e6b15b62086d17dd4 [file] [log] [blame]
/*
* Copyright (c) 2014 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <lib/bootargs.h>
#include <trace.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <lk/init.h>
#if WITH_KERNEL_VM
#include <kernel/vm.h>
#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);
strcpy(bi->device, 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);