From patchwork Thu Mar 28 22:17:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1068770 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44VfRj3fRRz9sR7 for ; Fri, 29 Mar 2019 09:19:29 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 44VfRj2Z49zDqTD for ; Fri, 29 Mar 2019 09:19:29 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=linux.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=erichte@linux.ibm.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 44VfQH0c6szDqC8 for ; Fri, 29 Mar 2019 09:18:14 +1100 (AEDT) Received: from pps.filterd (m0098393.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x2SM9xMO089507 for ; Thu, 28 Mar 2019 18:18:13 -0400 Received: from e06smtp07.uk.ibm.com (e06smtp07.uk.ibm.com [195.75.94.103]) by mx0a-001b2d01.pphosted.com with ESMTP id 2rh6q0ghab-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Thu, 28 Mar 2019 18:18:12 -0400 Received: from localhost by e06smtp07.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 28 Mar 2019 22:18:10 -0000 Received: from b06cxnps4076.portsmouth.uk.ibm.com (9.149.109.198) by e06smtp07.uk.ibm.com (192.168.101.137) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Thu, 28 Mar 2019 22:18:07 -0000 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x2SMI59q44368088 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 28 Mar 2019 22:18:05 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id AEE5411C052; Thu, 28 Mar 2019 22:18:05 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 27D8E11C050; Thu, 28 Mar 2019 22:18:05 +0000 (GMT) Received: from yorha.ibmmodules.com (unknown [9.80.235.135]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Thu, 28 Mar 2019 22:18:04 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Thu, 28 Mar 2019 17:17:53 -0500 X-Mailer: git-send-email 2.17.2 In-Reply-To: <20190328221754.20838-1-erichte@linux.ibm.com> References: <20190328221754.20838-1-erichte@linux.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 19032822-0028-0000-0000-00000359BF31 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19032822-0029-0000-0000-0000241881A6 Message-Id: <20190328221754.20838-6-erichte@linux.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-03-28_14:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=4 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1810050000 definitions=main-1903280143 Subject: [Skiboot] [RFC 5/6] libstb: add secvar flash storage implementation for pnor-based p9 platforms X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" This patch implements the platform specific logic for persisting the secure variable storage banks across reboots via the SECBOOT PNOR partition. This is a base implementation meant to provide the minimal functionality required, and is a work-in-progress. For POWER 9, all secure variables and updates are stored in the in the SECBOOT PNOR partition. The partition is split into three sections: two variable bank sections, and a section for storing updates. For this initial implementation, the second variable bank section is ignored, but later patches will alternate writes between them to provide a back up in the event of a failure. Forthcoming patches will extend this implementation to utilize the TPM NV storage space to protect the PNOR storage from external tampering. Signed-off-by: Eric Richter --- libstb/Makefile.inc | 2 +- libstb/secboot_p9.c | 293 ++++++++++++++++++++++++++++++++++++++++++++ libstb/secboot_p9.h | 6 + libstb/secvar.c | 2 + 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 libstb/secboot_p9.c create mode 100644 libstb/secboot_p9.h diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc index f3a7e716..a4da13bb 100644 --- a/libstb/Makefile.inc +++ b/libstb/Makefile.inc @@ -4,7 +4,7 @@ LIBSTB_DIR = libstb SUBDIRS += $(LIBSTB_DIR) -LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c +LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c secboot_p9.c LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o) LIBSTB = $(LIBSTB_DIR)/built-in.a diff --git a/libstb/secboot_p9.c b/libstb/secboot_p9.c new file mode 100644 index 00000000..39942356 --- /dev/null +++ b/libstb/secboot_p9.c @@ -0,0 +1,293 @@ +/* Copyright 2019 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "SECBOOT_P9: " fmt +#endif + +#include +#include +#include "secboot_p9.h" +#include "secvar.h" + +// TODO: Determine reasonable values for these +#define SECBOOT_VARIABLE_BANK_SIZE 32000 +#define SECBOOT_UPDATE_BANK_SIZE 32000 + + +/* 0x5053424b = "PSBK" or Power Secure Boot Keystore */ +#define SECBOOT_MAGIC_NUMBER 0x5053424b +#define SECBOOT_VERSION 1 + +struct secboot_header { + uint32_t magic_number; + uint8_t version; + uint8_t reserved[3]; // Fix alignment +} __packed; + + +struct secboot { + struct secboot_header header; + char bank0[SECBOOT_VARIABLE_BANK_SIZE]; + char bank1[SECBOOT_VARIABLE_BANK_SIZE]; + char update[SECBOOT_UPDATE_BANK_SIZE]; +} __attribute__((packed)); + +struct secboot *secboot_image; + + +static int secboot_format(void) +{ + if (!platform.secboot_write) + return -1; + + memset(secboot_image, 0x00, sizeof(struct secboot)); + + secboot_image->header.magic_number = SECBOOT_MAGIC_NUMBER; + secboot_image->header.version = SECBOOT_VERSION; + + return platform.secboot_write(0, secboot_image, sizeof(struct secboot)); +} + + +static int secboot_serialize_bank(struct list_head *bank, char *target, size_t target_size) +{ + struct secvar *var; + char *tmp = target; + + if (!bank) + return -1; + if (!target) + return -1; + + list_for_each(bank, var, link) { + // Bail early if we are out of storage space + if ((tmp - target) + sizeof_secvar(var) > target_size) { + return -1; + } + memcpy(tmp, var->name, sizeof(var->name)); + tmp += sizeof(var->name); + memcpy(tmp, var->vendor, sizeof(var->vendor)); + tmp += sizeof(var->vendor); + memcpy(tmp, &var->attributes, sizeof(var->attributes)); + tmp += sizeof(var->attributes); + memcpy(tmp, &var->data_size, sizeof(var->data_size)); + tmp += sizeof(var->data_size); + memcpy(tmp, var->data, var->data_size); + tmp += var->data_size; + } + + return 0; +} + + +static int secboot_write_to_pnor(struct list_head *bank, char *target, size_t max_size) +{ + int ret = 0; + + memset(target, 0, max_size); + + ret = secboot_serialize_bank(bank, target, max_size); + if (ret) + return ret; + + if (!platform.secboot_write) { + prlog(PR_ERR, "Failed to write: platform.secboot_write not set\n"); + return -1; + } + + ret = platform.secboot_write(0, secboot_image, sizeof(struct secboot)); + + return ret; +} + + +// Helper function to deserialize a single variable from a PNOR blob +// var should be initialized already +// source should point to the beginning of a variable buffer +// max_size should be how many remaining bytes are in the source buffer +// returns the number of bytes read in +static int secvar_deserialize_variable(struct secvar *var, char *source, size_t max_size) +{ + int size; + char *src = source; + char empty[] = {0,0,0,0}; + + if (!var) + return -1; + + // TODO: Do we want a sequence number, or magic number to signal the start of a variable? + if (!memcmp(source, empty, 4)) { + return 0; + } + + // Ensure the source buffer contains at least the variable header info + size = max_size - + sizeof(var->name) + + sizeof(var->vendor) + + sizeof(var->attributes) + + sizeof(var->data_size); + + if (size < 0) { + return -2; + } + + memcpy(var->name, src, sizeof(var->name)); + src += sizeof(var->name); + memcpy(var->vendor, src, sizeof(var->vendor)); + src += sizeof(var->vendor); + memcpy(&var->attributes, src, sizeof(var->attributes)); + src += sizeof(var->attributes); + memcpy(&var->data_size, src, sizeof(var->data_size)); + src += sizeof(var->data_size); + + if (var->data_size > SECVAR_MAX_DATA_SIZE) { + return -3; + } + // Ensure the source buffer has the expected data size + if (var->data_size > size) { + return -4; + } + + var->data = malloc(var->data_size); + + memcpy(var->data, src, var->data_size); + src += var->data_size; + + return src - source; +} + + +static int secboot_load_from_pnor(struct list_head *bank, char *source, size_t max_size) +{ + char *src; + struct secvar *tmp; + int ret; + uint64_t empty = 0; + + src = source; + + while (src < (source + max_size)) { + // Peek to see if we are at the end of the bank + if (!memcmp(src, &empty, sizeof(empty))) { + break; + } + + tmp = malloc(sizeof(struct secvar)); + if (!tmp) { + prlog(PR_ERR, "Could not allocate memory for loading secvar from image\n"); + return -1; + } + + ret = secvar_deserialize_variable(tmp, src, (max_size - (src - source))); + if (ret == 0) { + free(tmp); + break; + } + else if (ret < 0) + return ret; + + list_add_tail(bank, &tmp->link); + src += ret; + } + + return 0; +} + +// TODO: support bank0 vs bank1 +static int secvar_write_variable_bank_p9(struct list_head *variable_bank) +{ + return secboot_write_to_pnor(variable_bank, secboot_image->bank0, SECBOOT_VARIABLE_BANK_SIZE); +} + +static int secvar_write_update_bank_p9(struct list_head *update_bank) +{ + return secboot_write_to_pnor(update_bank, secboot_image->update, SECBOOT_UPDATE_BANK_SIZE); +} + +static int secvar_load_variable_bank_p9(struct list_head *variable_bank) +{ + return secboot_load_from_pnor(variable_bank, secboot_image->bank0, SECBOOT_VARIABLE_BANK_SIZE); +} + +static int secvar_load_update_bank_p9(struct list_head *update_bank) +{ + return secboot_load_from_pnor(update_bank, secboot_image->update, SECBOOT_UPDATE_BANK_SIZE); +} + + +int secboot_p9_init(void) +{ + int ret; + unsigned secboot_size; + + /* Already initialized */ + if (secboot_image) + return 0; + + if (!platform.secboot_info) + return -1; + + prlog(PR_DEBUG, "Initializing for pnor-based p9 platform\n"); + + ret = platform.secboot_info(&secboot_size); + if (ret) { + prlog(PR_ERR, "error %d retrieving keystore info\n", ret); + return -1; + } + if (sizeof(struct secboot) > secboot_size) { + prlog(PR_ERR, "secboot partition %d KB too small. min=%ld\n", + secboot_size >> 10, sizeof(struct secboot)); + return -1; + } + + secboot_image = memalign(0x1000, sizeof(struct secboot)); + if (!secboot_image) { + prlog(PR_ERR, "Failed to allocate space for the secboot image\n"); + return -1; + } + + /* Read it in */ + ret = platform.secboot_read(secboot_image, 0, sizeof(struct secboot)); + if (ret) { + prlog(PR_ERR, "failed to read the secboot partition, rc=%d\n", ret); + goto out_free; + } + + if (secboot_image->header.magic_number != SECBOOT_MAGIC_NUMBER) { + prlog(PR_INFO, "Formatting secboot partition...\n"); + ret = secboot_format(); + if (ret) { + prlog(PR_ERR, "Failed to format secboot!\n"); + goto out_free; + } + } + + secvar_load_variable_bank = secvar_load_variable_bank_p9; + secvar_load_update_bank = secvar_load_update_bank_p9; + secvar_write_variable_bank = secvar_write_variable_bank_p9; + secvar_write_update_bank = secvar_write_update_bank_p9; + + return 0; + +out_free: + if (secboot_image) { + free(secboot_image); + secboot_image = NULL; + } + + return -1; +} diff --git a/libstb/secboot_p9.h b/libstb/secboot_p9.h new file mode 100644 index 00000000..b3914900 --- /dev/null +++ b/libstb/secboot_p9.h @@ -0,0 +1,6 @@ +#ifndef _SECBOOT_P9_H_ +#define _SECBOOT_P9_H_ + +int secboot_p9_init(void); + +#endif diff --git a/libstb/secvar.c b/libstb/secvar.c index e1bc463f..4b438691 100644 --- a/libstb/secvar.c +++ b/libstb/secvar.c @@ -22,6 +22,7 @@ #include #include #include "secvar.h" +#include "secboot_p9.h" struct list_head variable_bank; struct list_head update_bank; @@ -110,6 +111,7 @@ int secvar_init(void) prlog(PR_DEBUG, "Initializing secure variables\n"); // Initialize platform storage, etc first switch (proc_gen) { + case proc_gen_p9: ret = secboot_p9_init(); break; default: prlog(PR_INFO, "Platform does not support secure boot, skipping...\n"); return 1;