From patchwork Tue Sep 13 13:23:00 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Winkler, Tomas" X-Patchwork-Id: 9329041 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8206B607FD for ; Tue, 13 Sep 2016 13:26:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 71E6C28F9D for ; Tue, 13 Sep 2016 13:26:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 65EBE2914A; Tue, 13 Sep 2016 13:26:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E987528F9D for ; Tue, 13 Sep 2016 13:25:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757230AbcIMNZg (ORCPT ); Tue, 13 Sep 2016 09:25:36 -0400 Received: from mga06.intel.com ([134.134.136.31]:8740 "EHLO mga06.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757178AbcIMNZc (ORCPT ); Tue, 13 Sep 2016 09:25:32 -0400 Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga104.jf.intel.com with ESMTP; 13 Sep 2016 06:25:31 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.30,328,1470726000"; d="scan'208";a="878254204" Received: from twinkler-lnx.jer.intel.com ([10.12.87.167]) by orsmga003.jf.intel.com with ESMTP; 13 Sep 2016 06:25:25 -0700 From: Tomas Winkler To: Greg Kroah-Hartman , Ulf Hansson , Adrian Hunter , James Bottomley , "Martin K . Petersen" , Vinayak Holikatti , Andy Lutomirski , =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= , Michael Ryleev , Joao Pinto , Christoph Hellwig , Yaniv Gardi Cc: Avri Altman , linux-kernel@vger.kernel.org, linux-mmc@vger.kernel.org, linux-scsi@vger.kernel.org, Tomas Winkler Subject: [PATCH v6 5/9] char: rpmb: provide a user space interface Date: Tue, 13 Sep 2016 16:23:00 +0300 Message-Id: <1473772984-17562-6-git-send-email-tomas.winkler@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1473772984-17562-1-git-send-email-tomas.winkler@intel.com> References: <1473772984-17562-1-git-send-email-tomas.winkler@intel.com> Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The user space API is achieved via two synchronous IOCTLs. Simplified one, RPMB_IOC_REQ_CMD, were read result cycles is performed by the framework on behalf the user and second, RPMB_IOC_SEQ_CMD where the whole RPMB sequence including RESULT_READ is supplied by the caller. The latter is intended for easier adjusting of the applications that use MMC_IOC_MULTI_CMD ioctl. Signed-off-by: Tomas Winkler --- V2: use memdup_user V3: commit message fix V4: resend V5: 1. Add RPMB_IOC_SEQ_CMD API. 2. Export uapi rpmb.h header V6: 1. remove #include 2. Add ioctl documentation Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 1 + drivers/char/rpmb/Kconfig | 7 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/cdev.c | 317 +++++++++++++++++++++++++++++++++++ drivers/char/rpmb/core.c | 9 +- drivers/char/rpmb/rpmb-cdev.h | 25 +++ include/linux/rpmb.h | 81 +-------- include/uapi/linux/Kbuild | 1 + include/uapi/linux/rpmb.h | 152 +++++++++++++++++ 10 files changed, 521 insertions(+), 74 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 diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 81c7f2bb7daf..f645543866c7 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments 0xB1 00-1F PPPoX 0xB3 00 linux/mmc/ioctl.h 0xB4 00-0F linux/gpio.h +0xB5 00-01 linux/uapi/linux/rpmb.h 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h 0xCA 80-8F uapi/scsi/cxlflash_ioctl.h diff --git a/MAINTAINERS b/MAINTAINERS index d701a972c83a..3a092b90add3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9999,6 +9999,7 @@ M: Tomas Winkler L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* +F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index c5e6e909efce..6794be9fcc5e 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -6,3 +6,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/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index 812b3ed264c0..b5dc087b1299 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -1,4 +1,5 @@ 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..837c32e722ec --- /dev/null +++ b/drivers/char/rpmb/cdev.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2015-2016 Intel Corp. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#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) */ +/* from MMC_IOC_MAX_CMDS */ +#define RPMB_MAX_FRAMES 255 + +/** + * 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_open - the open function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 on success, <0 on error + */ +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; +} + +/** + * rpmb_cmd_copy_from_user - copy rpmb command from the user space + * + * @cmd: internal cmd structure + * @ucmd: user space cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, + struct rpmb_ioc_cmd __user *ucmd) +{ + size_t sz; + struct rpmb_frame *frames; + + if (get_user(cmd->flags, &ucmd->flags)) + return -EFAULT; + + if (get_user(cmd->nframes, &ucmd->nframes)) + return -EFAULT; + + if (cmd->nframes > RPMB_MAX_FRAMES) + return -EINVAL; + + sz = cmd->nframes * sizeof(struct rpmb_frame); + frames = memdup_user(u64_to_user_ptr(ucmd->frames_ptr), sz); + if (IS_ERR(frames)) + return PTR_ERR(frames); + + cmd->frames = frames; + return 0; +} + +/** + * rpmb_cmd_copy_to_user - copy rpmb command the the user space + * + * @ucmd: user space cmd structure + * @cmd: internal cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, + struct rpmb_cmd *cmd) +{ + size_t sz; + + sz = cmd->nframes * sizeof(struct rpmb_frame); + if (copy_to_user(u64_to_user_ptr(ucmd->frames_ptr), cmd->frames, sz)) + return -EFAULT; + + return 0; +} + +/** + * rpmb_ioctl_seq_cmd: issue rpmb sequence + * + * @rdev: rpmb device + * @ptr: rpmb cmd sequence + * + * RPMB_IOC_SEQ_CMD handler + * + * Return: 0 on success, <0 on error + */ +static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_seq_cmd __user *ptr) +{ + __u64 ncmds; + struct rpmb_cmd *cmds; + struct rpmb_ioc_cmd __user *ucmds; + + int i; + int ret; + + /* The caller must have CAP_SYS_RAWIO, like mmc ioctl */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&ncmds, &ptr->num_of_cmds, sizeof(ncmds))) + return -EFAULT; + + if (ncmds > 3) { + dev_err(&rdev->dev, "supporting up to 3 packets (%llu)\n", + ncmds); + return -EINVAL; + } + + cmds = kcalloc(ncmds, sizeof(*cmds), GFP_KERNEL); + if (!cmds) + return -ENOMEM; + + ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]); + if (ret) + goto out; + } + + ret = rpmb_cmd_seq(rdev, cmds, ncmds); + if (ret) + goto out; + + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]); + if (ret) + goto out; + } +out: + for (i = 0; i < ncmds; i++) + kfree(cmds[i].frames); + kfree(cmds); + return ret; +} + +/** + * rpmb_ioctl_req_cmd: issue rpmb request command + * + * @rdev: rpmb device + * @ptr: rpmb request command + * + * RPMB_IOC_REQ_CMD handler + * + * Return: 0 on success; < 0 on error + */ +static long rpmb_ioctl_req_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_req_cmd __user *ptr) +{ + struct rpmb_data rpmbd; + u64 req_type; + int ret; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&req_type, &ptr->req_type, sizeof(req_type))) + return -EFAULT; + + if (req_type >= U16_MAX) + return -EINVAL; + + memset(&rpmbd, 0, sizeof(rpmbd)); + + rpmbd.req_type = req_type & 0xFFFF; + + ret = rpmb_cmd_copy_from_user(&rpmbd.icmd, &ptr->icmd); + if (ret) + goto out; + + ret = rpmb_cmd_copy_from_user(&rpmbd.ocmd, &ptr->ocmd); + if (ret) + goto out; + + ret = rpmb_cmd_req(rdev, &rpmbd); + if (ret) + goto out; + + ret = rpmb_cmd_copy_to_user(&ptr->ocmd, &rpmbd.ocmd); + +out: + kfree(rpmbd.icmd.frames); + kfree(rpmbd.ocmd.frames); + return ret; +} + +/** + * rpmb_ioctl - rpmb ioctl dispatcher + * + * @fp: a file pointer + * @cmd: ioctl command RPMB_IOC_REQ_CMD or RPMB_IOC_SEQ_CMD + * @arg: ioctl data: rpmb_ioc_req_cmd or rpmb_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_REQ_CMD: + return rpmb_ioctl_req_cmd(rdev, ptr); + case RPMB_IOC_SEQ_CMD: + return rpmb_ioctl_seq_cmd(rdev, ptr); + default: + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long rpmb_compat_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + return rpmb_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations rpmb_fops = { + .open = rpmb_open, + .release = rpmb_release, + .unlocked_ioctl = rpmb_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rpmb_compat_ioctl, +#endif + .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) +{ + if (rpmb_devt) + unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); +} diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index f6b4cabb0982..7707ccb37b23 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -20,6 +20,7 @@ #include #include +#include "rpmb-cdev.h" static DEFINE_IDA(rpmb_ida); @@ -429,6 +430,7 @@ int rpmb_dev_unregister(struct device *dev) rpmb_dev_put(rdev); mutex_lock(&rdev->lock); + rpmb_cdev_del(rdev); device_del(&rdev->dev); mutex_unlock(&rdev->lock); @@ -479,10 +481,14 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, rdev->dev.parent = dev; rdev->dev.groups = rpmb_attr_groups; + rpmb_cdev_prepare(rdev); + ret = device_register(&rdev->dev); if (ret) goto exit; + rpmb_cdev_add(rdev); + dev_dbg(&rdev->dev, "registered disk\n"); return rdev; @@ -499,11 +505,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..5fb41e586ba9 --- /dev/null +++ b/drivers/char/rpmb/rpmb-cdev.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015-2016 Intel Corp. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#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 ed4e372d5b13..1f3fa3fc5d67 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -15,79 +15,11 @@ #include #include +#include +#include #include /** - * struct rpmb_frame - rpmb frame as defined by specs - * - * @stuff : stuff bytes - * @key_mac : The authentication key or the message authentication - * code (MAC) depending on the request/response type. - * The MAC will be delivered in the last (or the only) - * block of data. - * @data : Data to be written or read by signed access. - * @nonce : Random number generated by the host for the requests - * and copied to the response by the RPMB engine. - * @write_counter: Counter value for the total amount of the successful - * authenticated data write requests made by the host. - * @addr : Address of the data to be programmed to or read - * from the RPMB. Address is the serial number of - * the accessed block (half sector 256B). - * @block_count : Number of blocks (half sectors, 256B) requested to be - * read/programmed. - * @result : Includes information about the status of the write counter - * (valid, expired) and result of the access made to the RPMB. - * @req_resp : Defines the type of request and response to/from the memory. - */ -struct rpmb_frame { - u8 stuff[196]; - u8 key_mac[32]; - u8 data[256]; - u8 nonce[16]; - __be32 write_counter; - __be16 addr; - __be16 block_count; - __be16 result; - __be16 req_resp; -} __packed; - -#define RPMB_PROGRAM_KEY 0x1 /* Program RPMB Authentication Key */ -#define RPMB_GET_WRITE_COUNTER 0x2 /* Read RPMB write counter */ -#define RPMB_WRITE_DATA 0x3 /* Write data to RPMB partition */ -#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */ -#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */ - -#define RPMB_REQ2RESP(_OP) ((_OP) << 8) -#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) - -/** - * enum rpmb_op_result - rpmb operation results - * - * @RPMB_ERR_OK : operation successful - * @RPMB_ERR_GENERAL : general failure - * @RPMB_ERR_AUTH : mac doesn't match or ac calculation failure - * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure - * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment - * @RPMB_ERR_WRITE : data, counter, or result write failure - * @RPMB_ERR_READ : data, counter, or result read failure - * @RPMB_ERR_NO_KEY : authentication key not yet programmed - * - * @RPMB_ERR_COUNTER_EXPIRED: counter expired - */ -enum rpmb_op_result { - RPMB_ERR_OK = 0x0000, - RPMB_ERR_GENERAL = 0x0001, - RPMB_ERR_AUTH = 0x0002, - RPMB_ERR_COUNTER = 0x0003, - RPMB_ERR_ADDRESS = 0x0004, - RPMB_ERR_WRITE = 0x0005, - RPMB_ERR_READ = 0x0006, - RPMB_ERR_NO_KEY = 0x0007, - - RPMB_ERR_COUNTER_EXPIRED = 0x0080 -}; - -/** * enum rpmb_type - type of underlaying storage technology * * @RPMB_TYPE_ANY : any type used for search only @@ -106,9 +38,6 @@ enum rpmb_type { extern struct class rpmb_class; -#define RPMB_F_WRITE BIT(0) -#define RPMB_F_REL_WRITE BIT(1) - /** * struct rpmb_cmd: rpmb access command * @@ -162,12 +91,18 @@ struct rpmb_ops { * @lock : the device lock * @dev : device * @id : device id + * @cdev : character dev + * @status : device status * @ops : operation exported by block layer */ struct rpmb_dev { struct mutex lock; /* device serialization lock */ struct device dev; int id; +#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/Kbuild b/include/uapi/linux/Kbuild index 185f8ea2702f..8ea2ae1952dc 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -362,6 +362,7 @@ header-y += rio_mport_cdev.h header-y += romfs_fs.h header-y += rose.h header-y += route.h +header-y += rpmb.h header-y += rtc.h header-y += rtnetlink.h header-y += scc.h diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file mode 100644 index 000000000000..a6fa78e03c4f --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015-2016, Intel Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _UAPI_LINUX_RPMB_H_ +#define _UAPI_LINUX_RPMB_H_ + +#include + +/** + * struct rpmb_frame - rpmb frame as defined by specs + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + */ +struct rpmb_frame { + __u8 stuff[196]; + __u8 key_mac[32]; + __u8 data[256]; + __u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __attribute__((packed)); + +#define RPMB_PROGRAM_KEY 0x1 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x2 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x3 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */ + +#define RPMB_REQ2RESP(_OP) ((_OP) << 8) +#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) + +/* length of the part of the frame used for HMAC computation */ +#define hmac_data_len \ + (sizeof(struct rpmb_frame) - offsetof(struct rpmb_frame, data)) + +/** + * enum rpmb_op_result - rpmb operation results + * + * @RPMB_ERR_OK: operation successful + * @RPMB_ERR_GENERAL: general failure + * @RPMB_ERR_AUTH: mac doesn't match or ac calculation failure + * @RPMB_ERR_COUNTER: counter doesn't match or counter increment failure + * @RPMB_ERR_ADDRESS: address out of range or wrong address alignment + * @RPMB_ERR_WRITE: data, counter, or result write failure + * @RPMB_ERR_READ: data, counter, or result read failure + * @RPMB_ERR_NO_KEY: authentication key not yet programmed + * + * @RPMB_ERR_COUNTER_EXPIRED: counter expired + */ +enum rpmb_op_result { + RPMB_ERR_OK = 0x0000, + RPMB_ERR_GENERAL = 0x0001, + RPMB_ERR_AUTH = 0x0002, + RPMB_ERR_COUNTER = 0x0003, + RPMB_ERR_ADDRESS = 0x0004, + RPMB_ERR_WRITE = 0x0005, + RPMB_ERR_READ = 0x0006, + RPMB_ERR_NO_KEY = 0x0007, + + RPMB_ERR_COUNTER_EXPIRED = 0x0080 +}; + +#define RPMB_F_WRITE (1UL << 0) +#define RPMB_F_REL_WRITE (1UL << 1) + +/** + * struct rpmb_cmd: rpmb access command + * + * @flags: command flags + * 0 - read command + * 1 - write commnad RPMB_F_WRITE + * 2 - reliable write RPMB_F_REL_WRITE + * @nframes: number of rpmb frames in the command + * @frames_ptr: a pointer to the list of rpmb frames + */ +struct rpmb_ioc_cmd { + __u32 flags; + __u32 nframes; + __aligned_u64 frames_ptr; +}; + +#define rpmb_ioc_cmd_set_frames(_cmd, _ptr) \ + (_cmd).frames_ptr = (__aligned_u64)(intptr_t)(_ptr) + +#define rpmb_ioc_cmd_set(_cmd, _flags, _ptr, _n) do { \ + (_cmd).flags = (_flags); \ + (_cmd).nframes = (_n); \ + (_cmd).frames_ptr = (__aligned_u64)(intptr_t)(_ptr); \ +} while (0) + +/** + * struct rpmb_ioc_req_cmd - rpmb operation request command + * + * @req_type: request type: must match the in frame req_resp + * program key + * get write counter + * write data + * read data + * @icmd: input command + * @ocmd: output/result command + */ +struct rpmb_ioc_req_cmd { + __u64 req_type; + struct rpmb_ioc_cmd icmd; + struct rpmb_ioc_cmd ocmd; +}; + +/** + * struct rpmb_ioc_seq_cmd - rpmb command sequence + * + * @num_of_cmds: number of commands + * @cmds: list of rpmb commands + */ +struct rpmb_ioc_seq_cmd { + __u64 num_of_cmds; + struct rpmb_ioc_cmd cmds[0]; +}; + +#define RPMB_IOC_REQ_CMD _IOWR(0xB5, 0, struct rpmb_ioc_req_cmd) +#define RPMB_IOC_SEQ_CMD _IOWR(0xB5, 1, struct rpmb_ioc_seq_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */