blob: 67aef32dc7e79ebec6cdb6f1e033dda34be7d59d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmdline.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utf_conversion.h>
#include <xefi.h>
#include "osboot.h"
#define CMDLINE_MAX_ITEMS 128
#define CMDLINE_MAX_STRINGDATA (PAGE_SIZE * 3)
static char buffer[CMDLINE_MAX_STRINGDATA];
static size_t buffer_next = 0;
typedef struct {
char* key;
char* val;
size_t klen;
size_t vlen;
} kv_t;
static kv_t entry[CMDLINE_MAX_ITEMS];
static size_t entry_count;
size_t cmdline_to_string(char* ptr, size_t max) {
char* start = ptr;
if (max == 0) {
return 0;
}
for (size_t n = 0; n < entry_count; n++) {
if ((entry[n].klen + entry[n].vlen + 3) > max) {
// require space for: space + key + equal + value + null
break;
}
if (n > 0) {
*ptr++ = ' ';
max--;
}
memcpy(ptr, entry[n].key, entry[n].klen);
ptr += entry[n].klen;
max -= entry[n].klen;
if (entry[n].vlen) {
*ptr++ = '=';
max--;
memcpy(ptr, entry[n].val, entry[n].vlen);
ptr += entry[n].vlen;
max -= entry[n].vlen;
}
}
*ptr++ = 0;
return ptr - start;
}
static void entry_add(const char* key, size_t klen, const char* val, size_t vlen) {
if (klen == 0) {
// empty keys are not allowed
return;
}
if ((klen > 1024) || (vlen > 1024)) {
// huge keys and values are not allowed
return;
}
if ((sizeof(buffer) - buffer_next) < (klen + vlen + 2)) {
// give up if it won't fit
return;
}
size_t n;
for (n = 0; n < entry_count; n++) {
if ((entry[n].klen == klen) && !memcmp(key, entry[n].key, klen)) {
goto write_value;
}
}
if (n == CMDLINE_MAX_ITEMS) {
// no space in table
return;
}
// new entry
entry_count++;
entry[n].key = buffer + buffer_next;
entry[n].klen = klen;
memcpy(entry[n].key, key, klen);
entry[n].key[klen] = 0;
buffer_next += klen + 1;
write_value:
entry[n].val = buffer + buffer_next;
entry[n].vlen = vlen;
memcpy(entry[n].val, val, vlen);
entry[n].val[vlen] = 0;
buffer_next += vlen + 1;
}
void cmdline_set(const char* key, const char* val) {
entry_add(key, strlen(key), val, strlen(val));
}
void cmdline_append(const char* ptr, size_t len) {
const char* key;
const char* val;
restart:
while (len > 0) {
if (isspace(*ptr)) {
ptr++;
len--;
continue;
}
key = ptr;
while (len > 0) {
if (*ptr == '=') {
size_t klen = ptr - key;
ptr++;
len--;
val = ptr;
while ((len > 0) && !isspace(*ptr)) {
len--;
ptr++;
}
size_t vlen = ptr - val;
entry_add(key, klen, val, vlen);
goto restart;
}
if (isspace(*ptr)) {
break;
}
ptr++;
len--;
}
size_t klen = ptr - key;
entry_add(key, klen, NULL, 0);
}
}
// Get any load options from the image and append them to the boot arguments.
void cmdline_append_load_options(void) {
size_t args_len = 0;
efi_status status;
size_t options_len;
void* options;
char16_t* wptr;
uint8_t* args;
uint8_t* ptr;
status = xefi_get_load_options(&options_len, &options);
if (status != EFI_SUCCESS) {
printf("xefi_get_load_options failed: %zu\n", status);
return;
}
args_len = options_len / sizeof(char16_t) + 1;
status = gBS->AllocatePool(EfiLoaderData, args_len, (void**)&args);
if (status != EFI_SUCCESS) {
printf("allocating arg memory failed: %zu\n", status);
goto alloc_fail;
}
wptr = options;
ptr = args;
zx_status_t result;
size_t converted_args_len = args_len;
result = utf16_to_utf8(options, options_len, args, &converted_args_len);
if (result != ZX_OK) {
printf("Could not convert options from UTF16->UTF8: %d\n", result);
goto fail;
}
if (converted_args_len > args_len) {
converted_args_len = args_len;
}
args[converted_args_len] = '\0';
// Skip first argument which is the filename.
ptr = args;
while (*ptr && *ptr != ' ') {
ptr++;
converted_args_len--;
}
while (*ptr && *ptr == ' ') {
ptr++;
converted_args_len--;
}
cmdline_append((char*)ptr, converted_args_len);
fail:
gBS->FreePool(args);
alloc_fail:
gBS->FreePool(options);
}
const char* cmdline_get(const char* key, const char* _default) {
size_t klen = strlen(key);
for (size_t n = 0; n < entry_count; n++) {
if ((entry[n].klen == klen) && !memcmp(key, entry[n].key, klen)) {
return entry[n].val;
}
}
return _default;
}
uint32_t cmdline_get_uint32(const char* key, uint32_t _default) {
const char* val = cmdline_get(key, NULL);
if (val == NULL) {
return _default;
}
return atol(val);
}