From patchwork Tue Apr 5 09:37:57 2022 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: 556446 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9DE6FC433FE for ; Tue, 5 Apr 2022 11:04:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241165AbiDEK4B (ORCPT ); Tue, 5 Apr 2022 06:56:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47312 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348949AbiDEJst (ORCPT ); Tue, 5 Apr 2022 05:48:49 -0400 Received: from mail-wr1-x432.google.com (mail-wr1-x432.google.com [IPv6:2a00:1450:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9E0F2A56EA for ; Tue, 5 Apr 2022 02:38:05 -0700 (PDT) Received: by mail-wr1-x432.google.com with SMTP id b19so18365874wrh.11 for ; Tue, 05 Apr 2022 02:38:05 -0700 (PDT) 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=NDQpY3c16JMV/jnTjMb+uHPqUg2ziPK08NP7ftGP+Bg=; b=GnJ5ccsU8yM52MY2dEtsITaa5H4dJ9cMEwh198EEPjheoMARjNFyADd/6dS78OVzGu AZwIJ1MuGn7tVuXhNvRlaID+FgWbOOK858AsaU1V3cFGb4D8Jlw4JFGmQtLKwdPxhIYK 4NeJRTtYYTRPD1TApLxhqLtNJdi8rQHHe2nMWPvl0mvNdpoHAPk60UznJ+UlUeZNlwE3 PjajDZH0G1qHhJYdCBx9yelx1XJPdqx5DqvQwcnTNLEIH2aKKZp7l2lDPIRXkm0PQaSA usVBu+B+Yj6+EIC2DDYr4165/qNI6X+ZFZq9FX/AQxW6rTT8NAmbC8UUcq5ldljQzULt OydA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NDQpY3c16JMV/jnTjMb+uHPqUg2ziPK08NP7ftGP+Bg=; b=PzYsdMqH3423VP7abzWh/gtZMT/9Ueeyi+5pxOvNZ51d684TcmhjkAQEiOid3oEzJT s4SbMAfVuyH1rVDrwGh+wumedC4IsKvXABLyfrDOJ91dWoCOH6wer8YeDM+9iFn28Zlo ZsoYNrigZ3QoG/1zGjMwOkiBj26zLQIs1a9jDZs22T/zPbzG4hwOFZkpr3QjkbHU/IPv fR1AXdEkQTsPNbY2DE/hMUpaiswC5ioNuzimWsyCvChHGst4LWnt0/9DAI24eDaCaj30 8G7JIBhfin6YVSOxqgGVBi87SNvBS6hnscuGqwqzS+rTKmF9zthnQuvf5QJrS/136pg6 CGWg== X-Gm-Message-State: AOAM533usQbv1j1qbWOuWWzMQl8FZ/+bmFLichzuWtRdPMd4HGsPGjRT BJNL3fC3dwx/8DNpxdunPPPRQg== X-Google-Smtp-Source: ABdhPJwvNLAXuiwz//DhXAexU6iWu8RMaTontHrUC9haKp9k1m6EKR+Sd1sCAs0RfRTiq+r12YiG1w== X-Received: by 2002:a5d:6905:0:b0:205:d510:c73a with SMTP id t5-20020a5d6905000000b00205d510c73amr2001129wru.275.1649151484085; Tue, 05 Apr 2022 02:38:04 -0700 (PDT) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id t9-20020adfa2c9000000b002061561d4a7sm4426805wra.96.2022.04.05.02.38.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Apr 2022 02:38:00 -0700 (PDT) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id 165501FFBA; Tue, 5 Apr 2022 10:38:00 +0100 (BST) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ulf.hansson@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, =?utf-8?q?Alex_Benn=C3=A9e?= , Linus Walleij , Arnd Bergmann , Alexander Usyskin , Avri Altman Subject: [PATCH v2 2/4] char: rpmb: provide a user space interface Date: Tue, 5 Apr 2022 10:37:57 +0100 Message-Id: <20220405093759.1126835-3-alex.bennee@linaro.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220405093759.1126835-1-alex.bennee@linaro.org> References: <20220405093759.1126835-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@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 operations which require authenticated frames or will respond with MAC hashes of nonce filled frames that userspace will need to verify share a common command frame format. The other operations can be considered generic and allow for common handling. [AJB: here the are key difference is the avoiding a single ioctl where all the frame data is put together by user space. User space is still the only place where certain operations can be verified due to the need of a secret key] 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 --- v2 - drop the key api stuff --- .../userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 1 + drivers/rpmb/Kconfig | 7 + drivers/rpmb/Makefile | 1 + drivers/rpmb/cdev.c | 309 ++++++++++++++++++ drivers/rpmb/core.c | 7 +- drivers/rpmb/rpmb-cdev.h | 17 + include/linux/rpmb.h | 10 + include/uapi/linux/rpmb.h | 99 ++++++ 9 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 drivers/rpmb/cdev.c create mode 100644 drivers/rpmb/rpmb-cdev.h create mode 100644 include/uapi/linux/rpmb.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index e6fce2cbd99e..874d01f11caf 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -355,6 +355,7 @@ Code Seq# Include File Comments 0xB6 all linux/fpga-dfl.h 0xB7 all uapi/linux/remoteproc_cdev.h 0xB7 all uapi/linux/nsfs.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 9ab02b589005..0a744da21817 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16749,6 +16749,7 @@ M: ? L: linux-kernel@vger.kernel.org S: Supported F: drivers/rpmb/* +F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h RPMSG TTY DRIVER diff --git a/drivers/rpmb/Kconfig b/drivers/rpmb/Kconfig index f2a9ebdc4435..126f336fe9ea 100644 --- a/drivers/rpmb/Kconfig +++ b/drivers/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 + help + Say yes here if you want to access RPMB from user space + via character device interface /dev/rpmb%d diff --git a/drivers/rpmb/Makefile b/drivers/rpmb/Makefile index 24d4752a9a53..f54b3f30514b 100644 --- a/drivers/rpmb/Makefile +++ b/drivers/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/rpmb/cdev.c b/drivers/rpmb/cdev.c new file mode 100644 index 000000000000..d9beeba53432 --- /dev/null +++ b/drivers/rpmb/cdev.c @@ -0,0 +1,309 @@ +// 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.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, struct rpmb_ioc_reqresp_cmd __user *ptr) +{ + struct rpmb_ioc_reqresp_cmd cmd; + u8 *request, *resp = NULL; + long ret; + + if (copy_from_user(&cmd, ptr, sizeof(struct rpmb_ioc_reqresp_cmd))) + return -EFAULT; + + request = kmalloc(cmd.len, GFP_KERNEL); + + if (!request) + return -ENOMEM; + + if (cmd.rlen && cmd.response) { + resp = kmalloc(cmd.rlen, GFP_KERNEL); + if (!resp) { + kfree(request); + return -ENOMEM; + } + } + + if (copy_from_user(request, cmd.request, cmd.len)) + ret = -EFAULT; + else + ret = rpmb_program_key(rdev, cmd.len, request, cmd.rlen, resp); + + if (!ret) + if (copy_to_user(cmd.response, resp, cmd.rlen)) + ret = -EFAULT; + + kfree(request); + kfree(resp); + + return ret; +} + +static long rpmb_ioctl_counter_cmd(struct rpmb_dev *rdev, struct rpmb_ioc_reqresp_cmd __user *ptr) +{ + struct rpmb_ioc_reqresp_cmd cmd; + u8 *request, *resp = NULL; + long count; + + if (copy_from_user(&cmd, ptr, sizeof(struct rpmb_ioc_reqresp_cmd))) + return -EFAULT; + + request = kmalloc(cmd.len, GFP_KERNEL); + + if (!request) + return -ENOMEM; + + if (cmd.rlen && cmd.response) { + resp = kmalloc(cmd.rlen, GFP_KERNEL); + if (!resp) { + kfree(request); + return -ENOMEM; + } + } + + if (copy_from_user(request, cmd.request, cmd.len)) { + count = -EFAULT; + } else { + count = rpmb_get_write_count(rdev, cmd.len, request, cmd.rlen, resp); + if (resp) + if (copy_to_user(cmd.response, resp, cmd.rlen)) + count = -EFAULT; + } + + kfree(request); + kfree(resp); + + return count; +} + +static long rpmb_ioctl_wblocks_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_reqresp_cmd __user *ptr) +{ + struct rpmb_ioc_reqresp_cmd cmd; + u8 *data, *resp = NULL; + + long ret; + + if (copy_from_user(&cmd, ptr, sizeof(struct rpmb_ioc_reqresp_cmd))) + return -EFAULT; + + data = kmalloc(cmd.len, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + if (cmd.rlen && cmd.response) { + resp = kmalloc(cmd.rlen, GFP_KERNEL); + if (!resp) { + kfree(data); + return -ENOMEM; + } + } + + if (copy_from_user(data, cmd.request, cmd.len)) + ret = -EFAULT; + else + ret = rpmb_write_blocks(rdev, cmd.len, data, cmd.rlen, resp); + + if (resp) + if (copy_to_user(cmd.response, resp, cmd.rlen)) + ret = -EFAULT; + + kfree(data); + kfree(resp); + + return ret; +} + +static long rpmb_ioctl_rblocks_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_rblocks_cmd __user *ptr) +{ + struct rpmb_ioc_rblocks_cmd rblocks; + long ret; + u8 *data; + + if (copy_from_user(&rblocks, ptr, sizeof(struct rpmb_ioc_rblocks_cmd))) + return -EFAULT; + + if (rblocks.count > rdev->ops->rd_cnt_max) + return -EINVAL; + + if (!rblocks.len || !rblocks.data) + return -EINVAL; + + data = kmalloc(rblocks.len, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + ret = rpmb_read_blocks(rdev, rblocks.addr, rblocks.count, rblocks.len, data); + + if (ret == 0) + ret = copy_to_user(rblocks.data, data, rblocks.len); + + 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/rpmb/core.c b/drivers/rpmb/core.c index 50b358a14db6..969536ba53cc 100644 --- a/drivers/rpmb/core.c +++ b/drivers/rpmb/core.c @@ -12,6 +12,7 @@ #include #include +#include "rpmb-cdev.h" static DEFINE_IDA(rpmb_ida); @@ -282,6 +283,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); @@ -401,6 +403,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; @@ -417,11 +421,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/rpmb/rpmb-cdev.h b/drivers/rpmb/rpmb-cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d --- /dev/null +++ b/drivers/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 4ed5e299623e..3b0731c07528 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -8,8 +8,12 @@ #include #include +#include +#include #include +#define RPMB_API_VERSION 0x80000001 + /** * struct rpmb_ops - RPMB ops to be implemented by underlying block device * @@ -54,6 +58,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 rpmb */ struct rpmb_dev { @@ -61,6 +67,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..2f4ee7279b1b --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,99 @@ +/* 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-2022 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; +} __packed; + +/** + * struct rpmb_ioc_reqresp_cmd - general purpose reqresp + * + * Most RPMB operations consist of a set of request frames and an + * optional response frame. If a response is requested the user must + * allocate enough space for the response, otherwise the fields should + * be set to 0/NULL. + * + * It is used for programming the key, reading the counter and writing + * blocks to the device. If the frames are malformed they may be + * rejected by the underlying driver or the device itself. + * + * Assuming the transaction succeeds it is still up to user space to + * validate the response and check MAC values correspond to the + * programmed keys. + * + * @len: length of write counter request + * @request: ptr to device specific request frame + * @rlen: length of response frame + * @resp: ptr to device specific response frame + */ +struct rpmb_ioc_reqresp_cmd { + __u32 len; + __u8 __user *request; + __u32 rlen; + __u8 __user *response; +} __packed; + +/** + * struct rpmb_ioc_rblocks_cmd - read blocks from RPMB + * + * @addr: index into device (units of 256B blocks) + * @count: number of 256B blocks + * @len: length of response frame + * @data: block data (in device specific framing) + * + * Reading blocks from an RPMB device doesn't require any specific + * authentication. However the result still needs to be validated by + * user space. + */ +struct rpmb_ioc_rblocks_cmd { + __u32 addr; + __u32 count; + __u32 len; + __u8 __user *data; +} __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 _IOWR(0xB8, 82, struct rpmb_ioc_reqresp_cmd) +#define RPMB_IOC_COUNTER_CMD _IOWR(0xB8, 84, struct rpmb_ioc_reqresp_cmd) +#define RPMB_IOC_WBLOCKS_CMD _IOWR(0xB8, 85, struct rpmb_ioc_reqresp_cmd) +#define RPMB_IOC_RBLOCKS_CMD _IOR(0xB8, 86, struct rpmb_ioc_rblocks_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */ From patchwork Tue Apr 5 09:37:58 2022 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: 556448 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1E07BC433FE for ; Tue, 5 Apr 2022 10:53:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240783AbiDEKzX (ORCPT ); Tue, 5 Apr 2022 06:55:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39808 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348954AbiDEJst (ORCPT ); Tue, 5 Apr 2022 05:48:49 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 97E9EA5EA6 for ; Tue, 5 Apr 2022 02:38:07 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id m30so18431186wrb.1 for ; Tue, 05 Apr 2022 02:38:07 -0700 (PDT) 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=5XIz5OfvKltqZSeXren24cN8/vWoQ7uO5RENAi6vLig=; b=UrUfEXIlvxSp8sIeO6xh/kGe1WpSq4OqI/LhLEgsXMMfzgl7OpApM9zMvJvcWAe7sp C1NkO5Z54qjRtT/gZ0XEOf2fVGk0aY/gFKs0y0BkRSO9RQDYk1SoBvSR1M11Kph0SKyW kwYz/F8IvDRqTIEx00WxT93T7B6U5KTDejqbmM1mwqf6upU9lzQsnXTdRNUF0jpXzL4w fAMflltmSitvXC1olNJQuRNSY8rOcDxVgYPk1879U/D6meXkohynmsgz2Wifn7ALuygs Y9xRoD/7cV9jGnmLZ8E/5S296sec2HmVZi7H92tJXDz8EHGl/KHP3F5RjnW/89ewCzRF ihsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5XIz5OfvKltqZSeXren24cN8/vWoQ7uO5RENAi6vLig=; b=Z8Q211RLVILPPXKYjMVOBWUY8BPNF2J23TqbBcEI0OW7HkH3gCPn1avlta+BqkI5gV TlmZsruQ3N4ravawX2LU8C/JCsdryWQMQIBvDt/bOdq6htMT2Nlp0RjnCZGbxCB2u6Oa vZIm7LJTjuYgvuPRCTWzvAdBiXsUYmA/YsRt6HyKAgN1JpM2bm36Z6ENfGS7iPMMbZ7T C4rmrQHW8Z1aLAbb6yw0GaVE+G2rmaiYD76CVwIs8UGWdfyrb0toF/WwaH1vCPJATM53 PyD+oXNBVADTYhbBWxUhgbpMWXb4enimmmnTdY/hS6IaCM7zeADKDlvnMU4Xd0Rp/i2r M1fw== X-Gm-Message-State: AOAM533FNSRioAjwISndMfMkSFzxQbU7f10a52KUicQzwAZYzi7wxWr4 DGTzQ970IWqbirBUPT+m8ggm0A== X-Google-Smtp-Source: ABdhPJyuOUieYVJh0pNqAbyEnkD6s6YTyfa0TavUfZNB+FdJRmfUgtO0dixX7I+3Wr7D2BXaYe9gtA== X-Received: by 2002:a5d:6782:0:b0:203:d6af:5869 with SMTP id v2-20020a5d6782000000b00203d6af5869mr1973756wru.213.1649151485956; Tue, 05 Apr 2022 02:38:05 -0700 (PDT) Received: from zen.linaroharston ([51.148.130.216]) by smtp.gmail.com with ESMTPSA id v15-20020a056000144f00b002057eac999fsm11955806wrx.76.2022.04.05.02.38.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Apr 2022 02:38:00 -0700 (PDT) Received: from zen.lan (localhost [127.0.0.1]) by zen.linaroharston (Postfix) with ESMTP id 238991FFBB; Tue, 5 Apr 2022 10:38:00 +0100 (BST) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: linux-kernel@vger.kernel.org Cc: maxim.uvarov@linaro.org, joakim.bech@linaro.org, ulf.hansson@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, =?utf-8?q?Alex_Benn=C3=A9e?= Subject: [PATCH v2 3/4] rpmb: create virtio rpmb frontend driver Date: Tue, 5 Apr 2022 10:37:58 +0100 Message-Id: <20220405093759.1126835-4-alex.bennee@linaro.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220405093759.1126835-1-alex.bennee@linaro.org> References: <20220405093759.1126835-1-alex.bennee@linaro.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This implements a virtio rpmb frontend driver for the RPMB subsystem. This driver conforms to the rpmb internal API which attempts to stay common for the majority of cases and only expose the low level frame details as required. In these cases it is up to something outside of the driver itself to do the appropriate MAC calculations. The driver does do some basic verification of the incoming data and will reject frames which are the wrong size or have the wrong commands in them. Signed-off-by: Alex Bennée Cc: Tomas Winkler --- ajb: - rewrite for new API --- drivers/rpmb/Kconfig | 10 + drivers/rpmb/Makefile | 1 + drivers/rpmb/virtio_rpmb.c | 518 +++++++++++++++++++++++++++++++ include/uapi/linux/virtio_rpmb.h | 54 ++++ 4 files changed, 583 insertions(+) create mode 100644 drivers/rpmb/virtio_rpmb.c create mode 100644 include/uapi/linux/virtio_rpmb.h diff --git a/drivers/rpmb/Kconfig b/drivers/rpmb/Kconfig index 126f336fe9ea..cd0d1bb10910 100644 --- a/drivers/rpmb/Kconfig +++ b/drivers/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 driver" + default n + depends on VIRTIO + select RPMB + help + Say yes here if you have a Virtio aware RPMB device or want to use + RPMB from a Virtual Machine images. + This device interface is only for guest/frontend virtio driver. diff --git a/drivers/rpmb/Makefile b/drivers/rpmb/Makefile index f54b3f30514b..4b397b50a42c 100644 --- a/drivers/rpmb/Makefile +++ b/drivers/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/rpmb/virtio_rpmb.c b/drivers/rpmb/virtio_rpmb.c new file mode 100644 index 000000000000..db309014a157 --- /dev/null +++ b/drivers/rpmb/virtio_rpmb.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtio RPMB Front End Driver + * + * Copyright (c) 2018-2019 Intel Corporation. + * Copyright (c) 2021-2022 Linaro Ltd. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMB_MAC_SIZE 32 +#define VIRTIO_RPMB_FRAME_SZ 512 + +static const char id[] = "RPMB:VIRTIO"; + +struct virtio_rpmb_info { + /* The virtio device we're associated with */ + struct virtio_device *vdev; + + /* The virtq we use */ + struct virtqueue *vq; + + struct mutex lock; /* info lock */ + wait_queue_head_t have_data; + + /* Underlying RPMB device */ + struct rpmb_dev *rdev; + + /* Config values */ + u8 max_wr, max_rd, capacity; +}; + +/** + * virtio_rpmb_recv_done() - vq completion callback + */ +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); +} + +/** + * do_virtio_transaction() - send sg list and wait for result + * @dev: linux device structure + * @vi: the device info (where the lock is) + * @sgs: array of scatterlists + * @out: total outgoing scatter lists + * @in: total returning scatter lists + * + * This is just a simple helper for processing the sg list. It will + * block until the response arrives. Returns number of bytes written + * back or negative if it failed. + */ +static int do_virtio_transaction(struct device *dev, + struct virtio_rpmb_info *vi, + struct scatterlist *sgs[], + int out, int in) +{ + int ret, len = 0; + + mutex_lock(&vi->lock); + ret = virtqueue_add_sgs(vi->vq, sgs, out, in, vi, GFP_KERNEL); + if (ret) { + dev_err(dev, "failed to send %d, recv %d sgs (%d) to vq\n", + out, in, ret); + ret = -1; + } else { + virtqueue_kick(vi->vq); + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + } + mutex_unlock(&vi->lock); + + return len; +} + +/** + * rpmb_virtio_program_key(): program key into virtio device + * @dev: device handle + * @target: target region (unused for VirtIO devices) + * @klen: length of key programming request + * @key_frame: key programming frames + * @rlen: length of response buffer + * @resp_frame: pointer to optional response frame + * + * Handle programming of the key (VIRTIO_RPMB_REQ_PROGRAM_KEY) + * + * The mandatory first frame contains the programming sequence. An + * optional second frame may ask for the result of the operation + * (VIRTIO_RPMB_REQ_RESULT_READ) which would trigger a response frame. + * + * Returns success/fail with errno and optional response frame + */ +static int rpmb_virtio_program_key(struct device *dev, u8 target, + int klen, u8 *key_frame, int rlen, u8 *resp_frame) +{ + struct virtio_rpmb_info *vi = dev_get_drvdata(dev); + struct virtio_rpmb_frame *pkey = (struct virtio_rpmb_frame *) key_frame; + struct virtio_rpmb_frame *resp = NULL; + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2] = { }; + int len; + + if (!pkey) + return -EINVAL; + + if (be16_to_cpu(pkey->req_resp) != VIRTIO_RPMB_REQ_PROGRAM_KEY) + return -EINVAL; + + /* validate incoming frame */ + switch (klen) { + case VIRTIO_RPMB_FRAME_SZ: + if (rlen || resp_frame) + return -EINVAL; + break; + case VIRTIO_RPMB_FRAME_SZ * 2: + if (!rlen || !resp_frame) + return -EINVAL; + if (be16_to_cpu(pkey[1].req_resp) != VIRTIO_RPMB_REQ_RESULT_READ) + return -EINVAL; + if (rlen < VIRTIO_RPMB_FRAME_SZ) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* setup outgoing frame(s) */ + sg_init_one(&out_frame, pkey, klen); + sgs[0] = &out_frame; + + /* optional incoming frame */ + if (rlen && resp_frame) { + resp = (struct virtio_rpmb_frame *) resp_frame; + sg_init_one(&in_frame, resp, sizeof(*resp)); + sgs[1] = &in_frame; + } + + len = do_virtio_transaction(dev, vi, sgs, 1, resp ? 1 : 0); + + if (len > 0 && resp) { + if (be16_to_cpu(resp->req_resp) != VIRTIO_RPMB_RESP_PROGRAM_KEY) { + dev_err(dev, "Bad response from device (%x/%x)", + be16_to_cpu(resp->req_resp), be16_to_cpu(resp->result)); + return -EPROTO; + } else { + /* map responses to better errors? */ + return be16_to_cpu(resp->result) == VIRTIO_RPMB_RES_OK ? 0 : -EIO; + } + } + + /* Something must have failed at this point. */ + return len < 0 ? -EIO : 0; +} + +static int rpmb_virtio_get_capacity(struct device *dev, u8 target) +{ + struct virtio_rpmb_info *vi = dev_get_drvdata(dev); + struct virtio_device *vdev = vi->vdev; + + 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, + int len, u8 *req, int rlen, u8 *resp) + +{ + struct virtio_rpmb_info *vi = dev_get_drvdata(dev); + struct virtio_rpmb_frame *request = (struct virtio_rpmb_frame *) req; + struct virtio_rpmb_frame *response = (struct virtio_rpmb_frame *) resp; + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2]; + unsigned int received; + + if (!len || len != VIRTIO_RPMB_FRAME_SZ || !request) + return -EINVAL; + + if (!rlen || rlen != VIRTIO_RPMB_FRAME_SZ || !resp) + return -EINVAL; + + if (be16_to_cpu(request->req_resp) != VIRTIO_RPMB_REQ_GET_WRITE_COUNTER) + return -EINVAL; + + /* Wrap into SG array */ + sg_init_one(&out_frame, request, VIRTIO_RPMB_FRAME_SZ); + sg_init_one(&in_frame, response, VIRTIO_RPMB_FRAME_SZ); + sgs[0] = &out_frame; + sgs[1] = &in_frame; + + /* Send it, blocks until response */ + received = do_virtio_transaction(dev, vi, sgs, 1, 1); + + if (received != VIRTIO_RPMB_FRAME_SZ) + return -EPROTO; + + if (be16_to_cpu(response->req_resp) != VIRTIO_RPMB_RESP_GET_COUNTER) { + dev_err(dev, "failed to get counter (%x/%x)", + be16_to_cpu(response->req_resp), be16_to_cpu(response->result)); + return -EPROTO; + } + + return be16_to_cpu(response->result) == VIRTIO_RPMB_RES_OK ? + be32_to_cpu(response->write_counter) : -EIO; +} + +static int rpmb_virtio_write_blocks(struct device *dev, u8 target, + int len, u8 *req, int rlen, u8 *resp) +{ + struct virtio_rpmb_info *vi = dev_get_drvdata(dev); + struct virtio_rpmb_frame *request = (struct virtio_rpmb_frame *) req; + struct virtio_rpmb_frame *response = (struct virtio_rpmb_frame *) resp; + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2]; + int blocks, data_len, received; + + if (!len || (len % VIRTIO_RPMB_FRAME_SZ) != 0 || !request) + return -EINVAL; + + /* The first frame will contain the details of the request */ + if (be16_to_cpu(request->req_resp) != VIRTIO_RPMB_REQ_DATA_WRITE) + return -EINVAL; + + blocks = be16_to_cpu(request->block_count); + if (blocks > vi->max_wr) + return -EINVAL; + + /* + * We either have exactly enough frames to write all the data + * or we have that plus a frame looking for a response. + */ + data_len = blocks * VIRTIO_RPMB_FRAME_SZ; + + if (len == data_len + VIRTIO_RPMB_FRAME_SZ) { + struct virtio_rpmb_frame *reply = &request[blocks]; + + if (be16_to_cpu(reply->req_resp) != VIRTIO_RPMB_REQ_RESULT_READ) + return -EINVAL; + + if (!rlen || rlen != VIRTIO_RPMB_FRAME_SZ || !resp) + return -EINVAL; + } else if (len > data_len) { + return -E2BIG; + } else if (len < data_len) { + return -ENOSPC; + } else if (rlen || resp) { + return -EINVAL; + } + + /* time to do the transaction */ + sg_init_one(&out_frame, request, len); + sgs[0] = &out_frame; + + /* optional incoming frame */ + if (rlen && resp) { + sg_init_one(&in_frame, resp, VIRTIO_RPMB_FRAME_SZ); + sgs[1] = &in_frame; + } + + received = do_virtio_transaction(dev, vi, sgs, 1, resp ? 1 : 0); + + if (response && received != VIRTIO_RPMB_FRAME_SZ) + return -EPROTO; + + if (response && be16_to_cpu(response->req_resp) != VIRTIO_RPMB_RESP_DATA_WRITE) { + dev_err(dev, "didn't get a response result (%x/%x)", + be16_to_cpu(response->req_resp), be16_to_cpu(response->result)); + return -EPROTO; + } + + return be16_to_cpu(response->result) == VIRTIO_RPMB_RES_OK ? 0 : -EIO; +} + +/** + * rpmb_virtio_read_blocks(): read blocks of data + * @dev: device handle + * @target: target region (unused for VirtIO devices) + * @addr: block address to start reading from + * @count: number of blocks to read + * @len: length of receiving buffer + * @data: receiving buffer + * + * Read a number of blocks from RPMB device. As there is no + * authentication required to read data we construct the outgoing + * frame in this driver. + * + * Returns success/fail with errno and filling in the buffer pointed + * to by @data. + */ +static int rpmb_virtio_read_blocks(struct device *dev, u8 target, + int addr, int count, int len, u8 *data) +{ + struct virtio_rpmb_info *vi = dev_get_drvdata(dev); + struct virtio_rpmb_frame *request; + struct virtio_rpmb_frame *response = (struct virtio_rpmb_frame *) data; + struct scatterlist out_frame; + struct scatterlist in_frame; + struct scatterlist *sgs[2]; + int computed_len = count * VIRTIO_RPMB_FRAME_SZ; + int received; + + if (!count || !data) + return -EINVAL; + + if (addr + count > vi->capacity) + return -ESPIPE; + + if (count > vi->max_rd) + return -EINVAL; + + /* EMSGSIZE? */ + if (len < computed_len) + return -EFBIG; + + /* + * With the basics done we can construct our request. + */ + request = kmalloc(VIRTIO_RPMB_FRAME_SZ, GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->req_resp = cpu_to_be16(VIRTIO_RPMB_REQ_DATA_READ); + request->block_count = cpu_to_be16(count); + request->address = cpu_to_be16(addr); + + /* time to do the transaction */ + sg_init_one(&out_frame, request, sizeof(*request)); + sgs[0] = &out_frame; + sg_init_one(&in_frame, data, len); + sgs[1] = &in_frame; + + received = do_virtio_transaction(dev, vi, sgs, 1, 1); + + kfree(request); + + if (received != computed_len) + return -EPROTO; + + if (be16_to_cpu(response->req_resp) != VIRTIO_RPMB_RESP_DATA_READ) { + dev_err(dev, "didn't get a response result (%x/%x)", + be16_to_cpu(response->req_resp), be16_to_cpu(response->result)); + return -EPROTO; + } + + return be16_to_cpu(response->result) == VIRTIO_RPMB_RES_OK ? 0 : -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, +}; + +static int rpmb_virtio_dev_init(struct virtio_rpmb_info *vi) +{ + struct virtio_device *vdev = vi->vdev; + /* XXX this seems very roundabout */ + struct device *dev = &vi->vq->vdev->dev; + int ret = 0; + + virtio_cread(vdev, struct virtio_rpmb_config, + max_wr_cnt, &vi->max_wr); + virtio_cread(vdev, struct virtio_rpmb_config, + max_rd_cnt, &vi->max_rd); + virtio_cread(vdev, struct virtio_rpmb_config, + capacity, &vi->capacity); + + rpmb_virtio_ops.dev_id_len = strlen(id); + rpmb_virtio_ops.dev_id = id; + rpmb_virtio_ops.wr_cnt_max = vi->max_wr; + rpmb_virtio_ops.rd_cnt_max = vi->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); + + /* link virtio_rpmb_info to virtio_device */ + vdev->priv = vi; + vi->vdev = vdev; + + /* 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_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 */