From patchwork Mon Jun 10 12:26:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1113096 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (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 45MtXR234cz9s6w for ; Mon, 10 Jun 2019 23:00:23 +1000 (AEST) 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 45MtXR170pzDqP6 for ; Mon, 10 Jun 2019 23:00:23 +1000 (AEST) 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 45MtXJ4YV7zDqJt for ; Mon, 10 Jun 2019 23:00:15 +1000 (AEST) Received: from pps.filterd (m0098394.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x5ACOdLc057611 for ; Mon, 10 Jun 2019 08:27:27 -0400 Received: from e06smtp04.uk.ibm.com (e06smtp04.uk.ibm.com [195.75.94.100]) by mx0a-001b2d01.pphosted.com with ESMTP id 2t1nrpv629-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 10 Jun 2019 08:27:26 -0400 Received: from localhost by e06smtp04.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 10 Jun 2019 13:27:24 +0100 Received: from b06cxnps4074.portsmouth.uk.ibm.com (9.149.109.196) by e06smtp04.uk.ibm.com (192.168.101.134) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 10 Jun 2019 13:27:21 +0100 Received: from d06av23.portsmouth.uk.ibm.com (d06av23.portsmouth.uk.ibm.com [9.149.105.59]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x5ACRKug51511332 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 10 Jun 2019 12:27:20 GMT Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 18445A405E; Mon, 10 Jun 2019 12:27:20 +0000 (GMT) Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 348B1A4055; Mon, 10 Jun 2019 12:27:19 +0000 (GMT) Received: from yorha.ibmuc.com (unknown [9.80.225.168]) by d06av23.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 10 Jun 2019 12:27:18 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Mon, 10 Jun 2019 07:26:45 -0500 X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190610122649.16618-1-erichte@linux.ibm.com> References: <20190610122649.16618-1-erichte@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19061012-0016-0000-0000-00000287BBA4 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19061012-0017-0000-0000-000032E4E47D Message-Id: <20190610122649.16618-4-erichte@linux.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-06-10_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=1 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-1906100087 Subject: [Skiboot] [PATCH 3/7] libstb/secvar: add secvar api implementation 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: , Cc: nayna@linux.ibm.com Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" This patch provides the OPAL runtime service frontend for the host OS to retrieve secure variables, and append new ones for processing on the next reboot. These calls operate on the internal abstraction or utilize the platform-provided driver hooks, and therefore this API should not need to be updated to support changes in storage or backend drivers. Included are the following functions: - opal_secvar_get() - opal_secvar_get_size() - opal_secvar_get_next() - opal_secvar_enqueue_update() - opal_secvar_backend() opal_secvar_get() retrieves the data blob and metadata blob associated with a given key. Either the data blob or the metadata blob are optional. This runtime service only operates on the variable bank. opal_secvar_get_size() returns the size of the data and/or metadata blob associated with a given key. This is a convenience function to retrieve only the size values, and ignore any data blob. This runtime service only operates on the variable bank. opal_secvar_get_next() can be used to iterate through the list of variable keys in the variable bank. Supplying an empty key (or zero key length) returns the key of the first variable in the variable bank. Supplying a valid key returns the key of the next variable in sequence. opal_secvar_enqueue_update() provides a method for the host OS to submit a new variable for processing on next boot, by appending it to the update bank. As this does not affect the variable bank, appending a variable via this runtime service will not affect the output of the previous set of functions. The update queue is only processed during secvar initialization. opal_secvar_backend() returns an enum value for which processing backend is in use. As the backend determines the format of the variables and how they should be updated, the host OS should query this function prior to any other secvar-related operations. The enum value returned from this function will not change at runtime. Signed-off-by: Eric Richter --- ccan/list/list.h | 38 ++++++ include/opal-api.h | 7 +- libstb/secvar/Makefile.inc | 2 +- libstb/secvar/secvar_api.c | 248 +++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 libstb/secvar/secvar_api.c diff --git a/ccan/list/list.h b/ccan/list/list.h index 7cd3a83e..fdeddeb4 100644 --- a/ccan/list/list.h +++ b/ccan/list/list.h @@ -523,4 +523,42 @@ static inline struct list_node *list_node_from_off_(void *ptr, size_t off) (container_off_var(var, member) + \ check_type(var->member, struct list_node)) + +#if HAVE_TYPEOF +#define list_typeof(var) typeof(var) +#else +#define list_typeof(var) void * +#endif + + +/* Returns member, or NULL if at end of list. */ +static inline void *list_entry_or_null(const struct list_head *h, + const struct list_node *n, + size_t off) +{ + if (n == &h->n) + return NULL; + return (char *)n - off; +} + +/** + * list_next - get the next entry in a list + * @h: the list_head + * @i: a pointer to an entry in the list. + * @member: the list_node member of the structure + * + * If @i was the last entry in the list, returns NULL. + * + * Example: + * struct child *second; + * second = list_next(&parent->children, first, list); + * if (!second) + * printf("No second child!\n"); + */ +#define list_next(h, i, member) \ + ((list_typeof(i))list_entry_or_null(list_debug(h), \ + (i)->member.next, \ + list_off_var_((i), member))) + + #endif /* CCAN_LIST_H */ diff --git a/include/opal-api.h b/include/opal-api.h index 0b0ae196..1451cb00 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -232,7 +232,12 @@ #define OPAL_XIVE_GET_VP_STATE 170 /* Get NVT state */ #define OPAL_NPU_MEM_ALLOC 171 #define OPAL_NPU_MEM_RELEASE 172 -#define OPAL_LAST 172 +#define OPAL_SECVAR_GET 173 +#define OPAL_SECVAR_GET_SIZE 174 +#define OPAL_SECVAR_GET_NEXT 175 +#define OPAL_SECVAR_ENQUEUE_UPDATE 176 +#define OPAL_SECVAR_BACKEND 177 +#define OPAL_LAST 177 #define QUIESCE_HOLD 1 /* Spin all calls at entry */ #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */ diff --git a/libstb/secvar/Makefile.inc b/libstb/secvar/Makefile.inc index 75870910..50316b48 100644 --- a/libstb/secvar/Makefile.inc +++ b/libstb/secvar/Makefile.inc @@ -7,7 +7,7 @@ SUBDIRS += $(SECVAR_DIR) include $(SECVAR_DIR)/storage/Makefile.inc include $(SECVAR_DIR)/backend/Makefile.inc -SECVAR_SRCS = secvar_main.c secvar_util.c +SECVAR_SRCS = secvar_main.c secvar_util.c secvar_api.c SECVAR_OBJS = $(SECVAR_SRCS:%.c=%.o) SECVAR = $(SECVAR_DIR)/built-in.a diff --git a/libstb/secvar/secvar_api.c b/libstb/secvar/secvar_api.c new file mode 100644 index 00000000..88275401 --- /dev/null +++ b/libstb/secvar/secvar_api.c @@ -0,0 +1,248 @@ +/* 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) "SECVAR_API: " fmt +#endif + +#include +#include "secvar.h" + + +static int64_t opal_secvar_get(uint64_t k_key, uint64_t k_key_len, uint64_t k_metadata, uint64_t k_metadata_size, uint64_t k_data, uint64_t k_data_size) +{ + struct secvar_node *node; + + char *key = (char *) k_key; + uint64_t key_len = (uint64_t) k_key_len; + void *metadata = (void*) k_metadata; + uint64_t *metadata_size = (uint64_t *) k_metadata_size; + void *data = (void *) k_data; + uint64_t *data_size = (uint64_t *) k_data_size; + + int64_t rc = OPAL_SUCCESS; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!key) + return OPAL_PARAMETER; + if (key_len == 0) + return OPAL_PARAMETER; + // buffer and size must both be valid OR both must be NULL + if ((!!metadata) != (!!metadata_size)) + return OPAL_PARAMETER; + if ((!!data) != (!!data_size)) + return OPAL_PARAMETER; + + node = find_secvar(key, key_len, &variable_bank); + + if (!node) + return OPAL_EMPTY; // Variable not found, bail early + + if (metadata) { + if (*metadata_size < node->var->metadata_size) + rc = OPAL_PARTIAL; + else + memcpy(metadata, node->var->metadata, node->var->metadata_size); + *metadata_size = node->var->metadata_size; + } + + if (data) { + if (*data_size < node->var->data_size) + rc = OPAL_PARTIAL; + else + memcpy(data, node->var->data, node->var->data_size); + *data_size = node->var->data_size; + } + + return rc; +} +opal_call(OPAL_SECVAR_GET, opal_secvar_get, 6); + + +static int64_t opal_secvar_get_size(uint64_t k_key, uint64_t k_key_len, uint64_t k_metadata_size, uint64_t k_data_size) +{ + struct secvar_node *node; + + char *key = (char *) k_key; + uint64_t *metadata_size = (uint64_t *) k_metadata_size; + uint64_t *data_size = (uint64_t *) k_data_size; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!key) + return OPAL_PARAMETER; + if (k_key_len == 0) + return OPAL_PARAMETER; + if ((!metadata_size) && (!data_size)) // Should return at least one of them + return OPAL_PARAMETER; + + node = find_secvar(key, k_key_len, &variable_bank); + if (!node) + return OPAL_EMPTY; + + if (metadata_size) + *metadata_size = node->var->metadata_size; + if (data_size) + *data_size = node->var->data_size; + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_GET_SIZE, opal_secvar_get_size, 4); + + + +static int64_t opal_secvar_get_next(uint64_t k_key, uint64_t k_key_len, uint64_t k_key_size) +{ + struct secvar_node *node; + + char *key = (char *) k_key; + uint64_t *key_len = (uint64_t *) k_key_len; + uint64_t key_size = k_key_size; + int i = 0; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!key_len) + return OPAL_PARAMETER; + if (key_size == 0) + return OPAL_PARAMETER; + if (*key_len > SECVAR_MAX_KEY_LEN) + return OPAL_PARAMETER; + if (*key_len > key_size) + return OPAL_PARAMETER; + if (!key) + return OPAL_PARAMETER; + + // This should fall through if *key_len == 0, otherwise check for empty + for (i = 0; i < *key_len; i++) { + // If the buffer is not empty, we assume it is a key + if (key[i] != 0) { + goto have_key; + } + } + + node = list_top(&variable_bank, struct secvar_node, link); + goto send_var; + +have_key: + // Non-empty string + node = find_secvar(key, *key_len, &variable_bank); + if (!node) + return OPAL_PARAMETER; + + node = list_next(&variable_bank, node, link); + +send_var: + if (!node) + return OPAL_EMPTY; + + if (key_size < node->var->key_len) { + *key_len = node->var->key_len; + return OPAL_PARTIAL; + } + + *key_len = node->var->key_len; + memcpy(key, node->var->key, node->var->key_len); + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_GET_NEXT, opal_secvar_get_next, 3); + +static int64_t opal_secvar_enqueue_update(uint64_t k_key, uint64_t k_key_len, uint64_t k_metadata, uint64_t k_metadata_size, uint64_t k_data, uint64_t k_data_size) +{ + struct secvar_node *node; + + char *key = (char *) k_key; + uint64_t key_len = (uint64_t) k_key_len; + void *metadata = (void*) k_metadata; + uint64_t metadata_size = (uint64_t) k_metadata_size; + void *data = (void *) k_data; + uint64_t data_size = (uint64_t) k_data_size; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!key) + return OPAL_PARAMETER; + if (key_len == 0) + return OPAL_PARAMETER; + if (key_len > SECVAR_MAX_KEY_LEN) + return OPAL_PARAMETER; + if (metadata && (metadata_size > SECVAR_MAX_METADATA_SIZE)) + return OPAL_PARAMETER; + if (!data) + return OPAL_PARAMETER; + if (data_size == 0) + return OPAL_PARAMETER; // we don't support variable deletion this way + if (data_size > SECVAR_MAX_DATA_SIZE) + return OPAL_PARAMETER; + + // Key should not be empty + if (is_key_empty(key, key_len)) { + return OPAL_PARAMETER; + } + + node = zalloc(sizeof(struct secvar_node)); + if (!node) + return OPAL_NO_MEM; + + node->var = zalloc(sizeof(struct secvar)); + if (!node->var) + return OPAL_NO_MEM; + node->var->flags = SECVAR_FLAG_DYN_ALLOC; + + memcpy(node->var->key, key, key_len); + node->var->key_len = key_len; + memcpy(node->var->data, data, data_size); + node->var->data_size = data_size; + + if (metadata) { + memcpy(node->var->metadata, metadata, metadata_size); + node->var->metadata_size = metadata_size; + } + else { + memset(node->var->metadata, 0x00, SECVAR_MAX_METADATA_SIZE); + node->var->metadata_size = 0; + } + + list_add_tail(&update_bank, &node->link); + + if (!secvar_storage.write_bank) + return OPAL_HARDWARE; + + secvar_storage.write_bank(&update_bank, SECVAR_UPDATE_BANK); + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_ENQUEUE_UPDATE, opal_secvar_enqueue_update, 6); + + +static int64_t opal_secvar_backend(uint64_t k_backend) +{ + uint64_t *backend = (uint64_t *) k_backend; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!secvar_backend.version) + return OPAL_UNSUPPORTED; + if (!backend) + return OPAL_PARAMETER; + + *backend = secvar_backend.version; + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_BACKEND, opal_secvar_backend, 1);