From patchwork Wed Mar 3 13:54:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 392657 Delivered-To: patch@linaro.org Received: by 2002:a02:290e:0:0:0:0:0 with SMTP id p14csp5616612jap; Wed, 3 Mar 2021 18:00:49 -0800 (PST) X-Google-Smtp-Source: ABdhPJyO/tBvXbFMMuCunY8W1jZMmrQKVlCFbkB3nFPta80S7NkHkgH36AZX5IZ0sBy2pHfUiGbo X-Received: by 2002:a50:ec81:: with SMTP id e1mr1914448edr.0.1614822839572; Wed, 03 Mar 2021 17:53:59 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1614822839; cv=none; d=google.com; s=arc-20160816; b=q60rDEA1RSilaQ2YN45Gc+xf0iY2Er0MIXq5Pg7rA5BdlfE3o3cGQISuiRZZGWOIGR zVQFzjUR8WBwGiZgiZt8+Qh+Cb+R2i/bEmrxs96LMf0B0r3gRk/Ee0nGWcnJDyDougzP xQtm+yzE2zN3kLyLwhmgIWxjEGrVG9ciLtisvq2s6TPSyN3mvjw7+zFBr0xgfm3sL9X7 5Cmdv6POfHxFMXmMEbsYekjytBpI6RegSi4ThPrb9PPg1jpDbPl9l0BJpVW89K0cQTUd r0bJbKpO+12fXDjNu9H/9+BmnJ3XXL1EXzpqiapOOX9DCsefftyH8YWNo4uufc1IjFPy 35OA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=ladQSxyEYg3GRP4Tkmu6+iNdfdy8fg4VbV+Nnovrbuw=; b=lOOeleIO69abWLIeg7sbonK1qzj8anEpLw9ZvLqS2+9giQiBV1gthFcu9Zz4CZ4iQZ Ng+gNKznPmOVDeWppuXv1/naFQJCho72xkLEEo29zt+I2XLOJOdUgPf8eIznf79kRv8t L52d7cfDYAcQgssCQGiVpK9jaAK8fRJhZTFyl61xKI/dGGdp+Y/k9MCKkXZO3Xi29SUD d3laWH8umFhqmbn3MrPCdkHVN9lCl6V26o5ScKuB9a9yILmRCwbfdqJ1Ppxla2yNCDpW pNS13JFEHHsSMo/lOsHWCqSQzGfQQ2MNVoFZ8Aw9lqrkdGx8qgE1zAseNb3IFcn1JvOO UKpg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=S3mg5NZo; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id p17si16189393ejd.307.2021.03.03.17.53.59; Wed, 03 Mar 2021 17:53:59 -0800 (PST) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=S3mg5NZo; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229688AbhCDAAf (ORCPT + 3 others); Wed, 3 Mar 2021 19:00:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349250AbhCCN4z (ORCPT ); Wed, 3 Mar 2021 08:56:55 -0500 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E4F42C0611BE for ; Wed, 3 Mar 2021 05:55:09 -0800 (PST) Received: by mail-wm1-x32c.google.com with SMTP id k66so6435323wmf.1 for ; Wed, 03 Mar 2021 05:55:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ladQSxyEYg3GRP4Tkmu6+iNdfdy8fg4VbV+Nnovrbuw=; b=S3mg5NZo6X8TmoiBaUwY/t26lZWqF0Nun90I65Os/m/qMYCFTB1k3VrEScsYnjE/9/ 29S+sYTbd+Ya337fggPWBhcmqb18UYhtWA25SbuIeO6gJMAFadKVptvU7owuoGppyJG/ ts1VPtJL0S4TJPVwmk5TH92HXCN1RyOCCRkIt2cXaNJ13Dkhkeg3eNB4fh8acGu51Jdz pxjfi69xPM9kMPAVND6l5qg6yuqN66ta5kKwYDOjn0o6ZSAnI6YAP+ikbVPzy7XznrFz 63L700jpvGXjezKJOaePeXLlTpHBprbKldvM5kqUKmA7wntWs6hlwiZutfsHkwZgQ0uL cFUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ladQSxyEYg3GRP4Tkmu6+iNdfdy8fg4VbV+Nnovrbuw=; b=YO9pUEqqsx39nKliYuJneyFxfktZsWnOhZ9pBUF23QksxVK4nxry6W2o4iiXFFJAPm Nb9PSW3Ubox+jM6Sq/rzVWxW+8Fs6/SzxR08p1CJO1p+N4EZy7pVKO8cNDrjzTBKt74G LtH5GKmkaW+YWeRKC7kvuTqT0fXHBXwwZ8m7vMvk83Gyar+uefGEDMx6z+DXHqJFseI4 XBazSDzjPmr6lOEdeQPTAs4ZyDGjBrUrxZL1xSxAKNjwIEF9B53saTTIM72hCxiaSGj2 wCX2ojfx7k74tsCZM4kZzKxEftRiOys9b+2So06tpelbImNqF7sxqFPSTtnRlS8o58cq La7A== X-Gm-Message-State: AOAM531nVYJ7LUhg4SGFu4TU5DtQIQv0410NLox0WK4m7UJ+Eh4Ok5xD 91aXtQ/jGBq8nwmDZNhsw0ezLw== X-Received: by 2002:a05:600c:21ca:: with SMTP id x10mr950960wmj.48.1614779708508; Wed, 03 Mar 2021 05:55:08 -0800 (PST) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id z9sm32166307wrv.56.2021.03.03.05.55.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Mar 2021 05:55:01 -0800 (PST) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id 9CEE21FF87; Wed, 3 Mar 2021 13:55:00 +0000 (GMT) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ilias.apalodimas@linaro.org, arnd@linaro.org, ruchika.gupta@linaro.org, tomas.winkler@intel.com, yang.huang@intel.com, bing.zhu@intel.com, Matti.Moell@opensynergy.com, hmo@opensynergy.com, linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org, linux-nvme@vger.kernel.org, =?utf-8?q?Alex_Benn=C3=A9e?= , Ulf Hansson , Linus Walleij , Arnd Bergmann Subject: [RFC PATCH 1/5] rpmb: add Replay Protected Memory Block (RPMB) subsystem Date: Wed, 3 Mar 2021 13:54:56 +0000 Message-Id: <20210303135500.24673-2-alex.bennee@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210303135500.24673-1-alex.bennee@linaro.org> References: <20210303135500.24673-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org A number of storage technologies support a specialised hardware partition designed to be resistant to replay attacks. The underlying HW protocols differ but the operations are common. The RPMB partition cannot be accessed via standard block layer, but by a set of specific commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a partition provides authenticated and replay protected access, hence suitable as a secure storage. The RPMB layer aims to provide in-kernel API for Trusted Execution Environment (TEE) devices that are capable to securely compute block frame signature. In case a TEE device wishes to store a replay protected data, requests the storage device via RPMB layer to store the data. A TEE device driver can claim the RPMB interface, for example, via class_interface_register(). The RPMB layer provides a series of operations for interacting with the device. * program_key - a one time operation for setting up a new device * get_capacity - introspect the device capacity * get_write_count - check the write counter * write_blocks - write a series of blocks to the RPMB device * read_blocks - read a series of blocks from the RPMB device The detailed operation of implementing the access is left to the TEE device driver itself. [This is based-on Thomas Winkler's proposed API from: https://lore.kernel.org/linux-mmc/1478548394-8184-2-git-send-email-tomas.winkler@intel.com/ The principle difference is the framing details and HW specific bits (JDEC vs NVME frames) are left to the lower level TEE driver to worry about. The eventual userspace ioctl interface will aim to be similarly generic. This is an RFC to follow up on: Subject: RPMB user space ABI Date: Thu, 11 Feb 2021 14:07:00 +0000 Message-ID: <87mtwashi4.fsf@linaro.org>] Signed-off-by: Alex Bennée Cc: Tomas Winkler Cc: Ulf Hansson Cc: Linus Walleij Cc: Arnd Bergmann Cc: Ilias Apalodimas --- MAINTAINERS | 7 + drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/rpmb/Kconfig | 11 + drivers/char/rpmb/Makefile | 7 + drivers/char/rpmb/core.c | 429 +++++++++++++++++++++++++++++++++++++ include/linux/rpmb.h | 163 ++++++++++++++ 7 files changed, 620 insertions(+) create mode 100644 drivers/char/rpmb/Kconfig create mode 100644 drivers/char/rpmb/Makefile create mode 100644 drivers/char/rpmb/core.c create mode 100644 include/linux/rpmb.h -- 2.20.1 diff --git a/MAINTAINERS b/MAINTAINERS index bfc1b86e3e73..076f3983526c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15369,6 +15369,13 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml F: drivers/media/platform/sunxi/sun8i-rotate/ +RPMB SUBSYSTEM +M: ? +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/char/rpmb/* +F: include/linux/rpmb.h + RTL2830 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d229a2d0c017..a7834cc3e0ea 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -471,6 +471,8 @@ config ADI and SSM (Silicon Secured Memory). Intended consumers of this driver include crash and makedumpfile. +source "drivers/char/rpmb/Kconfig" + endmenu config RANDOM_TRUST_CPU diff --git a/drivers/char/Makefile b/drivers/char/Makefile index ffce287ef415..0eed6e21a7a7 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_RPMB) += rpmb/ diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig new file mode 100644 index 000000000000..431c2823cf70 --- /dev/null +++ b/drivers/char/rpmb/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2019, Intel Corporation. + +config RPMB + tristate "RPMB partition interface" + help + Unified RPMB partition interface for eMMC and UFS. + Provides interface for in kernel security controllers to + access RPMB partition. + + If unsure, select N. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile new file mode 100644 index 000000000000..24d4752a9a53 --- /dev/null +++ b/drivers/char/rpmb/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2019, Intel Corporation. + +obj-$(CONFIG_RPMB) += rpmb.o +rpmb-objs += core.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c new file mode 100644 index 000000000000..a2e21c14986a --- /dev/null +++ b/drivers/char/rpmb/core.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved. + * Copyright(c) 2021 - Linaro Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +static DEFINE_IDA(rpmb_ida); + +/** + * rpmb_dev_get() - increase rpmb device ref counter + * @rdev: rpmb device + */ +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return get_device(&rdev->dev) ? rdev : NULL; +} +EXPORT_SYMBOL_GPL(rpmb_dev_get); + +/** + * rpmb_dev_put() - decrease rpmb device ref counter + * @rdev: rpmb device + */ +void rpmb_dev_put(struct rpmb_dev *rdev) +{ + put_device(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_put); + +/** + * rpmb_program_key() - program the RPMB access key + * @rdev: rpmb device + * @key: key data + * @keylen: length of key data + * + * A successful programming of the key implies it has been set by the + * driver and can be used. + * + * Return: + * * 0 on success + * * -EINVAL on wrong parameters + * * -EPERM key already programmed + * * -EOPNOTSUPP if device doesn't support the requested operation + * * < 0 if the operation fails + */ +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid) +{ + int err; + + if (!rdev || !keyid) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->program_key) { + err = rdev->ops->program_key(rdev->dev.parent, rdev->target, + keyid); + } + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_program_key); + +/** + * rpmb_get_capacity() - returns the capacity of the rpmb device + * @rdev: rpmb device + * + * Return: + * * capacity of the device in units of 128K, on success + * * -EINVAL on wrong parameters + * * -EOPNOTSUPP if device doesn't support the requested operation + * * < 0 if the operation fails + */ +int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + int err; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->get_capacity) + err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target); + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_get_capacity); + +/** + * rpmb_get_write_count() - returns the write counter of the rpmb device + * @rdev: rpmb device + * + * Return: + * * counter + * * -EINVAL on wrong parameters + * * -EOPNOTSUPP if device doesn't support the requested operation + * * < 0 if the operation fails + */ +int rpmb_get_write_count(struct rpmb_dev *rdev) +{ + int err; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->get_write_count) + err = rdev->ops->get_write_count(rdev->dev.parent, rdev->target); + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_get_write_count); + +/** + * rpmb_write_blocks() - write data to RPMB device + * @rdev: rpmb device + * @addr: block address (index of first block - 256B blocks) + * @count: number of 256B blosks + * @data: pointer to data to program + * + * Write a series of blocks to the RPMB device. + * + * Return: + * * 0 on success + * * -EINVAL on wrong parameters + * * -EACCESS no key set + * * -EOPNOTSUPP if device doesn't support the requested operation + * * < 0 if the operation fails + */ +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid, int addr, + int count, u8 *data) +{ + int err; + + if (!rdev || !count || !data) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->write_blocks) { + err = rdev->ops->write_blocks(rdev->dev.parent, rdev->target, keyid, + addr, count, data); + } + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_write_blocks); + +/** + * rpmb_read_blocks() - read data from RPMB device + * @rdev: rpmb device + * @addr: block address (index of first block - 256B blocks) + * @count: number of 256B blocks + * @data: pointer to data to read + * + * Read a series of one or more blocks from the RPMB device. + * + * Return: + * * 0 on success + * * -EINVAL on wrong parameters + * * -EACCESS no key set + * * -EOPNOTSUPP if device doesn't support the requested operation + * * < 0 if the operation fails + */ +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data) +{ + int err; + + if (!rdev || !count || !data) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->read_blocks) { + err = rdev->ops->read_blocks(rdev->dev.parent, rdev->target, + addr, count, data); + } + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_read_blocks); + + +static void rpmb_dev_release(struct device *dev) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + ida_simple_remove(&rpmb_ida, rdev->id); + kfree(rdev); +} + +struct class rpmb_class = { + .name = "rpmb", + .owner = THIS_MODULE, + .dev_release = rpmb_dev_release, +}; +EXPORT_SYMBOL(rpmb_class); + +/** + * rpmb_dev_find_device() - return first matching rpmb device + * @data: data for the match function + * @match: the matching function + * + * Return: matching rpmb device or NULL on failure + */ +static +struct rpmb_dev *rpmb_dev_find_device(const void *data, + int (*match)(struct device *dev, + const void *data)) +{ + struct device *dev; + + dev = class_find_device(&rpmb_class, NULL, data, match); + + return dev ? to_rpmb_dev(dev) : NULL; +} + +struct device_with_target { + const struct device *dev; + u8 target; +}; + +static int match_by_parent(struct device *dev, const void *data) +{ + const struct device_with_target *d = data; + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return (d->dev && dev->parent == d->dev && rdev->target == d->target); +} + +/** + * rpmb_dev_find_by_device() - retrieve rpmb device from the parent device + * @parent: parent device of the rpmb device + * @target: RPMB target/region within the physical device + * + * Return: NULL if there is no rpmb device associated with the parent device + */ +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target) +{ + struct device_with_target t; + + if (!parent) + return NULL; + + t.dev = parent; + t.target = target; + + return rpmb_dev_find_device(&t, match_by_parent); +} +EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device); + +/** + * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem + * @rdev: the rpmb device to unregister + * Return: + * * 0 on success + * * -EINVAL on wrong parameters + */ +int rpmb_dev_unregister(struct rpmb_dev *rdev) +{ + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + device_del(&rdev->dev); + mutex_unlock(&rdev->lock); + + rpmb_dev_put(rdev); + + return 0; +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister); + +/** + * rpmb_dev_unregister_by_device() - unregister RPMB partition + * from the RPMB subsystem + * @dev: the parent device of the rpmb device + * @target: RPMB target/region within the physical device + * Return: + * * 0 on success + * * -EINVAL on wrong parameters + * * -ENODEV if a device cannot be find. + */ +int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + struct rpmb_dev *rdev; + + if (!dev) + return -EINVAL; + + rdev = rpmb_dev_find_by_device(dev, target); + if (!rdev) { + dev_warn(dev, "no disk found %s\n", dev_name(dev->parent)); + return -ENODEV; + } + + rpmb_dev_put(rdev); + + return rpmb_dev_unregister(rdev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device); + +/** + * rpmb_dev_get_drvdata() - driver data getter + * @rdev: rpmb device + * + * Return: driver private data + */ +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return dev_get_drvdata(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata); + +/** + * rpmb_dev_set_drvdata() - driver data setter + * @rdev: rpmb device + * @data: data to store + */ +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ + dev_set_drvdata(&rdev->dev, data); +} +EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata); + +/** + * rpmb_dev_register - register RPMB partition with the RPMB subsystem + * @dev: storage device of the rpmb device + * @target: RPMB target/region within the physical device + * @ops: device specific operations + * + * Return: a pointer to rpmb device + */ +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops) +{ + struct rpmb_dev *rdev; + int id; + int ret; + + if (!dev || !ops) + return ERR_PTR(-EINVAL); + + if (!ops->program_key) + return ERR_PTR(-EINVAL); + + if (!ops->get_capacity) + return ERR_PTR(-EINVAL); + + if (!ops->get_write_count) + return ERR_PTR(-EINVAL); + + if (!ops->write_blocks) + return ERR_PTR(-EINVAL); + + if (!ops->read_blocks) + return ERR_PTR(-EINVAL); + + if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX) + return ERR_PTR(-EINVAL); + + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return ERR_PTR(-ENOMEM); + + id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto exit; + } + + mutex_init(&rdev->lock); + rdev->ops = ops; + rdev->id = id; + rdev->target = target; + + dev_set_name(&rdev->dev, "rpmb%d", id); + rdev->dev.class = &rpmb_class; + rdev->dev.parent = dev; + ret = device_register(&rdev->dev); + if (ret) + goto exit; + + dev_dbg(&rdev->dev, "registered device\n"); + + return rdev; + +exit: + if (id >= 0) + ida_simple_remove(&rpmb_ida, id); + kfree(rdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rpmb_dev_register); + +static int __init rpmb_init(void) +{ + ida_init(&rpmb_ida); + class_register(&rpmb_class); + return 0; +} + +static void __exit rpmb_exit(void) +{ + class_unregister(&rpmb_class); + ida_destroy(&rpmb_ida); +} + +subsys_initcall(rpmb_init); +module_exit(rpmb_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("RPMB class"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h new file mode 100644 index 000000000000..718ba7c91ecd --- /dev/null +++ b/include/linux/rpmb.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2019 Intel Corp. All rights reserved + * Copyright (C) 2021 Linaro Ltd + */ +#ifndef __RPMB_H__ +#define __RPMB_H__ + +#include +#include +#include +#include + +/** + * struct rpmb_ops - RPMB ops to be implemented by underlying block device + * + * @program_key : program device key (once only op). + * @get_capacity : rpmb size in 128K units in for region/target. + * @get_write_count: return the device write counter + * @write_blocks : write blocks to RPMB device + * @read_blocks : read blocks from RPMB device + * @block_size : block size in half sectors (1 == 256B) + * @wr_cnt_max : maximal number of blocks that can be + * written in one access. + * @rd_cnt_max : maximal number of blocks that can be + * read in one access. + * @auth_method : rpmb_auth_method + * @dev_id : unique device identifier + * @dev_id_len : unique device identifier length + */ +struct rpmb_ops { + int (*program_key)(struct device *dev, u8 target, key_serial_t keyid); + int (*get_capacity)(struct device *dev, u8 target); + int (*get_write_count)(struct device *dev, u8 target); + int (*write_blocks)(struct device *dev, u8 target, key_serial_t keyid, + int addr, int count, u8 *data); + int (*read_blocks)(struct device *dev, u8 target, + int addr, int count, u8 *data); + u16 block_size; + u16 wr_cnt_max; + u16 rd_cnt_max; + u16 auth_method; + const u8 *dev_id; + size_t dev_id_len; +}; + +/** + * struct rpmb_dev - device which can support RPMB partition + * + * @lock : the device lock + * @dev : device + * @id : device id + * @target : RPMB target/region within the physical device + * @ops : operation exported by block layer + */ +struct rpmb_dev { + struct mutex lock; /* device serialization lock */ + struct device dev; + int id; + u8 target; + const struct rpmb_ops *ops; +}; + +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev) + +#if IS_ENABLED(CONFIG_RPMB) +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev); +void rpmb_dev_put(struct rpmb_dev *rdev); +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target); +struct rpmb_dev *rpmb_dev_get_by_type(u32 type); +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops); +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev); +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data); +int rpmb_dev_unregister(struct rpmb_dev *rdev); +int rpmb_dev_unregister_by_device(struct device *dev, u8 target); +int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid); +int rpmb_get_capacity(struct rpmb_dev *rdev); +int rpmb_get_write_count(struct rpmb_dev *rdev); +int rpmb_write_blocks(struct rpmb_dev *rdev, key_serial_t keyid, + int addr, int count, u8 *data); +int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, u8 *data); + +#else +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { } + +static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, + u8 target) +{ + return NULL; +} + +static inline +struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type) +{ + return NULL; +} + +static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ +} + +static inline struct rpmb_dev * +rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops) +{ + return NULL; +} + +static inline int rpmb_dev_unregister(struct rpmb_dev *dev) +{ + return 0; +} + +static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + return 0; +} + +static inline int rpmb_program_key(struct rpmb_dev *rdev, key_serial_t keyid) +{ + return 0; +} + +static inline rpmb_set_key(struct rpmb_dev *rdev, u8 *key, int keylen); +{ + return 0; +} + +static inline int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + return 0; +} + +static inline int rpmb_get_write_count(struct rpmb_dev *rdev) +{ + return 0; +} + +static inline int rpmb_write_blocks(struct rpmb_dev *rdev, int addr, int count, + u8 *data) +{ + return 0; +} + +static inline int rpmb_read_blocks(struct rpmb_dev *rdev, int addr, int count, + u8 *data) +{ + return 0; +} + +#endif /* CONFIG_RPMB */ + +#endif /* __RPMB_H__ */ From patchwork Wed Mar 3 13:54:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 392654 Delivered-To: patch@linaro.org Received: by 2002:a02:290e:0:0:0:0:0 with SMTP id p14csp5615683jap; Wed, 3 Mar 2021 17:59:14 -0800 (PST) X-Google-Smtp-Source: ABdhPJzE8hkUPaaT3JiJEYiUTtkEiRmPZmXil2t72CT/NfCkd3sPdN1w4VXssNahKdcCrIhz6pDa X-Received: by 2002:a17:907:10c1:: with SMTP id rv1mr1672554ejb.5.1614822835294; Wed, 03 Mar 2021 17:53:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1614822835; cv=none; d=google.com; s=arc-20160816; b=L+bGFajIlWpDLHei8UuvnXwvqAGbSzvkg+8VKeICO+9/jrPcp7pm/G5UxDdT9opMsa yxj/pCdXc9g9cD2gYwPMcmaySTNe5q9WY+spYo4IIFy5PuGxyCbZYeG3MqM5BwAFa2vo bB3/tNaMsSoQZURBo4dVma6jheaHV1Sgw+z7OoestB2ximeyoFwkspF2xL9KaFrUvNFy LFxiTW12NHM89nHtar9e5dCP0eAZwqiNSW/ZQ/17BvB+krY1I7iy0/LYBX6TFcROMeW/ Dgs+MdeRcfJ+ojFelyYCmZFeUBUUrUbENNX5QBNhlanhDiJ5MmPRYivJhmwW1vKQpHXv JlaQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=WJUtAp3RgTuh77Yze697rFi7ZjYyj9YiGnAS+QwJ7C0=; b=DJWJjwEA1bpgYxmbjru/XvMSDkE2PKOMEF9lw6oOTJT72rVGNS9I8QucjlI142btZf DqPYP4T72SH2e0d96/zvS7dJfNLLhy0fFCuqPzJOgOWM7EmFld+e9X29BAdHBdv+fFXE UuBBUiQHhlo/CKkA7V/j1TADdj24Ieo5gSi/yS7weF9xs+dgnaPpMYIkjwP06rL90BHJ d0uz6YGXnP5dqTojWEN9hgR1xm1dU5SZJHKPpio+/N7lEW1/0+OBf5r20ND6kmmEMNRa TLL4nOBmH2xA2FZPEgLr8jEojEnxpKYT5r8zuVG0JNNWictjIZlKqissMkD/fclV9Lvl 0dOw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YL9ezgrs; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id p17si16189393ejd.307.2021.03.03.17.53.55; Wed, 03 Mar 2021 17:53:55 -0800 (PST) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YL9ezgrs; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244750AbhCDAAa (ORCPT + 3 others); Wed, 3 Mar 2021 19:00:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55586 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348847AbhCCN4R (ORCPT ); Wed, 3 Mar 2021 08:56:17 -0500 Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E96E3C061225 for ; Wed, 3 Mar 2021 05:55:06 -0800 (PST) Received: by mail-wm1-x331.google.com with SMTP id o16so6438339wmh.0 for ; Wed, 03 Mar 2021 05:55:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WJUtAp3RgTuh77Yze697rFi7ZjYyj9YiGnAS+QwJ7C0=; b=YL9ezgrsIYF/mhf7EYRWSR1lQZXrDPM/NOFRk+RGiRvHGHVOtx/Y3isZqopKl4RG6z ix32xeM0AR16nmSlcBFhUbyu/6WdESyu+0x3bkTWPfhCrigYGd0dhAn5uII4A4ombPrL bnggwd5WcH4P9RhdrafjN379iDJlE8OrjDeGZBiFjMWE3pyOzJZVYP7C81Ab7/GaNy7D xRMsOkrzbyq7dxDRHQh3MlVKKVlci7syCEhd6qLTuV1LlYRbIxHPgUDGb4MzWGigkLaH zr2T8xG+2g4lZ0dZcs4XDO2ef4cFMViexc6uT+xAJPYtJ+3TJLxCj86QlOTE40QR6pZ0 GGFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WJUtAp3RgTuh77Yze697rFi7ZjYyj9YiGnAS+QwJ7C0=; b=e34C06gch7map4cVSjSShs8rHCNtHD3DKWX7avlyfdaKg2B6ZIAOQBmu4QPNwtFq5L bDwMaTToSvLW7joFPV8eKw3TXUqL3uhuemxgfEPZDmcjbsJyhBzif5tPGJlAQ9McvmCo iICDqucUAjFn+tqm76bK9ZZr401CeMdnVZzQzbcO4Dl4TVT+QYnPFrcYTcinhd/W7fF7 9tU/lVHNcYX0FB3iVv/C7TVwjMs8YBtcy7Hl48c/r5AQP1aq7NscZSCog+h8o36rVbWO OpE5GNowIlwS6yNOP8asMaUtbEzGieJGbBhW0ryJwXFDnuauZDUCLrkKUBCMsCDhoO6q ZQ4w== X-Gm-Message-State: AOAM5305qAJhZ44YdLu4GHV/nyRcknio+zx/IZzuYP7Xxjtli/Vs8JKd O7IUrEQ3UGsxY91vJG9eVj23ug== X-Received: by 2002:a1c:2ed4:: with SMTP id u203mr9479000wmu.45.1614779705554; Wed, 03 Mar 2021 05:55:05 -0800 (PST) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id l15sm5963387wme.43.2021.03.03.05.55.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Mar 2021 05:55:01 -0800 (PST) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id A9ED71FF8C; Wed, 3 Mar 2021 13:55:00 +0000 (GMT) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ilias.apalodimas@linaro.org, arnd@linaro.org, ruchika.gupta@linaro.org, tomas.winkler@intel.com, yang.huang@intel.com, bing.zhu@intel.com, Matti.Moell@opensynergy.com, hmo@opensynergy.com, linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org, linux-nvme@vger.kernel.org, =?utf-8?q?Alex_Benn=C3=A9e?= , Ulf Hansson , Linus Walleij , Arnd Bergmann , Alexander Usyskin , Avri Altman Subject: [RFC PATCH 2/5] char: rpmb: provide a user space interface Date: Wed, 3 Mar 2021 13:54:57 +0000 Message-Id: <20210303135500.24673-3-alex.bennee@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210303135500.24673-1-alex.bennee@linaro.org> References: <20210303135500.24673-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org The user space API is achieved via a number of synchronous IOCTLs. * RPMB_IOC_VER_CMD - simple versioning API * RPMB_IOC_CAP_CMD - query of underlying capabilities * RPMB_IOC_PKEY_CMD - one time programming of access key * RPMB_IOC_COUNTER_CMD - query the write counter * RPMB_IOC_WBLOCKS_CMD - write blocks to device * RPMB_IOC_RBLOCKS_CMD - read blocks from device The keys used for programming and writing blocks to the device are key_serial_t handles as provided by the keyctl() interface. [AJB: here there are two key differences between this and the original proposal. The first is the dropping of the sequence of preformated frames in favour of explicit actions. The second is the introduction of key_serial_t and the keyring API for referencing the key to use] Signed-off-by: Alex Bennée Cc: Ulf Hansson Cc: Linus Walleij Cc: Arnd Bergmann Cc: Ilias Apalodimas Cc: Tomas Winkler Cc: Alexander Usyskin Cc: Avri Altman --- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 1 + drivers/char/rpmb/Kconfig | 7 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/cdev.c | 246 ++++++++++++++++++ drivers/char/rpmb/core.c | 10 +- drivers/char/rpmb/rpmb-cdev.h | 17 ++ include/linux/rpmb.h | 10 + include/uapi/linux/rpmb.h | 68 +++++ 9 files changed, 357 insertions(+), 4 deletions(-) create mode 100644 drivers/char/rpmb/cdev.c create mode 100644 drivers/char/rpmb/rpmb-cdev.h create mode 100644 include/uapi/linux/rpmb.h -- 2.20.1 diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index a4c75a28c839..0ff2d4d81bb0 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -344,6 +344,7 @@ Code Seq# Include File Comments 0xB5 00-0F uapi/linux/rpmsg.h 0xB6 all linux/fpga-dfl.h 0xB7 all uapi/linux/remoteproc_cdev.h +0xB8 80-8F uapi/linux/rpmb.h 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h 0xCA 10-2F uapi/misc/ocxl.h diff --git a/MAINTAINERS b/MAINTAINERS index 076f3983526c..c60b41b6e6bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15374,6 +15374,7 @@ M: ? L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* +F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h RTL2830 MEDIA DRIVER diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index 431c2823cf70..9068664a399a 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -9,3 +9,10 @@ config RPMB access RPMB partition. If unsure, select N. + +config RPMB_INTF_DEV + bool "RPMB character device interface /dev/rpmbN" + depends on RPMB && KEYS + help + Say yes here if you want to access RPMB from user space + via character device interface /dev/rpmb%d diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index 24d4752a9a53..f54b3f30514b 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new file mode 100644 index 000000000000..55f66720fd03 --- /dev/null +++ b/drivers/char/rpmb/cdev.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2015 - 2019 Intel Corporation. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +#include "rpmb-cdev.h" + +static dev_t rpmb_devt; +#define RPMB_MAX_DEVS MINORMASK + +#define RPMB_DEV_OPEN 0 /** single open bit (position) */ + +/** + * rpmb_open - the open function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_open(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev; + + rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev); + if (!rdev) + return -ENODEV; + + /* the rpmb is single open! */ + if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status)) + return -EBUSY; + + mutex_lock(&rdev->lock); + + fp->private_data = rdev; + + mutex_unlock(&rdev->lock); + + return nonseekable_open(inode, fp); +} + +/** + * rpmb_release - the cdev release function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 always. + */ +static int rpmb_release(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev = fp->private_data; + + clear_bit(RPMB_DEV_OPEN, &rdev->status); + + return 0; +} + +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_ver_cmd __user *ptr) +{ + struct rpmb_ioc_ver_cmd ver = { + .api_version = RPMB_API_VERSION, + }; + + return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; +} + +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_cap_cmd __user *ptr) +{ + struct rpmb_ioc_cap_cmd cap; + + cap.target = rdev->target; + cap.block_size = rdev->ops->block_size; + cap.wr_cnt_max = rdev->ops->wr_cnt_max; + cap.rd_cnt_max = rdev->ops->rd_cnt_max; + cap.auth_method = rdev->ops->auth_method; + cap.capacity = rpmb_get_capacity(rdev); + cap.reserved = 0; + + return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; +} + +static long rpmb_ioctl_pkey_cmd(struct rpmb_dev *rdev, key_serial_t __user *k) +{ + key_serial_t keyid; + + if (get_user(keyid, k)) + return -EFAULT; + else + return rpmb_program_key(rdev, keyid); +} + +static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, int __user *ptr) +{ + int count = rpmb_get_write_count(rdev); + + if (count > 0) + return put_user(count, ptr); + else + return count; +} + +static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_blocks_cmd __user *ptr) +{ + struct rpmb_ioc_blocks_cmd wblocks; + int sz; + long ret; + u8 *data; + + if (copy_from_user(&wblocks, ptr, sizeof(struct rpmb_ioc_blocks_cmd))) + return -EFAULT; + + /* Don't write more blocks device supports */ + if (wblocks.count > rdev->ops->wr_cnt_max) + return -EINVAL; + + sz = wblocks.count * 256; + data = kmalloc(sz, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + if (copy_from_user(data, wblocks.data, sz)) + ret = -EFAULT; + else + ret = rpmb_write_blocks(rdev, wblocks.key, wblocks.addr, wblocks.count, data); + + kfree(data); + return ret; +} + +static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_blocks_cmd __user *ptr) +{ + struct rpmb_ioc_blocks_cmd rblocks; + int sz; + long ret; + u8 *data; + + if (copy_from_user(&rblocks, ptr, sizeof(struct rpmb_ioc_blocks_cmd))) + return -EFAULT; + + if (rblocks.count > rdev->ops->rd_cnt_max) + return -EINVAL; + + sz = rblocks.count * 256; + data = kmalloc(sz, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, data); + + if (ret == 0) + ret = copy_to_user(rblocks.data, data, sz); + + kfree(data); + return ret; +} + +/** + * rpmb_ioctl - rpmb ioctl dispatcher + * + * @fp: a file pointer + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD RPMB_IOC_CAP_CMD + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd pmb_ioc_seq_cmd + * + * Return: 0 on success; < 0 on error + */ +static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct rpmb_dev *rdev = fp->private_data; + void __user *ptr = (void __user *)arg; + + switch (cmd) { + case RPMB_IOC_VER_CMD: + return rpmb_ioctl_ver_cmd(rdev, ptr); + case RPMB_IOC_CAP_CMD: + return rpmb_ioctl_cap_cmd(rdev, ptr); + case RPMB_IOC_PKEY_CMD: + return rpmb_ioctl_pkey_cmd(rdev, ptr); + case RPMB_IOC_COUNTER_CMD: + return rpmb_ioctl_counter_cmd(rdev, ptr); + case RPMB_IOC_WBLOCKS_CMD: + return rpmb_ioctl_wblocks_cmd(rdev, ptr); + case RPMB_IOC_RBLOCKS_CMD: + return rpmb_ioctl_rblocks_cmd(rdev, ptr); + default: + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } +} + +static const struct file_operations rpmb_fops = { + .open = rpmb_open, + .release = rpmb_release, + .unlocked_ioctl = rpmb_ioctl, + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +void rpmb_cdev_prepare(struct rpmb_dev *rdev) +{ + rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id); + rdev->cdev.owner = THIS_MODULE; + cdev_init(&rdev->cdev, &rpmb_fops); +} + +void rpmb_cdev_add(struct rpmb_dev *rdev) +{ + cdev_add(&rdev->cdev, rdev->dev.devt, 1); +} + +void rpmb_cdev_del(struct rpmb_dev *rdev) +{ + if (rdev->dev.devt) + cdev_del(&rdev->cdev); +} + +int __init rpmb_cdev_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS, "rpmb"); + if (ret < 0) + pr_err("unable to allocate char dev region\n"); + + return ret; +} + +void __exit rpmb_cdev_exit(void) +{ + unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); +} diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index a2e21c14986a..e26d605e48e1 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -12,6 +12,7 @@ #include #include +#include "rpmb-cdev.h" static DEFINE_IDA(rpmb_ida); @@ -277,6 +278,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); + rpmb_cdev_del(rdev); device_del(&rdev->dev); mutex_unlock(&rdev->lock); @@ -371,9 +373,6 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, if (!ops->read_blocks) return ERR_PTR(-EINVAL); - if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX) - return ERR_PTR(-EINVAL); - rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) return ERR_PTR(-ENOMEM); @@ -396,6 +395,8 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, if (ret) goto exit; + rpmb_cdev_add(rdev); + dev_dbg(&rdev->dev, "registered device\n"); return rdev; @@ -412,11 +413,12 @@ static int __init rpmb_init(void) { ida_init(&rpmb_ida); class_register(&rpmb_class); - return 0; + return rpmb_cdev_init(); } static void __exit rpmb_exit(void) { + rpmb_cdev_exit(); class_unregister(&rpmb_class); ida_destroy(&rpmb_ida); } diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d --- /dev/null +++ b/drivers/char/rpmb/rpmb-cdev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifdef CONFIG_RPMB_INTF_DEV +int __init rpmb_cdev_init(void); +void __exit rpmb_cdev_exit(void); +void rpmb_cdev_prepare(struct rpmb_dev *rdev); +void rpmb_cdev_add(struct rpmb_dev *rdev); +void rpmb_cdev_del(struct rpmb_dev *rdev); +#else +static inline int __init rpmb_cdev_init(void) { return 0; } +static inline void __exit rpmb_cdev_exit(void) {} +static inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_add(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {} +#endif /* CONFIG_RPMB_INTF_DEV */ diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index 718ba7c91ecd..fe44f60efe31 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -8,9 +8,13 @@ #include #include +#include +#include #include #include +#define RPMB_API_VERSION 0x80000001 + /** * struct rpmb_ops - RPMB ops to be implemented by underlying block device * @@ -51,6 +55,8 @@ struct rpmb_ops { * @dev : device * @id : device id * @target : RPMB target/region within the physical device + * @cdev : character dev + * @status : device status * @ops : operation exported by block layer */ struct rpmb_dev { @@ -58,6 +64,10 @@ struct rpmb_dev { struct device dev; int id; u8 target; +#ifdef CONFIG_RPMB_INTF_DEV + struct cdev cdev; + unsigned long status; +#endif /* CONFIG_RPMB_INTF_DEV */ const struct rpmb_ops *ops; }; diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file mode 100644 index 000000000000..3957b785cdd5 --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + * Copyright (C) 2021 Linaro Ltd + */ +#ifndef _UAPI_LINUX_RPMB_H_ +#define _UAPI_LINUX_RPMB_H_ + +#include + +/** + * struct rpmb_ioc_ver_cmd - rpmb api version + * + * @api_version: rpmb API version. + */ +struct rpmb_ioc_ver_cmd { + __u32 api_version; +} __packed; + +enum rpmb_auth_method { + RPMB_HMAC_ALGO_SHA_256 = 0, +}; + +/** + * struct rpmb_ioc_cap_cmd - rpmb capabilities + * + * @target: rpmb target/region within RPMB partition. + * @capacity: storage capacity (in units of 128K) + * @block_size: storage data block size (in units of 256B) + * @wr_cnt_max: maximal number of block that can be written in a single request. + * @rd_cnt_max: maximal number of block that can be read in a single request. + * @auth_method: authentication method: currently always HMAC_SHA_256 + * @reserved: reserved to align to 4 bytes. + */ +struct rpmb_ioc_cap_cmd { + __u16 target; + __u16 capacity; + __u16 block_size; + __u16 wr_cnt_max; + __u16 rd_cnt_max; + __u16 auth_method; + __u16 reserved; +} __attribute__((packed)); + +/** + * struct rpmb_ioc_blocks_cmd - read/write blocks to/from RPMB + * + * @keyid: key_serial_t of key to use + * @addr: index into device (units of 256B blocks) + * @count: number of 256B blocks + * @data: pointer to data to write/read + */ +struct rpmb_ioc_blocks_cmd { + __s32 key; /* key_serial_t */ + __u32 addr; + __u32 count; + __u8 __user *data; +} __attribute__((packed)); + + +#define RPMB_IOC_VER_CMD _IOR(0xB8, 80, struct rpmb_ioc_ver_cmd) +#define RPMB_IOC_CAP_CMD _IOR(0xB8, 81, struct rpmb_ioc_cap_cmd) +#define RPMB_IOC_PKEY_CMD _IOW(0xB8, 82, key_serial_t) +#define RPMB_IOC_COUNTER_CMD _IOR(0xB8, 84, int) +#define RPMB_IOC_WBLOCKS_CMD _IOW(0xB8, 85, struct rpmb_ioc_blocks_cmd) +#define RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_blocks_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */ From patchwork Wed Mar 3 13:54:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 392442 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.9 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B932DC43381 for ; Thu, 4 Mar 2021 00:00:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 831C864F5B for ; Thu, 4 Mar 2021 00:00:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344745AbhCDAAb (ORCPT ); Wed, 3 Mar 2021 19:00:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55560 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348208AbhCCN4R (ORCPT ); Wed, 3 Mar 2021 08:56:17 -0500 Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DF164C0617AB for ; Wed, 3 Mar 2021 05:55:04 -0800 (PST) Received: by mail-wr1-x431.google.com with SMTP id j2so10923148wrx.9 for ; Wed, 03 Mar 2021 05:55:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aKhicD2UVcMDm5lK1Pq3TMyG5fu0FbWM9t04Rq8hGto=; b=XHHwLU1T/a//9Raf3vwCDjPBWaFDLZJvjv20JlCErT7HUGU0tJpyMzEuuIBW2X501f 1scVeo/XlCtSKwMVIVDkthFPSi0DXpggbGEOBJJZ8k3d3+gwyunjOGEj5IuWa7Mk9Ecz /8ppOsxBFqmEE5eoge2ynak/MHzT6tO2Y92rHGQ6du8s8U2d0jKgXknaiDNkOygifD+e z9ZUi/7GbqxsaEXaq8nppxpCDHS8ecX9ON3fckTHILm/EqCcMvAtZ3pQLqeSF4tvOf2g SJxiak04NNIgdt45aE/tZxBGdrzS86KVxl+HHEoyQbf1D+VxoZlTQGIxnLTgeGuDy+K2 YcSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aKhicD2UVcMDm5lK1Pq3TMyG5fu0FbWM9t04Rq8hGto=; b=icRe4vdZ4JQmt1WTOcbgoEioFIC2f3qxY1HRIJ/CVxo5VpGVSxWFjcuyoXhNFF4PH9 2FVw/eoAZcOcYD9pbnD3ONsY4jdhkaW6BY3o7OnnHGdpK7PlJVznrzWwyAMThkuSV5FP uvrpor8/uzzayVLbZYegNEdSPyZjx6WbRuU0nbnY6egxPJnhKp5WDZNA7fHeBZjDjKKT uKcaxuSg3pTvCyGUMM9+gyOeDzSYR179AY7tygJEab/aCP4rlQVyJyVhIBqlXb2qHPjV XhqhsbZ0Yd6/fYmVY/5Acv38OM4j/tRry8F7JGpwN6XNl1QDNPSxp/yixjBhjkWWiqz1 /njQ== X-Gm-Message-State: AOAM531d6/2Ahbl+OPEZ9kk7FO1d8n4qHoUxGu5zOs5LePwbKoe/d8mY YLsb7+jDxPbHvAFc/+kpBF/bRg== X-Google-Smtp-Source: ABdhPJy0lZDVFRb97Pvu74TNTT2LLZ5U4Ih/Xev4AlKIfFfbgZb6wmFCAguYaJvM2nYhZAwdbKhHkA== X-Received: by 2002:adf:8b5c:: with SMTP id v28mr26790300wra.272.1614779703367; Wed, 03 Mar 2021 05:55:03 -0800 (PST) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id j12sm4332801wrx.59.2021.03.03.05.55.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Mar 2021 05:55:01 -0800 (PST) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id B81FF1FF8F; Wed, 3 Mar 2021 13:55:00 +0000 (GMT) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ilias.apalodimas@linaro.org, arnd@linaro.org, ruchika.gupta@linaro.org, tomas.winkler@intel.com, yang.huang@intel.com, bing.zhu@intel.com, Matti.Moell@opensynergy.com, hmo@opensynergy.com, linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org, linux-nvme@vger.kernel.org, =?utf-8?q?Alex_Benn=C3=A9e?= , Alexander Usyskin Subject: [RFC PATCH 3/5] tools rpmb: add RPBM access tool Date: Wed, 3 Mar 2021 13:54:58 +0000 Message-Id: <20210303135500.24673-4-alex.bennee@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210303135500.24673-1-alex.bennee@linaro.org> References: <20210303135500.24673-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org Add simple RPMB host testing tool. It can be used to program key, write and read data block, and retrieve write counter. [AJB: the principle differences are a simpler ioctl API which doesn't need to do the low level frame construction itself. It also uses the kernel keychain API for managing the keys] Signed-off-by: Alex Bennée Cc: Tomas Winkler Cc: Alexander Usyskin --- MAINTAINERS | 1 + tools/Makefile | 14 +- tools/rpmb/.gitignore | 2 + tools/rpmb/Makefile | 41 +++ tools/rpmb/rpmb.c | 649 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 702 insertions(+), 5 deletions(-) create mode 100644 tools/rpmb/.gitignore create mode 100644 tools/rpmb/Makefile create mode 100644 tools/rpmb/rpmb.c diff --git a/MAINTAINERS b/MAINTAINERS index c60b41b6e6bd..8b0768b16eae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15376,6 +15376,7 @@ S: Supported F: drivers/char/rpmb/* F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h +F: tools/rpmb/ RTL2830 MEDIA DRIVER M: Antti Palosaari diff --git a/tools/Makefile b/tools/Makefile index 85af6ebbce91..916ab8f8cefc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -27,6 +27,7 @@ help: @echo ' objtool - an ELF object analysis tool' @echo ' pci - PCI tools' @echo ' perf - Linux performance measurement and analysis tool' + @echo ' rpmb - Replay protected memory block access tool' @echo ' selftests - various kernel selftests' @echo ' bootconfig - boot config tool' @echo ' spi - spi tools' @@ -64,7 +65,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE +cgroup firewire hv guest bootconfig rpmb spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging: FORCE $(call descend,$@) bpf/%: FORCE @@ -100,7 +101,7 @@ kvm_stat: FORCE $(call descend,kvm/$@) all: acpi cgroup cpupower gpio hv firewire liblockdep \ - perf selftests bootconfig spi turbostat usb \ + perf rpmb selftests bootconfig spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \ pci debugging @@ -111,7 +112,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install: +cgroup_install firewire_install gpio_install hv_install iio_install rpmb_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install: $(call descend,$(@:_install=),install) liblockdep_install: @@ -134,7 +135,7 @@ kvm_stat_install: install: acpi_install cgroup_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ - perf_install selftests_install turbostat_install usb_install \ + perf_install rpmb_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install \ wmi_install pci_install debugging_install intel-speed-select_install @@ -164,6 +165,9 @@ perf_clean: $(Q)mkdir -p $(PERF_O) . $(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= clean +rpmb_clean: + $(call descend,$(@:_clean=),clean) + selftests_clean: $(call descend,testing/$(@:_clean=),clean) @@ -180,7 +184,7 @@ build_clean: $(call descend,build,clean) clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ - perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ + perf_clean rpmb_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \ diff --git a/tools/rpmb/.gitignore b/tools/rpmb/.gitignore new file mode 100644 index 000000000000..218f680548e6 --- /dev/null +++ b/tools/rpmb/.gitignore @@ -0,0 +1,2 @@ +*.o +rpmb diff --git a/tools/rpmb/Makefile b/tools/rpmb/Makefile new file mode 100644 index 000000000000..3d49a94ffb66 --- /dev/null +++ b/tools/rpmb/Makefile @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../scripts/Makefile.include + +CC ?= $(CROSS_COMPILE)gcc +LD ?= $(CROSS_COMPILE)ld +PKG_CONFIG = $(CROSS_COMPILE)pkg-config + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +INSTALL = install +prefix ?= /usr/local +bindir = $(prefix)/bin + + +CFLAGS += $(HOSTCFLAGS) +CFLAGS += -D__EXPORTED_HEADERS__ +CFLAGS += -Wall -Wextra -ggdb +ifdef RPMB_STATIC +LDFLAGS += -pthread -static -Wl,-u,pthread_mutex_unlock +CFLAGS += -pthread -static +PKG_STATIC = --static +endif +CFLAGS += -I$(srctree)/include/uapi -I$(srctree)/include +LDLIBS += $(shell $(PKG_CONFIG) --libs $(PKG_STATIC) libkeyutils) + +prog := rpmb + +all : $(prog) + +$(prog): rpmb.o + +clean : + $(RM) $(prog) *.o + +install: $(prog) + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(prog) $(DESTDIR)$(bindir) diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c new file mode 100644 index 000000000000..d5b85af14f94 --- /dev/null +++ b/tools/rpmb/rpmb.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (C) 2016-2019 Intel Corp. All rights reserved + * Copyright (C) 2021 Linaro Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linux/rpmb.h" + +#define RPMB_KEY_SIZE 32 +#define RPMB_MAC_SIZE 32 +#define RPMB_NONCE_SIZE 16 + +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + + +static bool verbose; +#define rpmb_dbg(fmt, ARGS...) do { \ + if (verbose) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS); \ +} while (0) + +#define rpmb_msg(fmt, ARGS...) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS) + +#define rpmb_err(fmt, ARGS...) \ + fprintf(stderr, "rpmb: error: " fmt, ##ARGS) + +static int open_dev_file(const char *devfile, struct rpmb_ioc_cap_cmd *cap) +{ + struct rpmb_ioc_ver_cmd ver; + int fd; + int ret; + + fd = open(devfile, O_RDWR); + if (fd < 0) + rpmb_err("Cannot open: %s: %s.\n", devfile, strerror(errno)); + + ret = ioctl(fd, RPMB_IOC_VER_CMD, &ver); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + printf("RPMB API Version %X\n", ver.api_version); + + ret = ioctl(fd, RPMB_IOC_CAP_CMD, cap); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + rpmb_dbg("RPMB rpmb_target = %hd\n", cap->target); + rpmb_dbg("RPMB capacity = %hd\n", cap->capacity); + rpmb_dbg("RPMB block_size = %hd\n", cap->block_size); + rpmb_dbg("RPMB wr_cnt_max = %hd\n", cap->wr_cnt_max); + rpmb_dbg("RPMB rd_cnt_max = %hd\n", cap->rd_cnt_max); + rpmb_dbg("RPMB auth_method = %hd\n", cap->auth_method); + + return fd; +err: + close(fd); + return -1; +} + +static int open_rd_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDIN_FILENO; + else + fd = open(datafile, O_RDONLY); + + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + + return fd; +} + +static int open_wr_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDOUT_FILENO; + else + fd = open(datafile, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + return fd; +} + +static void close_fd(int fd) +{ + if (fd > 0 && fd != STDIN_FILENO && fd != STDOUT_FILENO) + close(fd); +} + +/* need to just cast out 'const' in write(2) */ +typedef ssize_t (*rwfunc_t)(int fd, void *buf, size_t count); +/* blocking rw wrapper */ +static ssize_t rw(rwfunc_t func, int fd, unsigned char *buf, size_t size) +{ + ssize_t ntotal = 0, n; + char *_buf = (char *)buf; + + do { + n = func(fd, _buf + ntotal, size - ntotal); + if (n == -1 && errno != EINTR) { + ntotal = -1; + break; + } else if (n > 0) { + ntotal += n; + } + } while (n != 0 && (size_t)ntotal != size); + + return ntotal; +} + +static ssize_t read_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw(read, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s\n.", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("read %zd but must be %zu bytes length.\n", ret, size); + ret = -EINVAL; + } + + return ret; +} + +static ssize_t write_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw((rwfunc_t)write, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s.\n", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("data is %zd but must be %zu bytes length.\n", + ret, size); + ret = -EINVAL; + } + return ret; +} + +static int op_get_info(int nargs, char *argv[]) +{ + int dev_fd; + struct rpmb_ioc_cap_cmd cap; + + if (nargs != 1) + return -EINVAL; + + memset(&cap, 0, sizeof(cap)); + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + return -errno; + argv++; + + printf("RPMB rpmb_target = %hd\n", cap.target); + printf("RPMB capacity = %hd\n", cap.capacity); + printf("RPMB block_size = %hd\n", cap.block_size); + printf("RPMB wr_cnt_max = %hd\n", cap.wr_cnt_max); + printf("RPMB rd_cnt_max = %hd\n", cap.rd_cnt_max); + printf("RPMB auth_method = %hd\n", cap.auth_method); + + close(dev_fd); + + return 0; +} + +static int op_rpmb_add_key(int nargs, char *argv[]) +{ + int key_fd, ret = -EINVAL; + unsigned char key_data[RPMB_KEY_SIZE]; + key_serial_t key; + + if (nargs != 1) + return -EINVAL; + + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) { + perror("opening key file"); + return ret; + } + + if (read_file(key_fd, key_data, RPMB_KEY_SIZE) != RPMB_KEY_SIZE) + { + perror("reading key file"); + return ret; + } + + key = add_key("user", "RPMB MAC", key_data, RPMB_KEY_SIZE, + KEY_SPEC_SESSION_KEYRING); + + if (key == -1) { + perror("add_key"); + return ret; + } + + printf("Key ID is %jx\n", (uintmax_t) key); + + return 0; +} + +static key_serial_t get_key(char *keyid) { + key_serial_t key = -1; + + if (keyid) { + if (sscanf(keyid, "%jx", (uintmax_t *) &key) != 1) + perror("reading keyid"); + } else { + key = request_key("user", "RPMB MAC", NULL, + KEY_SPEC_SESSION_KEYRING); + } + return key; +} + +static int op_rpmb_program_key(int nargs, char *argv[]) +{ + int ret, fd = -1; + key_serial_t key; + struct rpmb_ioc_cap_cmd cap; + + ret = -EINVAL; + if (nargs < 1 || nargs > 2) + return ret; + + fd = open_dev_file(argv[0], &cap); + if (fd < 0) { + perror("opening RPMB device"); + return ret; + } + argv++; + + key = get_key(nargs == 2 ? argv[0] : NULL); + if (key == -1) + goto out; + + ret = ioctl(fd, RPMB_IOC_PKEY_CMD, &key); + if (ret < 0) { + rpmb_err("pkey ioctl failure %d: %s.\n", ret, strerror(errno)); + } + +out: + close_fd(fd); + return ret; +} + + +static int op_rpmb_get_write_counter(int nargs, char **argv) +{ + int ret, fd = -1; + unsigned int counter; + struct rpmb_ioc_cap_cmd cap; + + ret = -EINVAL; + if (nargs != 1) + return ret; + + fd = open_dev_file(argv[0], &cap); + if (fd < 0) { + perror("opening RPMB device"); + return ret; + } + + ret = ioctl(fd, RPMB_IOC_COUNTER_CMD, &counter); + if (ret < 0) { + rpmb_err("counter ioctl failure %d: %s.\n", ret, strerror(errno)); + } + + printf("Counter value is: %ud\n", counter); + + close_fd(fd); + return ret; +} + +static int op_rpmb_read_blocks(int nargs, char **argv) +{ + int ret, data_fd, fd = -1; + struct rpmb_ioc_cap_cmd cap; + unsigned long numarg; + uint16_t addr, blocks_cnt; + + ret = -EINVAL; + if (nargs != 4) + return ret; + + fd = open_dev_file(argv[0], &cap); + if (fd < 0) { + perror("opening RPMB device"); + return ret; + } + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address\n"); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_wr_file(argv[0], "output data"); + if (data_fd < 0) + goto out; + argv++; + + while (blocks_cnt > 0) { + int to_copy = min(blocks_cnt, cap.rd_cnt_max); + int length = to_copy * 256; + void *data = malloc(length); + struct rpmb_ioc_blocks_cmd cmd; + if (!data) { + ret = ENOMEM; + goto out; + } + cmd.addr = addr; + cmd.count = to_copy; + cmd.data = data; + + ret = ioctl(fd, RPMB_IOC_RBLOCKS_CMD, &cmd); + if (ret < 0) { + rpmb_err("rblocks ioctl failure %d: %s.\n", ret, + strerror(errno)); + goto out; + } + + ret = write_file(data_fd, data, length); + if (ret < 0) { + perror("writing data"); + goto out; + } + + free(data); + addr += to_copy; + blocks_cnt -= to_copy; + } + + ret = 0; +out: + close_fd(fd); + close_fd(data_fd); + + return ret; +} + +static int op_rpmb_write_blocks(int nargs, char **argv) +{ + int ret, data_fd, fd = -1; + struct rpmb_ioc_cap_cmd cap; + unsigned long numarg; + uint16_t addr, blocks_cnt; + key_serial_t key; + + ret = -EINVAL; + if (nargs < 4 || nargs > 5) + return ret; + + fd = open_dev_file(argv[0], &cap); + if (fd < 0) { + perror("opening RPMB device"); + return ret; + } + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address\n"); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_wr_file(argv[0], "input data"); + if (data_fd < 0) + goto out; + argv++; + + key = get_key(nargs == 5 ? argv[0] : NULL); + if (key == -1) + goto out; + + while (blocks_cnt > 0) { + int to_copy = min(blocks_cnt, cap.wr_cnt_max); + int length = to_copy * 256; + void *data = malloc(length); + struct rpmb_ioc_blocks_cmd cmd; + if (!data) { + ret = ENOMEM; + goto out; + } + cmd.key = + cmd.addr = addr; + cmd.count = to_copy; + cmd.data = data; + + ret = read_file(data_fd, data, length); + if (ret < 0) { + perror("reading data"); + goto out; + } + + ret = ioctl(fd, RPMB_IOC_WBLOCKS_CMD, &cmd); + if (ret < 0) { + rpmb_err("wblocks ioctl failure %d: %s.\n", ret, + strerror(errno)); + goto out; + } + + free(data); + addr += to_copy; + blocks_cnt -= to_copy; + } + + ret = 0; +out: + close_fd(fd); + close_fd(data_fd); + + return ret; +} + +typedef int (*rpmb_op)(int argc, char *argv[]); + +struct rpmb_cmd { + const char *op_name; + rpmb_op op; + const char *usage; /* usage title */ + const char *help; /* help */ +}; + +static const struct rpmb_cmd cmds[] = { + { + "get-info", + op_get_info, + "", + " Get RPMB device info\n", + }, + { + "add-key", + op_rpmb_add_key, + "", + " Load a 32 byte KEY_FILE into the session keyring.\n" + " Returns a KEYID to use in future transactions.", + }, + { + "program-key", + op_rpmb_program_key, + " ", + " Program authentication KEYID\n" + " NOTE: This is a one-time programmable irreversible change.\n", + }, + { + "write-counter", + op_rpmb_get_write_counter, + "", + " Rertrive write counter value from the to stdout.\n" + }, + { + "write-blocks", + op_rpmb_write_blocks, + "
", + " of 256 bytes will be written from the DATA_FILE\n" + " to the at block offset
.\n" + " When DATA_FILE is -, read from standard input.\n", + }, + { + "read-blocks", + op_rpmb_read_blocks, + "
", + " of 256 bytes will be read from \n" + " to the OUTPUT_FILE\n" + " When OUTPUT_FILE is -, write to standard output\n", + }, + + { NULL, NULL, NULL, NULL } +}; + +static void help(const char *prog, const struct rpmb_cmd *cmd) +{ + printf("%s %s %s\n", prog, cmd->op_name, cmd->usage); + printf("%s\n", cmd->help); +} + +static void usage(const char *prog) +{ + int i; + + printf("\n"); + printf("Usage: %s [-v] \n\n", prog); + for (i = 0; cmds[i].op_name; i++) + printf(" %s %s %s\n", + prog, cmds[i].op_name, cmds[i].usage); + + printf("\n"); + printf(" %s -v/--verbose: runs in verbose mode\n", prog); + printf(" %s help : shows this help\n", prog); + printf(" %s help : shows detailed help\n", prog); +} + +static bool call_for_help(const char *arg) +{ + return !strcmp(arg, "help") || + !strcmp(arg, "-h") || + !strcmp(arg, "--help"); +} + +static bool parse_verbose(const char *arg) +{ + return !strcmp(arg, "-v") || + !strcmp(arg, "--verbose"); +} + +static const +struct rpmb_cmd *parse_args(const char *prog, int *_argc, char **_argv[]) +{ + int i; + int argc = *_argc; + char **argv = *_argv; + const struct rpmb_cmd *cmd = NULL; + bool need_help = false; + + argc--; argv++; + + if (argc == 0) + goto out; + + if (call_for_help(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + need_help = true; + } + + if (parse_verbose(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + verbose = true; + } + + for (i = 0; cmds[i].op_name; i++) { + if (!strncmp(argv[0], cmds[i].op_name, + strlen(cmds[i].op_name))) { + cmd = &cmds[i]; + argc--; argv++; + break; + } + } + + if (!cmd) + goto out; + + if (need_help || (argc > 0 && call_for_help(argv[0]))) { + help(prog, cmd); + argc--; argv++; + return NULL; + } + +out: + *_argc = argc; + *_argv = argv; + + if (!cmd) + usage(prog); + + return cmd; +} + +int main(int argc, char *argv[]) +{ + const char *prog = basename(argv[0]); + const struct rpmb_cmd *cmd; + int ret; + + cmd = parse_args(prog, &argc, &argv); + if (!cmd) + exit(EXIT_SUCCESS); + + ret = cmd->op(argc, argv); + if (ret == -EINVAL) + help(prog, cmd); + + if (ret) { + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +} From patchwork Wed Mar 3 13:54:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 392635 Delivered-To: patch@linaro.org Received: by 2002:a02:290e:0:0:0:0:0 with SMTP id p14csp5612586jap; Wed, 3 Mar 2021 17:53:54 -0800 (PST) X-Google-Smtp-Source: ABdhPJyX65gQqRbUG4gm/Uelf/9FSPodJrZygq7mwYZMc7xEv23itSpcePy3UtGMW0bkz7UW0HJO X-Received: by 2002:a17:906:2bce:: with SMTP id n14mr1560888ejg.171.1614822834646; Wed, 03 Mar 2021 17:53:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1614822834; cv=none; d=google.com; s=arc-20160816; b=h1vMHLmjZ+rVZinZBxUwCF4Dysm+kCLxGjP8Tg/+CcmcB4WEesRsC1Hc3EXWHMWJqN nX4+K2pKpNwaWhB2lWGfE3mwUiiw6DdXj79bz20ebMppa4UAyqdmwBxbTJM8VJD8fRQ8 AiVkdtm59utj63AXqy/7XGi7jZdzNU1NT47fY4Lt0UlYFeoYySoUuPAyfN0CDLgf6o6j ZnRs3dwuWW4g7syJqLEh/lB3nA2aAQJmjLKaLwn9XM4V+OYzx/0KFzlJtjwSZ/KHtqQF I/PXCaZUDFE3a70GP/OjrQbYGmqTJBvsuK0Bdv57PkCARK3QMth2geot4SOPZG7uqhrJ uXHg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=HCnjHWCo/Q+bojTy7byBJ3tyq3PAWYgoo0RiPlZgCx8=; b=eSrShgj4FkTH6QlNkykk9uA/UoIiDy2occAw2HVEseMdaT/E/SdUS84tiufs88IZeM qKRyuSFn8A7FsVrh9VBMowJ6J52extZkEcyygGaSYcetJA/CBWu0ID5NH421kjsbnXcf rmTXvA0kwT5v85sb8/AjSIYQa72kXsBAqzns2y+p4UWDrAZC2Po4J87D5XFf9TRDie21 wqQLW7T9TVEgnqdFkHnsCxUmreGWRBr+iqb5Om+SnkdF86p1iRQ35ayEaMVACqG+vcqe lWnvmL4kHmGAVB6kbhaGZdFauu0o8aeFb7A+V7AKV2JyqLSQCc5OM23g1Gkvch+6Jgil d2cQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=eM6j6sHD; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id p17si16189393ejd.307.2021.03.03.17.53.54; Wed, 03 Mar 2021 17:53:54 -0800 (PST) Received-SPF: pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=eM6j6sHD; spf=pass (google.com: domain of linux-mmc-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-mmc-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344691AbhCDAAa (ORCPT + 3 others); Wed, 3 Mar 2021 19:00:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55080 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348941AbhCCN4R (ORCPT ); Wed, 3 Mar 2021 08:56:17 -0500 Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D63C6C0611C0 for ; Wed, 3 Mar 2021 05:55:07 -0800 (PST) Received: by mail-wr1-x436.google.com with SMTP id u14so23789283wri.3 for ; Wed, 03 Mar 2021 05:55:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=HCnjHWCo/Q+bojTy7byBJ3tyq3PAWYgoo0RiPlZgCx8=; b=eM6j6sHD8iQR88/l5k52Vjbo6tLW/vfWt4PrURyyXxxIMpNZFM6pTi3/0eWgzDctxk x2YL5bv03zGQelKM5mRJvn/U5TWhT4YJSWOHh5Jg3gLhmB5eF18YaQAFbodRKw9S8W7e /3KuHpHx0xnb7mVRw4K29Za0CmfY7KY/BNzdJ8pI1cwajO4m6k6JKm+x8gj8g8MOHmpw OGfFrzvX3D90S7iBwaR1cXoZwhxJddXZGXKoQU/Eb9POoAU1sXL2cW7F0fgh0kQtmSHs C6clHKEF82LkPySj+3ekBr/u014f6wnIcRR81DLCt3ryvIOFFl8sHDQNBrxZvu8rLxdR S92A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=HCnjHWCo/Q+bojTy7byBJ3tyq3PAWYgoo0RiPlZgCx8=; b=ubui6ZFbi1LucnVPc9k2Vuw+xAnS4cBzw2sdPYdYcwDoTGEOq4TwTAz+nNmno5SAb3 cOwLwwbefNN7uJ/CM+b78gNbMgyOReafNQWtaM62xEhj/xRb9MYjtYKKuMP4s2irjAgx Tdu5q7i5mPKbQpvYFlcfL76kBEycDqyAzww/UgK04w+7tV5AmA/GRD/5n2Aa3FsXOApo JuVmpGQ/gHK1AE47KXaMRzNTkIfz4NPhXS3gX2uzVeqZ6eSZsMygXQxKNCoNDu4JTO3V rb4mHH+FV/pKWagLRszFQ0UYBrINlTeYak4cr5BJoMv+TalUJsZp4a78lhlXT6zXarc0 M/IQ== X-Gm-Message-State: AOAM532tOYBRpPhEFP9RgGiOKhemiwaXJGHvi+l3NXN8mW8nRX6qOzKP lwhBCwZDZ9a7DcFCjRJP4iEZOA== X-Received: by 2002:adf:f587:: with SMTP id f7mr16082253wro.147.1614779706553; Wed, 03 Mar 2021 05:55:06 -0800 (PST) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id a21sm7759875wmb.5.2021.03.03.05.55.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Mar 2021 05:55:01 -0800 (PST) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id C5CAC1FF90; Wed, 3 Mar 2021 13:55:00 +0000 (GMT) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ilias.apalodimas@linaro.org, arnd@linaro.org, ruchika.gupta@linaro.org, tomas.winkler@intel.com, yang.huang@intel.com, bing.zhu@intel.com, Matti.Moell@opensynergy.com, hmo@opensynergy.com, linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org, linux-nvme@vger.kernel.org, =?utf-8?q?Alex_Benn=C3=A9e?= Subject: [RFC PATCH 4/5] rpmb: create virtio rpmb frontend driver [WIP] Date: Wed, 3 Mar 2021 13:54:59 +0000 Message-Id: <20210303135500.24673-5-alex.bennee@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210303135500.24673-1-alex.bennee@linaro.org> References: <20210303135500.24673-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org This implements a virtio rpmb frontend driver for the RPMB subsystem. [AJB: the principle difference is all the MAC calculation and validation is now done in the driver] Signed-off-by: Alex Bennée Cc: Tomas Winkler --- ajb: - add include linux/slab.h for kzalloc - include standardised VIRTIO_ID_RPMB - remove extra frames when sending commands down virtqueue - clean up out/in sgs - hopefully more conforming - remove cmd_cap code --- drivers/char/rpmb/Kconfig | 10 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/virtio_rpmb.c | 366 +++++++++++++++++++++++++++++++ include/uapi/linux/virtio_ids.h | 1 + include/uapi/linux/virtio_rpmb.h | 54 +++++ 5 files changed, 432 insertions(+) create mode 100644 drivers/char/rpmb/virtio_rpmb.c create mode 100644 include/uapi/linux/virtio_rpmb.h -- 2.20.1 diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index 9068664a399a..845f458452a5 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -16,3 +16,13 @@ config RPMB_INTF_DEV help Say yes here if you want to access RPMB from user space via character device interface /dev/rpmb%d + +config VIRTIO_RPMB + tristate "Virtio RPMB character device interface /dev/vrpmb" + default n + depends on VIRTIO && KEYS + select RPMB + help + Say yes here if you want to access virtio RPMB from user space + via character device interface /dev/vrpmb. + This device interface is only for guest/frontend virtio driver. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index f54b3f30514b..4b397b50a42c 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o +obj-$(CONFIG_VIRTIO_RPMB) += virtio_rpmb.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/virtio_rpmb.c b/drivers/char/rpmb/virtio_rpmb.c new file mode 100644 index 000000000000..6ade26874a4e --- /dev/null +++ b/drivers/char/rpmb/virtio_rpmb.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtio RPMB Front End Driver + * + * Copyright (c) 2018-2019 Intel Corporation. + * Copyright (c) 2021 Linaro Ltd. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMB_MAC_SIZE 32 + +static const char id[] = "RPMB:VIRTIO"; + +struct virtio_rpmb_info { + struct virtqueue *vq; + struct mutex lock; /* info lock */ + wait_queue_head_t have_data; + struct rpmb_dev *rdev; +}; + +static void virtio_rpmb_recv_done(struct virtqueue *vq) +{ + struct virtio_rpmb_info *vi; + struct virtio_device *vdev = vq->vdev; + + vi = vq->vdev->priv; + if (!vi) { + dev_err(&vdev->dev, "Error: no found vi data.\n"); + return; + } + + wake_up(&vi->have_data); +} + +/* static int rpmb_virtio_cmd_seq(struct device *dev, u8 target, */ +/* struct rpmb_cmd *cmds, u32 ncmds) */ +/* { */ +/* struct virtio_device *vdev = dev_to_virtio(dev); */ +/* struct virtio_rpmb_info *vi = vdev->priv; */ +/* unsigned int i; */ +/* struct scatterlist out_frames[3]; */ +/* struct scatterlist in_frames[3]; */ +/* struct scatterlist *sgs[3]; */ +/* unsigned int num_out = 0, num_in = 0; */ +/* size_t sz; */ +/* int ret; */ +/* unsigned int len; */ + +/* if (ncmds > RPMB_SEQ_CMD_MAX) */ +/* return -EINVAL; */ + +/* mutex_lock(&vi->lock); */ + +/* /\* */ +/* * Process the frames - putting in the appropriate out_frames */ +/* * and in_frames before we build our final ordered sgs[] out */ +/* * array. */ +/* *\/ */ +/* for (i = 0; i < ncmds; i++) { */ +/* struct rpmb_cmd *cmd = &cmds[i]; */ +/* sz = sizeof(struct rpmb_frame_jdec) * (cmd->nframes ?: 1); */ +/* if (cmd->flags) { */ +/* sg_init_one(&out_frames[num_out++], cmd->frames, sz); */ +/* } else { */ +/* sg_init_one(&in_frames[num_in++], cmd->frames, sz); */ +/* } */ +/* } */ + +/* for (i = 0; i < num_out; i++) { */ +/* sgs[i] = &out_frames[i]; */ +/* } */ +/* for (i = 0; i < num_in; i++) { */ +/* sgs[num_out + i] = &in_frames[i]; */ +/* } */ + +/* dev_warn(dev, "out = %d, in = %d\n", num_out, num_in); */ + +/* virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); */ +/* virtqueue_kick(vi->vq); */ + +/* wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); */ + +/* ret = 0; */ + +/* /\* */ +/* * We can't fail at this point, we assume we got a response */ +/* * buffer back. */ +/* *\/ */ + +/* mutex_unlock(&vi->lock); */ +/* return ret; */ +/* } */ + +static void * rpmb_virtio_get_key(struct device *dev, key_serial_t keyid) +{ + key_ref_t keyref; + struct key *key; + void *ptr = NULL; + + keyref = lookup_user_key(keyid, 0, KEY_NEED_SEARCH); + if (IS_ERR(keyref)) + return NULL; + + key = key_ref_to_ptr(keyref); + + if (key->datalen != RPMB_MAC_SIZE) + dev_err(dev, "Invalid key size (%d)", key->datalen); + else + ptr = key->payload.data[0]; + + key_put(key); + return ptr; +} + +static int rpmb_virtio_program_key(struct device *dev, u8 target, key_serial_t keyid) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + const void *key = rpmb_virtio_get_key(dev, keyid); + + if (key) { + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2]; + unsigned int len; + struct virtio_rpmb_frame resp; + struct virtio_rpmb_frame cmd = { + .req_resp = cpu_to_le16(VIRTIO_RPMB_REQ_PROGRAM_KEY) + }; + + /* Prepare the command frame & response */ + memcpy(&cmd.key_mac, key, sizeof(cmd.key_mac)); + + mutex_lock(&vi->lock); + + /* Wrap into SG array */ + sg_init_one(&out_frame, &cmd, sizeof(cmd)); + sg_init_one(&in_frame, &resp, sizeof(resp)); + sgs[0] = &out_frame; + sgs[1] = &in_frame; + + /* Send it */ + virtqueue_add_sgs(vi->vq, sgs, 1, 1, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + mutex_unlock(&vi->lock); + + if (le16_to_cpu(resp.req_resp) != VIRTIO_RPMB_RESP_PROGRAM_KEY) { + dev_err(dev, "Bad response from device (%x/%x)", + le16_to_cpu(resp.req_resp), le16_to_cpu(resp.result)); + return -EPROTO; + } + + /* check the MAC? */ + + /* map responses to better errors? */ + return le16_to_cpu(resp.result) == VIRTIO_RPMB_RES_OK ? 0 : -EIO; + } else { + return -EINVAL; + } +} + +static int rpmb_virtio_get_capacity(struct device *dev, u8 target) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + u8 capacity; + + virtio_cread(vdev, struct virtio_rpmb_config, capacity, &capacity); + + if (capacity > 0x80) { + dev_err(&vdev->dev, "Error: invalid capacity reported.\n"); + capacity = 0x80; + } + + return capacity; +} + +static int rpmb_virtio_get_write_count(struct device *dev, u8 target) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2]; + unsigned int len; + struct virtio_rpmb_frame resp; + struct virtio_rpmb_frame cmd = { + .req_resp = cpu_to_le16(VIRTIO_RPMB_REQ_GET_WRITE_COUNTER) + }; + + mutex_lock(&vi->lock); + + /* Wrap into SG array */ + sg_init_one(&out_frame, &cmd, sizeof(cmd)); + sg_init_one(&in_frame, &resp, sizeof(resp)); + sgs[0] = &out_frame; + sgs[1] = &in_frame; + + /* Send it */ + virtqueue_add_sgs(vi->vq, sgs, 1, 1, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + mutex_unlock(&vi->lock); + + if (le16_to_cpu(resp.req_resp) != VIRTIO_RPMB_RESP_GET_COUNTER) { + dev_err(dev, "failed to program key (%x/%x)", + le16_to_cpu(resp.req_resp), le16_to_cpu(resp.result)); + return -EPROTO; + } + + return le16_to_cpu(resp.result) == VIRTIO_RPMB_RES_OK ? be32_to_cpu(resp.write_counter) : -EIO; +} + +static struct rpmb_ops rpmb_virtio_ops = { + .program_key = rpmb_virtio_program_key, + .get_capacity = rpmb_virtio_get_capacity, + .get_write_count = rpmb_virtio_get_write_count, + /* .write_blocks = rpmb_virtio_write_blocks, */ + /* .read_blocks = rpmb_virtio_read_blocks, */ + .auth_method = RPMB_HMAC_ALGO_SHA_256, +}; + +static int rpmb_virtio_dev_init(struct virtio_rpmb_info *vi) +{ + int ret = 0; + struct device *dev = &vi->vq->vdev->dev; + struct virtio_device *vdev = dev_to_virtio(dev); + u8 max_wr, max_rd; + + virtio_cread(vdev, struct virtio_rpmb_config, + max_wr_cnt, &max_wr); + virtio_cread(vdev, struct virtio_rpmb_config, + max_rd_cnt, &max_rd); + + rpmb_virtio_ops.dev_id_len = strlen(id); + rpmb_virtio_ops.dev_id = id; + rpmb_virtio_ops.wr_cnt_max = max_wr; + rpmb_virtio_ops.rd_cnt_max = max_rd; + rpmb_virtio_ops.block_size = 1; + + vi->rdev = rpmb_dev_register(dev, 0, &rpmb_virtio_ops); + if (IS_ERR(vi->rdev)) { + ret = PTR_ERR(vi->rdev); + goto err; + } + + dev_set_drvdata(dev, vi); +err: + return ret; +} + +static int virtio_rpmb_init(struct virtio_device *vdev) +{ + int ret; + struct virtio_rpmb_info *vi; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) + return -ENOMEM; + + init_waitqueue_head(&vi->have_data); + mutex_init(&vi->lock); + vdev->priv = vi; + + /* We expect a single virtqueue. */ + vi->vq = virtio_find_single_vq(vdev, virtio_rpmb_recv_done, "request"); + if (IS_ERR(vi->vq)) { + dev_err(&vdev->dev, "get single vq failed!\n"); + ret = PTR_ERR(vi->vq); + goto err; + } + + /* create vrpmb device. */ + ret = rpmb_virtio_dev_init(vi); + if (ret) { + dev_err(&vdev->dev, "create vrpmb device failed.\n"); + goto err; + } + + dev_info(&vdev->dev, "init done!\n"); + + return 0; + +err: + kfree(vi); + return ret; +} + +static void virtio_rpmb_remove(struct virtio_device *vdev) +{ + struct virtio_rpmb_info *vi; + + vi = vdev->priv; + if (!vi) + return; + + if (wq_has_sleeper(&vi->have_data)) + wake_up(&vi->have_data); + + rpmb_dev_unregister(vi->rdev); + + if (vdev->config->reset) + vdev->config->reset(vdev); + + if (vdev->config->del_vqs) + vdev->config->del_vqs(vdev); + + kfree(vi); +} + +static int virtio_rpmb_probe(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} + +#ifdef CONFIG_PM_SLEEP +static int virtio_rpmb_freeze(struct virtio_device *vdev) +{ + virtio_rpmb_remove(vdev); + return 0; +} + +static int virtio_rpmb_restore(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} +#endif + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMB, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_rpmb_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_rpmb_probe, + .remove = virtio_rpmb_remove, +#ifdef CONFIG_PM_SLEEP + .freeze = virtio_rpmb_freeze, + .restore = virtio_rpmb_restore, +#endif +}; + +module_virtio_driver(virtio_rpmb_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_DESCRIPTION("Virtio rpmb frontend driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index bc1c0621f5ed..934df72714e2 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -53,6 +53,7 @@ #define VIRTIO_ID_MEM 24 /* virtio mem */ #define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ +#define VIRTIO_ID_RPMB 28 /* virtio RPMB */ #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ #endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/uapi/linux/virtio_rpmb.h b/include/uapi/linux/virtio_rpmb.h new file mode 100644 index 000000000000..f048cd968210 --- /dev/null +++ b/include/uapi/linux/virtio_rpmb.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ + +#ifndef _UAPI_LINUX_VIRTIO_RPMB_H +#define _UAPI_LINUX_VIRTIO_RPMB_H + +#include +#include +#include +#include + +struct virtio_rpmb_config { + __u8 capacity; + __u8 max_wr_cnt; + __u8 max_rd_cnt; +} __attribute__((packed)); + +/* RPMB Request Types (in .req_resp) */ +#define VIRTIO_RPMB_REQ_PROGRAM_KEY 0x0001 +#define VIRTIO_RPMB_REQ_GET_WRITE_COUNTER 0x0002 +#define VIRTIO_RPMB_REQ_DATA_WRITE 0x0003 +#define VIRTIO_RPMB_REQ_DATA_READ 0x0004 +#define VIRTIO_RPMB_REQ_RESULT_READ 0x0005 + +/* RPMB Response Types (in .req_resp) */ +#define VIRTIO_RPMB_RESP_PROGRAM_KEY 0x0100 +#define VIRTIO_RPMB_RESP_GET_COUNTER 0x0200 +#define VIRTIO_RPMB_RESP_DATA_WRITE 0x0300 +#define VIRTIO_RPMB_RESP_DATA_READ 0x0400 + +struct virtio_rpmb_frame { + __u8 stuff[196]; + __u8 key_mac[32]; + __u8 data[256]; + __u8 nonce[16]; + __be32 write_counter; + __be16 address; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __attribute__((packed)); + +/* RPMB Operation Results (in .result) */ +#define VIRTIO_RPMB_RES_OK 0x0000 +#define VIRTIO_RPMB_RES_GENERAL_FAILURE 0x0001 +#define VIRTIO_RPMB_RES_AUTH_FAILURE 0x0002 +#define VIRTIO_RPMB_RES_COUNT_FAILURE 0x0003 +#define VIRTIO_RPMB_RES_ADDR_FAILURE 0x0004 +#define VIRTIO_RPMB_RES_WRITE_FAILURE 0x0005 +#define VIRTIO_RPMB_RES_READ_FAILURE 0x0006 +#define VIRTIO_RPMB_RES_NO_AUTH_KEY 0x0007 +#define VIRTIO_RPMB_RES_WRITE_COUNTER_EXPIRED 0x0080 + + +#endif /* _UAPI_LINUX_VIRTIO_RPMB_H */