diff options
Diffstat (limited to 'discover/platform-powerpc.c')
-rw-r--r-- | discover/platform-powerpc.c | 187 |
1 files changed, 140 insertions, 47 deletions
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index 5d7cc59..c6ab416 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -21,6 +21,7 @@ #include "platform.h" #include "ipmi.h" #include "dt.h" +#include "elf.h" static const char *partition = "common"; static const char *sysparams_dir = "/sys/firmware/opal/sysparams/"; @@ -222,6 +223,13 @@ static void params_update_all(struct param_list *pl, params_update_network_values(pl, "petitboot,network", config); params_update_bootdev_values(pl, "petitboot,bootdevs", config); + + if (config->preboot_check_enabled == defaults->preboot_check_enabled) + val = ""; + else + val = config->preboot_check_enabled ? "true" : "false"; + + param_list_set_non_empty(pl, "petitboot,preboot-check", val, true); } static void config_set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev, @@ -437,10 +445,10 @@ static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform, } static int get_ipmi_boot_mailbox_block(struct platform_powerpc *platform, - char *buf, uint8_t block) + mbox_t *mailbox, uint8_t block) { size_t blocksize = 16; - uint8_t resp[3 + 16]; + ipmi_mbox_response_t ipmi_mbox_resp = { .cc = 0xFF }; uint16_t resp_len; char *debug_buf; int rc; @@ -449,60 +457,71 @@ static int get_ipmi_boot_mailbox_block(struct platform_powerpc *platform, block, /* set selector */ 0x00, /* no block selector */ }; + size_t ipmi_header_len = sizeof(ipmi_mbox_response_t) - sizeof(mbox_t); - resp_len = sizeof(resp); + resp_len = sizeof(ipmi_mbox_response_t); rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS, IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS, req, sizeof(req), - resp, &resp_len, + (uint8_t*)&ipmi_mbox_resp, &resp_len, ipmi_timeout); if (rc) { pb_log("platform: error reading IPMI boot options\n"); return -1; } - if (resp_len < sizeof(resp)) { - if (resp_len < 3) { - pb_log("platform: unexpected length (%d) in " - "boot options mailbox response\n", - resp_len); - return -1; - } - - if (resp_len == 4) { - pb_debug_fn("block %hu empty\n", block); - return 0; - } + if (resp_len > sizeof(ipmi_mbox_response_t)) { + pb_debug("platform: invalid mailbox response size!\n"); + return -1; + } - blocksize = sizeof(resp) - 3; - pb_debug_fn("Mailbox block %hu returns only %zu bytes in block\n", - block, blocksize); + if (resp_len < ipmi_header_len) { + pb_log("platform: unexpected length (%d) in " + "boot options mailbox response\n", + resp_len); + return -1; } - debug_buf = format_buffer(platform, resp, resp_len); + blocksize = resp_len - ipmi_header_len; + pb_debug_fn("Mailbox block %hu returns only %zu bytes in block\n", + block, blocksize); + + debug_buf = format_buffer(platform, (uint8_t*)&ipmi_mbox_resp, resp_len); pb_debug_fn("IPMI bootdev mailbox block %hu:\n%s\n", block, debug_buf); talloc_free(debug_buf); - if (resp[0] != 0) { + if (ipmi_mbox_resp.cc != 0) { pb_log("platform: non-zero completion code %d from IPMI req\n", - resp[0]); + ipmi_mbox_resp.cc); return -1; } /* check for correct parameter version */ - if ((resp[1] & 0xf) != 0x1) { + if ((ipmi_mbox_resp.param_version & 0xf) != 0x1) { pb_log("platform: unexpected version (0x%x) in " - "boot mailbox response\n", resp[0]); + "boot mailbox response\n", ipmi_mbox_resp.param_version); return -1; } /* check for valid paramters */ - if (resp[2] & 0x80) { + if (ipmi_mbox_resp.param_valid & 0x80) { pb_debug("platform: boot mailbox parameters are invalid/locked\n"); return -1; } - memcpy(buf, &resp[3], blocksize); + /* check for block number */ + if (ipmi_mbox_resp.block_selector != block) { + pb_debug("platform: returned boot mailbox block doesn't match " + "requested\n"); + return -1; + } + + if (!blocksize) { + pb_debug_fn("block %hu empty\n", block); + return 0; + } + + memcpy(mailbox, &ipmi_mbox_resp.mbox, blocksize); return blocksize; } @@ -511,12 +530,10 @@ static int get_ipmi_boot_mailbox(struct platform_powerpc *platform, char **buf) { char *mailbox_buffer, *prefix; - const size_t blocksize = 16; - char block_buffer[blocksize]; + mbox_t mailbox; size_t mailbox_size; int content_size; uint8_t i; - int rc; mailbox_buffer = NULL; mailbox_size = 0; @@ -527,15 +544,16 @@ static int get_ipmi_boot_mailbox(struct platform_powerpc *platform, * on higher numbers. */ for (i = 0; i < UCHAR_MAX; i++) { - rc = get_ipmi_boot_mailbox_block(platform, block_buffer, i); - if (rc < 3 && i == 0) { + uint8_t *boot_opt_data; + int block_size = get_ipmi_boot_mailbox_block(platform, &mailbox, i); + if (block_size < CHASSIS_BOOT_MBOX_IANA_SZ && i == 0) { /* * Immediate failure, no blocks read or missing IANA * number. */ return -1; } - if (rc < 1) { + if (block_size < 1) { /* Error or no bytes read */ break; } @@ -543,28 +561,33 @@ static int get_ipmi_boot_mailbox(struct platform_powerpc *platform, if (i == 0) { /* * The first three bytes of block zero are an IANA - * Enterprise ID number. Check it matches the IBM - * number, '2'. + * Enterprise ID number */ - if (block_buffer[0] != 0x02 || - block_buffer[1] != 0x00 || - block_buffer[2] != 0x00) { + block_size -= CHASSIS_BOOT_MBOX_IANA_SZ; + boot_opt_data = &mailbox.b0.data; + + /* Check IANA matches the IBM number, '2' */ + if (mailbox.b0.iana[0] != 0x02 || + mailbox.b0.iana[1] != 0x00 || + mailbox.b0.iana[2] != 0x00) { pb_log_fn("IANA number unrecognised: 0x%x:0x%x:0x%x\n", - block_buffer[0], - block_buffer[1], - block_buffer[2]); + mailbox.b0.iana[0], + mailbox.b0.iana[1], + mailbox.b0.iana[2]); return -1; } + } else { + boot_opt_data = &mailbox.data; } mailbox_buffer = talloc_realloc(platform, mailbox_buffer, - char, mailbox_size + rc); + char, mailbox_size + block_size); if (!mailbox_buffer) { pb_log_fn("Failed to allocate mailbox buffer\n"); return -1; } - memcpy(mailbox_buffer + mailbox_size, block_buffer, rc); - mailbox_size += rc; + memcpy(mailbox_buffer + mailbox_size, boot_opt_data, block_size); + mailbox_size += block_size; } if (i < 5) @@ -574,10 +597,10 @@ static int get_ipmi_boot_mailbox(struct platform_powerpc *platform, else pb_debug_fn("%hu blocks read (%zu bytes)\n", i, mailbox_size); - if (mailbox_size < 3 + strlen("petitboot,bootdevs=")) + if (mailbox_size < strlen("petitboot,bootdevs=")) return -1; - prefix = talloc_strndup(mailbox_buffer, mailbox_buffer + 3, + prefix = talloc_strndup(mailbox_buffer, mailbox_buffer, strlen("petitboot,bootdevs=")); if (!prefix) { pb_log_fn("Couldn't check prefix\n"); @@ -595,9 +618,9 @@ static int get_ipmi_boot_mailbox(struct platform_powerpc *platform, } /* Don't include IANA number in buffer */ - content_size = mailbox_size - 3 - strlen("petitboot,bootdevs="); + content_size = mailbox_size - strlen("petitboot,bootdevs="); *buf = talloc_memdup(platform, - mailbox_buffer + 3 + strlen("petitboot,bootdevs="), + mailbox_buffer + strlen("petitboot,bootdevs="), content_size + 1); (*buf)[content_size] = '\0'; @@ -923,6 +946,72 @@ static void pre_boot(struct platform *p, const struct config *config) platform->set_os_boot_sensor(platform); } +static bool preboot_check(struct platform *p, + const struct config *config, + const char *image, + char **err_msg) +{ + struct platform_powerpc *platform = p->platform_data; + unsigned int *ppc_cap_bitmap = NULL; + bool ultravisor_enabled; + struct stat statbuf; + bool ret = true; + + /* check if ultravisor-system is enabled */ + ultravisor_enabled = stat("/proc/device-tree/ibm,ultravisor", + &statbuf) == 0; + + /* if ultravisor-system is disabled, continue the boot process */ + if (!ultravisor_enabled) + return true; + + ppc_cap_bitmap = elf_getnote_desc(elf_open_image(image), + POWERPC_ELFNOTE_NAMESPACE, + PPC_ELFNOTE_CAPABILITIES); + + if ((ppc_cap_bitmap) && (*ppc_cap_bitmap & PPCCAP_ULTRAVISOR_BIT)) { + pb_debug("kernel capabilities: ultravisor mode found.\n"); + } else { + ret = false; + pb_log_fn("kernel capabilities failed:" + " IBM Ultravisor mode is required.\n"); + *err_msg = talloc_strdup(platform, "IBM Ultravisor capability" + " not found"); + } + free(ppc_cap_bitmap); + + /* if preboot_check is disabled, continue the boot process */ + if (!config->preboot_check_enabled) + return true; + + return ret; +} + +static void get_sysinfo_stb(struct platform_powerpc *platform, + struct system_info *sysinfo) +{ + char *filename; + unsigned int i; + int rc; + struct { + const char *name; + bool *flag; + } props[] = { + { "secure-enabled", &sysinfo->stb_fw_enforcing }, + { "trusted-enabled", &sysinfo->stb_fw_measurement }, + { "os-secureboot-enforcing", &sysinfo->stb_os_enforcing }, + }; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + struct stat statbuf; + filename = talloc_asprintf(platform, "%sibm,secureboot/%s", + devtree_dir, props[i].name); + rc = stat(filename, &statbuf); + *props[i].flag = (rc == 0); + talloc_free(filename); + } +} + static int get_sysinfo(struct platform *p, struct system_info *sysinfo) { struct platform_powerpc *platform = p->platform_data; @@ -951,6 +1040,9 @@ static int get_sysinfo(struct platform *p, struct system_info *sysinfo) if (platform->get_platform_versions) platform->get_platform_versions(sysinfo); + get_sysinfo_stb(platform, sysinfo); + + return 0; } @@ -1028,6 +1120,7 @@ static struct platform platform_powerpc = { .get_sysinfo = get_sysinfo, .restrict_clients = restrict_clients, .set_password = set_password, + .preboot_check = preboot_check, }; register_platform(platform_powerpc); |