The user space API is achieved via single synchronous IOCTL. The request is submitted in_frames_ptr pointer and received in out_frames_ptr. Signed-off-by: Tomas Winkler <tomas.winkler@xxxxxxxxx> --- V2: use memdup_user Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 1 + drivers/char/rpmb/Kconfig | 7 ++ drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/cdev.c | 207 +++++++++++++++++++++++++++++++++++ drivers/char/rpmb/core.c | 9 +- drivers/char/rpmb/rpmb-cdev.h | 31 ++++++ include/linux/rpmb.h | 78 ++----------- include/uapi/linux/rpmb.h | 120 ++++++++++++++++++++ 9 files changed, 384 insertions(+), 71 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 9369d3b0f09a..51a514befa48 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -320,6 +320,7 @@ Code Seq#(hex) Include File Comments 0xB1 00-1F PPPoX <mailto:mostrows@xxxxxxxxxxxxxxxxx> 0xB3 00 linux/mmc/ioctl.h 0xB4 00-0F linux/gpio.h <mailto:linux-gpio@xxxxxxxxxxxxxxx> +0xB5 00 linux/uapi/linux/rpmb.h <mailto:linux-mei@xxxxxxxxxxxxxxx> 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 62b9edb8881c..07bd6f380460 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9422,6 +9422,7 @@ M: Tomas Winkler <tomas.winkler@xxxxxxxxx> L: linux-kernel@xxxxxxxxxxxxxxx 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..67e94943d3e3 --- /dev/null +++ b/drivers/char/rpmb/cdev.c @@ -0,0 +1,207 @@ +/* + * 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/module.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/compat.h> +#include <linux/slab.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) */ + +/** + * rpmb_open - the open function + * + * @inode: pointer to inode structure + * @file: pointer to file structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_open(struct inode *inode, struct file *file) +{ + 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); + + file->private_data = rdev; + + mutex_unlock(&rdev->lock); + + return nonseekable_open(inode, file); +} + +static int rpmb_release(struct inode *inode, struct file *file) +{ + struct rpmb_dev *rdev = file->private_data; + + clear_bit(RPMB_DEV_OPEN, &rdev->status); + + return 0; +} + +/* + * FIMXE: will be exported by the kernel in future version + * helper to convert user pointers passed inside __aligned_u64 fields + */ +static void __user *u64_to_ptr(__u64 val) +{ + return (void __user *) (unsigned long) val; +} + +static int rpmb_ioctl_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_cmd __user *ptr) +{ + struct rpmb_ioc_cmd cmd; + struct rpmb_data rpmbd; + struct rpmb_frame *in_frames = NULL; + struct rpmb_frame *out_frames = NULL; + size_t in_sz, out_sz; + int ret; + + if (copy_from_user(&cmd, ptr, sizeof(cmd))) + return -EFAULT; + + if (cmd.in_frames_count == 0 || cmd.out_frames_count == 0) + return -EINVAL; + + in_sz = sizeof(struct rpmb_frame) * cmd.in_frames_count; + in_frames = memdup_user(u64_to_ptr(cmd.in_frames_ptr), in_sz); + if (IS_ERR(in_frames)) { + ret = PTR_ERR(in_frames); + in_frames = NULL; + goto out; + } + + out_sz = sizeof(struct rpmb_frame) * cmd.out_frames_count; + out_frames = kmalloc(out_sz, GFP_KERNEL); + if (!out_frames) { + ret = -ENOMEM; + goto out; + } + + rpmbd.req_type = cmd.req; + rpmbd.in_frames = in_frames; + rpmbd.out_frames = out_frames; + rpmbd.in_frames_cnt = cmd.in_frames_count; + rpmbd.out_frames_cnt = cmd.out_frames_count; + + ret = rpmb_send_req(rdev, &rpmbd); + if (ret) { + dev_err(&rdev->dev, "Failed to process request = %d.\n", ret); + goto out; + } + + if (copy_to_user(u64_to_ptr(cmd.out_frames_ptr), out_frames, out_sz)) { + ret = -EFAULT; + goto out; + } + + if (copy_to_user(ptr, &cmd, sizeof(cmd))) { + ret = -EFAULT; + goto out; + } +out: + kfree(in_frames); + kfree(out_frames); + return ret; +} + +static long rpmb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct rpmb_dev *rdev = file->private_data; + struct rpmb_ioc_cmd __user *req = (void __user *)arg; + + if (cmd != RPMB_IOC_REQ) { + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } + + return rpmb_ioctl_cmd(rdev, req); +} + +#ifdef CONFIG_COMPAT +static long compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct rpmb_dev *rdev = file->private_data; + struct rpmb_ioc_cmd __user *req = (void __user *)compat_ptr(arg); + + if (cmd != RPMB_IOC_REQ) { + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } + + return rpmb_ioctl_cmd(rdev, req); +} +#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 = 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 fb401331d345..ce875d773ee4 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); @@ -313,6 +314,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); @@ -364,10 +366,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; @@ -384,11 +390,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..d20e0f16f1a1 --- /dev/null +++ b/drivers/char/rpmb/rpmb-cdev.h @@ -0,0 +1,31 @@ +/* + * 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 2c1e259f1a04..446c6639d275 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 @@ -144,12 +76,18 @@ struct rpmb_ops { * @lock : lock * @dev : device * @id : device id + * @cdev : character dev + * @status : device status * @ops : operation exported by block layer */ struct rpmb_dev { struct mutex 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/rpmb.h b/include/uapi/linux/rpmb.h new file mode 100644 index 000000000000..55aa92e11280 --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,120 @@ +/* + * 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 +}; + +/** + * struct rpmb_ioc_cmd - rpmb operation request command + * + * @req: request type: must match the in frame req_resp + * program key + * get write counter + * write/read data + * @in_frames_count: number of in frames + * @out_frames_count: number of out frames + * @in_frames_ptr: a pointer to the input frames buffer + * @out_frames_ptr: a pointer to output frames buffer + */ +struct rpmb_ioc_cmd { + __u32 req; + __u32 in_frames_count; + __u32 out_frames_count; + __aligned_u64 in_frames_ptr; + __aligned_u64 out_frames_ptr; +}; + +#define rpmb_ioc_cmd_set_in_frames(_ic, _ptr) \ + (_ic).in_frames_ptr = (__aligned_u64)(intptr_t)(_ptr) +#define rpmb_ioc_cmd_set_out_frames(_ic, _ptr) \ + (_ic).out_frames_ptr = (__aligned_u64)(intptr_t)(_ptr) + +#define RPMB_IOC_REQ _IOWR(0xB5, 1, struct rpmb_ioc_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */ -- 2.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html