[PATCH v6 5/9] char: rpmb: provide a user space interface

Tomas Winkler
2016-09-13T13:25:32Z

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 <tomas.winkler@intel.com>
---
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 <linux/module.h>
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 <mailto:mostrows@styx.uwaterloo.ca>
0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
+0xB5 00-01 linux/uapi/linux/rpmb.h <mailto:linux-mei@linux.intel.com>
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 <tomas.winkler@intel.com>
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 <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+
+#include <linux/rpmb.h>
+
+#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 <linux/slab.h>

#include <linux/rpmb.h>
+#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 <linux/types.h>
#include <linux/device.h>
+#include <linux/cdev.h>
+#include <uapi/linux/rpmb.h>
#include <linux/kref.h>

/**
- * 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 <linux/types.h>
+
+/**
+ * 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_ */
--
2.7.4