From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/cmdline.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 tboot/cmdline.c (limited to 'tboot/cmdline.c') diff --git a/tboot/cmdline.c b/tboot/cmdline.c new file mode 100644 index 0000000..cee60a5 --- /dev/null +++ b/tboot/cmdline.c @@ -0,0 +1,606 @@ +/* + * cmdline.c: command line parsing fns + * + * Copyright (c) 2006-2012, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * copy of original command line + * part of tboot measurement (hence in .text section) + */ +__text char g_cmdline[CMDLINE_SIZE] = { 0 }; + +/* Used for kernel command line parameter setup */ +typedef struct { + const char *name; /* set to NULL for last item in list */ + const char *def_val; +} cmdline_option_t; + +#define MAX_VALUE_LEN 64 + +/* + * the option names and default values must be separate from the actual + * params entered + * this allows the names and default values to be part of the MLE measurement + * param_values[] need to be in .bss section so that will get cleared on launch + */ + +/* global option array for command line */ +static const cmdline_option_t g_tboot_cmdline_options[] = { + { "loglvl", "all" }, /* all|err,warn,info|none */ + { "logging", "serial,vga,efi" }, /* vga,serial,memory,efi|none */ + { "serial", "115200,8n1,0x3f8" }, + /* serial=[/][,[,[,[,[,]]]]] */ + { "vga_delay", "0" }, /* # secs */ + { "ap_wake_mwait", "false" }, /* true|false */ + { "pcr_map", "legacy" }, /* legacy|da */ + { "min_ram", "0" }, /* size in bytes | 0 for no min */ + { "call_racm", "false" }, /* true|false|check */ + { "measure_nv", "false" }, /* true|false */ + { "extpol", "sha1" }, /* agile|embedded|sha1|sha256|sm3|... */ + { NULL, NULL } +}; +static char g_tboot_param_values[ARRAY_SIZE(g_tboot_cmdline_options)][MAX_VALUE_LEN]; + +static const cmdline_option_t g_linux_cmdline_options[] = { + { "vga", "" }, + { "mem", "" }, + { NULL, NULL } +}; +static char g_linux_param_values[ARRAY_SIZE(g_linux_cmdline_options)][MAX_VALUE_LEN]; + +typedef struct { + const char *log_name; + uint8_t log_val; +} tb_loglvl_map_t; + +/* map */ +static const tb_loglvl_map_t g_loglvl_map[] = { + { "none", TBOOT_LOG_LEVEL_NONE }, + { "err", TBOOT_LOG_LEVEL_ERR }, + { "warn", TBOOT_LOG_LEVEL_WARN }, + { "info", TBOOT_LOG_LEVEL_INFO }, + { "detail",TBOOT_LOG_LEVEL_DETA }, + { "all", TBOOT_LOG_LEVEL_ALL }, +}; + +static const char* get_option_val(const cmdline_option_t *options, char vals[][MAX_VALUE_LEN], const char *opt_name) +{ + for ( int i = 0; options[i].name != NULL; i++ ) { + if ( strcmp(options[i].name, opt_name) == 0 ) + return vals[i]; + } + printk(TBOOT_ERR"requested unknown option: %s\n", opt_name); + return NULL; +} + +static void cmdline_parse(const char *cmdline, const cmdline_option_t *options, + char vals[][MAX_VALUE_LEN]) +{ + const char *p = cmdline; + int i; + + /* copy default values to vals[] */ + for ( i = 0; options[i].name != NULL; i++ ) { + strncpy(vals[i], options[i].def_val, MAX_VALUE_LEN-1); + vals[i][MAX_VALUE_LEN-1] = '\0'; + } + + if ( p == NULL ) + return; + + /* parse options */ + while ( true ) + { + /* skip whitespace */ + while ( isspace(*p) ) + p++; + if ( *p == '\0' ) + break; + + /* find end of current option */ + const char *opt_start = p; + const char *opt_end = strchr(opt_start, ' '); + if ( opt_end == NULL ) + opt_end = opt_start + strlen(opt_start); + p = opt_end; + + /* find value part; if no value found, use default and continue */ + const char *val_start = strchr(opt_start, '='); + if ( val_start == NULL || val_start > opt_end ) + continue; + val_start++; + + unsigned int opt_name_size = val_start - opt_start - 1; + unsigned int copy_size = opt_end - val_start; + if ( copy_size > MAX_VALUE_LEN - 1 ) + copy_size = MAX_VALUE_LEN - 1; + if ( opt_name_size == 0 || copy_size == 0 ) + continue; + + /* value found, so copy it */ + for ( i = 0; options[i].name != NULL; i++ ) { + if ( strncmp(options[i].name, opt_start, opt_name_size ) == 0 ) { + strncpy(vals[i], val_start, copy_size); + vals[i][copy_size] = '\0'; /* add '\0' to the end of string */ + break; + } + } + } +} + +void tboot_parse_cmdline(bool defaults) +{ + if (!defaults) + cmdline_parse(g_cmdline, g_tboot_cmdline_options, g_tboot_param_values); + else + cmdline_parse(NULL, g_tboot_cmdline_options, g_tboot_param_values); +} + +void linux_parse_cmdline(const char *cmdline) +{ + cmdline_parse(cmdline, g_linux_cmdline_options, g_linux_param_values); +} + +uint8_t get_loglvl_prefix(char **pbuf, int *len) +{ + uint8_t log_level = TBOOT_LOG_LEVEL_ALL; + + if ( *len > 2 && **pbuf == '<' && *(*pbuf+2) == '>' + && isdigit(*(*pbuf+1)) ) { + unsigned int i = *(*pbuf+1) - '0'; + if ( i < ARRAY_SIZE(g_loglvl_map) ) + log_level = g_loglvl_map[i].log_val; + *pbuf += 3; + *len = *len - 3; + } + + return log_level; +} + +void get_tboot_loglvl(void) +{ + const char *loglvl = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "loglvl"); + if ( loglvl == NULL ) + return; + + /* determine whether the target is set explicitly */ + while ( isspace(*loglvl) ) + loglvl++; + + g_log_level = TBOOT_LOG_LEVEL_NONE; + + while ( *loglvl != '\0' ) { + unsigned int i; + + for ( i = 0; i < ARRAY_SIZE(g_loglvl_map); i++ ) { + if ( strncmp(loglvl, g_loglvl_map[i].log_name, + strlen(g_loglvl_map[i].log_name)) == 0 ) { + loglvl += strlen(g_loglvl_map[i].log_name); + + if ( g_loglvl_map[i].log_val == TBOOT_LOG_LEVEL_NONE ) { + g_log_level = TBOOT_LOG_LEVEL_NONE; + return; + } + else { + g_log_level |= g_loglvl_map[i].log_val; + break; + } + } + } + + if ( i == ARRAY_SIZE(g_loglvl_map) ) + break; /* unrecognized, end loop */ + + /* skip ',' */ + if ( *loglvl == ',' ) + loglvl++; + else + break; /* unrecognized, end loop */ + } +} + +void get_tboot_log_targets(void) +{ + const char *targets = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "logging"); + + /* nothing set, leave defaults */ + if ( targets == NULL || *targets == '\0' ) + return; + + /* determine if no targets set explicitly */ + if ( strcmp(targets, "none") == 0 ) { + g_log_targets = TBOOT_LOG_TARGET_NONE; /* print nothing */ + return; + } + + /* else init to nothing and parse the possible targets */ + g_log_targets = TBOOT_LOG_TARGET_NONE; + + while ( *targets != '\0' ) { + if ( strncmp(targets, "memory", 6) == 0 ) { + g_log_targets |= TBOOT_LOG_TARGET_MEMORY; + targets += 6; + } + else if ( strncmp(targets, "serial", 6) == 0 ) { + g_log_targets |= TBOOT_LOG_TARGET_SERIAL; + targets += 6; + } + else if ( strncmp(targets, "vga", 3) == 0 && g_post_ebs ) { + g_log_targets |= TBOOT_LOG_TARGET_VGA; + targets += 3; + } + else if ( strncmp(targets, "efi", 3) == 0 && !g_post_ebs ) { + g_log_targets |= TBOOT_LOG_TARGET_EFI; + targets += 3; + } + else + break; /* unrecognized, end loop */ + + if ( *targets == ',' ) + targets++; + else + break; /* unrecognized, end loop */ + } +} + +static bool parse_pci_bdf(const char **bdf, uint32_t *bus, uint32_t *slot, + uint32_t *func) +{ + *bus = strtoul(*bdf, (char **)bdf, 16); + if ( **bdf != ':' ) + return false; + (*bdf)++; + *slot = strtoul(*bdf, (char **)bdf, 16); + if ( **bdf != '.' ) + return false; + (*bdf)++; + *func = strtoul(*bdf, (char **)bdf, 16); + + return true; +} + +bool g_psbdf_enabled = false; +static bool parse_com_psbdf(const char **bdf) +{ + g_psbdf_enabled = parse_pci_bdf(bdf, + &g_com_port.comc_psbdf.bus, + &g_com_port.comc_psbdf.slot, + &g_com_port.comc_psbdf.func); + + return g_psbdf_enabled; +} + +bool g_pbbdf_enabled = false; +static bool parse_com_pbbdf(const char **bdf) +{ + g_pbbdf_enabled = parse_pci_bdf(bdf, + &g_com_port.comc_pbbdf.bus, + &g_com_port.comc_pbbdf.slot, + &g_com_port.comc_pbbdf.func); + + return g_pbbdf_enabled; +} + +static bool parse_com_fmt(const char **fmt) +{ + /* fmt: <5|6|7|8><0|1> */ + /* default 8n1 */ + uint8_t data_bits = 8; + uint8_t parity = 'n'; + uint8_t stop_bits = 1; + + + /* must specify all values */ + if ( strlen(*fmt) < 3 ) + return false; + + /* data bits */ + if ( **fmt >= '5' && **fmt <= '8' ) + data_bits = **fmt - '0'; + else + return false; + (*fmt)++; + + /* parity */ + if ( **fmt == 'n' || **fmt == 'o' || **fmt == 'e' || **fmt == 'm' || + **fmt == 's' ) + parity = **fmt; + else + return false; + (*fmt)++; + + /* stop bits */ + if ( **fmt == '0' || **fmt == '1' ) + stop_bits = **fmt - '0'; + else + return false; + (*fmt)++; + + g_com_port.comc_fmt = GET_LCR_VALUE(data_bits, stop_bits, parity); + + return true; +} + +static bool parse_serial_param(const char *com) +{ + /* parse baud */ + g_com_port.comc_curspeed = strtoul(com, (char **)&com, 10); + if ( (g_com_port.comc_curspeed < 1200) || + (g_com_port.comc_curspeed > 115200) ) + return false; + + /* parse clock hz */ + if ( *com == '/' ) { + ++com; + g_com_port.comc_clockhz = strtoul(com, (char **)&com, 0) << 4; + if ( g_com_port.comc_clockhz == 0 ) + return false; + } + + /* parse data_bits/parity/stop_bits */ + if ( *com != ',' ) + goto exit; + ++com; + while ( isspace(*com) ) + com++; + if ( !parse_com_fmt(&com) ) + return false; + + /* parse IO base */ + if ( *com != ',' ) + goto exit; + ++com; + g_com_port.comc_port = strtoul(com, (char **)&com, 0); + if ( g_com_port.comc_port == 0 ) + return false; + + /* parse irq */ + if ( *com != ',' ) + goto exit; + ++com; + g_com_port.comc_irq = strtoul(com, (char **)&com, 10); + if ( g_com_port.comc_irq == 0 ) + return false; + + /* parse PCI serial controller bdf */ + if ( *com != ',' ) + goto exit; + ++com; + if ( !parse_com_psbdf(&com) ) + return false; + + /* parse PCI bridge bdf */ + if ( *com != ',' ) + goto exit; + ++com; + if ( !parse_com_pbbdf(&com) ) + return false; + + exit: + return true; +} + +bool get_tboot_serial(void) +{ + const char *serial = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "serial"); + if ( serial == NULL || *serial == '\0' ) + return false; + + return parse_serial_param(serial); +} + +void get_tboot_vga_delay(void) +{ + const char *vga_delay = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "vga_delay"); + if ( vga_delay == NULL ) + return; + + g_vga_delay = strtoul(vga_delay, NULL, 0); +} + +bool get_tboot_prefer_da(void) +{ + const char *value = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "pcr_map"); + if ( value != NULL && strcmp(value, "da") == 0 ) + return true; + + return false; +} + +uint32_t g_min_ram; +void get_tboot_min_ram(void) +{ + const char *min_ram = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "min_ram"); + if ( min_ram == NULL ) + return; + + g_min_ram = strtoul(min_ram, NULL, 0); +} + +bool get_tboot_mwait(void) +{ + const char *mwait = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "ap_wake_mwait"); + if ( mwait == NULL || strcmp(mwait, "false") == 0 ) + return false; + return true; +} + +bool get_tboot_call_racm(void) +{ + const char *call_racm = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "call_racm"); + if ( call_racm == NULL || strcmp(call_racm, "true") != 0 ) + return false; + return true; +} + +bool get_tboot_call_racm_check(void) +{ + const char *call_racm = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "call_racm"); + if ( call_racm == NULL || strcmp(call_racm, "check") != 0 ) + return false; + return true; +} + +bool get_tboot_measure_nv(void) +{ + const char *measure_nv = get_option_val(g_tboot_cmdline_options, + g_tboot_param_values, "measure_nv"); + if ( measure_nv == NULL || strcmp(measure_nv, "true") != 0 ) + return false; + return true; +} + + +void get_tboot_extpol(void) +{ + const char *extpol = get_option_val(g_tboot_cmdline_options, g_tboot_param_values, "extpol"); + + if ( extpol == NULL ) { + g_tpm->extpol = TB_EXTPOL_FIXED; + g_tpm->cur_alg = TB_HALG_SHA256; + return; + } + + if ( strcmp(extpol, "agile") == 0 ) { + g_tpm->extpol = TB_EXTPOL_AGILE; + g_tpm->cur_alg = TB_HALG_SHA256; + } else if ( strcmp(extpol, "embedded") == 0 ) { + g_tpm->extpol = TB_EXTPOL_EMBEDDED; + g_tpm->cur_alg = TB_HALG_SHA256; + } else if ( strcmp(extpol, "sha256") == 0 ) { + g_tpm->extpol = TB_EXTPOL_FIXED; + g_tpm->cur_alg = TB_HALG_SHA256; + } else if ( strcmp(extpol, "sha1") == 0 ) { + g_tpm->extpol = TB_EXTPOL_FIXED; + g_tpm->cur_alg = TB_HALG_SHA1; + } else if ( strcmp(extpol, "sm3") == 0 ) { + g_tpm->extpol = TB_EXTPOL_FIXED; + g_tpm->cur_alg = TB_HALG_SM3; + } +} + +/* + * linux kernel command line parsing + */ + +bool get_linux_vga(int *vid_mode) +{ + const char *vga = get_option_val(g_linux_cmdline_options, + g_linux_param_values, "vga"); + if ( vga == NULL || vid_mode == NULL ) + return false; + + if ( strcmp(vga, "normal") == 0 ) + *vid_mode = 0xFFFF; + else if ( strcmp(vga, "ext") == 0 ) + *vid_mode = 0xFFFE; + else if ( strcmp(vga, "ask") == 0 ) + *vid_mode = 0xFFFD; + else + *vid_mode = strtoul(vga, NULL, 0); + + return true; +} + +bool get_linux_mem(uint64_t *max_mem) +{ + char *last = NULL; + const char *mem = get_option_val(g_linux_cmdline_options, + g_linux_param_values, "mem"); + if ( mem == NULL || max_mem == NULL ) + return false; + + *max_mem = strtoul(mem, &last, 0); + if ( *max_mem == 0 ) + return false; + + if ( last == NULL ) + return true; + + switch ( *last ) { + case 'G': + case 'g': + *max_mem = *max_mem << 30; + return true; + case 'M': + case 'm': + *max_mem = *max_mem << 20; + return true; + case 'K': + case 'k': + *max_mem = *max_mem << 10; + return true; + default: + return false; + } + + return true; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3