0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include <linux/io.h>
0023 #include <linux/miscdevice.h>
0024 #include <linux/mm.h>
0025 #include <linux/module.h>
0026 #include <linux/of_reserved_mem.h>
0027 #include <linux/platform_device.h>
0028
0029 #define DRIVER_NAME "open-dice"
0030
0031 struct open_dice_drvdata {
0032 struct mutex lock;
0033 char name[16];
0034 struct reserved_mem *rmem;
0035 struct miscdevice misc;
0036 };
0037
0038 static inline struct open_dice_drvdata *to_open_dice_drvdata(struct file *filp)
0039 {
0040 return container_of(filp->private_data, struct open_dice_drvdata, misc);
0041 }
0042
0043 static int open_dice_wipe(struct open_dice_drvdata *drvdata)
0044 {
0045 void *kaddr;
0046
0047 mutex_lock(&drvdata->lock);
0048 kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base,
0049 drvdata->rmem->size, MEMREMAP_WC);
0050 if (IS_ERR(kaddr)) {
0051 mutex_unlock(&drvdata->lock);
0052 return PTR_ERR(kaddr);
0053 }
0054
0055 memset(kaddr, 0, drvdata->rmem->size);
0056 devm_memunmap(drvdata->misc.this_device, kaddr);
0057 mutex_unlock(&drvdata->lock);
0058 return 0;
0059 }
0060
0061
0062
0063
0064 static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len,
0065 loff_t *off)
0066 {
0067 unsigned long val = to_open_dice_drvdata(filp)->rmem->size;
0068
0069 return simple_read_from_buffer(ptr, len, off, &val, sizeof(val));
0070 }
0071
0072
0073
0074
0075
0076 static ssize_t open_dice_write(struct file *filp, const char __user *ptr,
0077 size_t len, loff_t *off)
0078 {
0079 if (open_dice_wipe(to_open_dice_drvdata(filp)))
0080 return -EIO;
0081
0082
0083 return len;
0084 }
0085
0086
0087
0088
0089 static int open_dice_mmap(struct file *filp, struct vm_area_struct *vma)
0090 {
0091 struct open_dice_drvdata *drvdata = to_open_dice_drvdata(filp);
0092
0093
0094 if ((vma->vm_flags & VM_WRITE) && (vma->vm_flags & VM_SHARED))
0095 return -EPERM;
0096
0097
0098 if (vma->vm_flags & VM_WRITE)
0099 vma->vm_flags &= ~VM_MAYSHARE;
0100 else if (vma->vm_flags & VM_SHARED)
0101 vma->vm_flags &= ~VM_MAYWRITE;
0102
0103
0104 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
0105 vma->vm_flags |= VM_DONTCOPY | VM_DONTDUMP;
0106 return vm_iomap_memory(vma, drvdata->rmem->base, drvdata->rmem->size);
0107 }
0108
0109 static const struct file_operations open_dice_fops = {
0110 .owner = THIS_MODULE,
0111 .read = open_dice_read,
0112 .write = open_dice_write,
0113 .mmap = open_dice_mmap,
0114 };
0115
0116 static int __init open_dice_probe(struct platform_device *pdev)
0117 {
0118 static unsigned int dev_idx;
0119 struct device *dev = &pdev->dev;
0120 struct reserved_mem *rmem;
0121 struct open_dice_drvdata *drvdata;
0122 int ret;
0123
0124 rmem = of_reserved_mem_lookup(dev->of_node);
0125 if (!rmem) {
0126 dev_err(dev, "failed to lookup reserved memory\n");
0127 return -EINVAL;
0128 }
0129
0130 if (!rmem->size || (rmem->size > ULONG_MAX)) {
0131 dev_err(dev, "invalid memory region size\n");
0132 return -EINVAL;
0133 }
0134
0135 if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) {
0136 dev_err(dev, "memory region must be page-aligned\n");
0137 return -EINVAL;
0138 }
0139
0140 drvdata = devm_kmalloc(dev, sizeof(*drvdata), GFP_KERNEL);
0141 if (!drvdata)
0142 return -ENOMEM;
0143
0144 *drvdata = (struct open_dice_drvdata){
0145 .lock = __MUTEX_INITIALIZER(drvdata->lock),
0146 .rmem = rmem,
0147 .misc = (struct miscdevice){
0148 .parent = dev,
0149 .name = drvdata->name,
0150 .minor = MISC_DYNAMIC_MINOR,
0151 .fops = &open_dice_fops,
0152 .mode = 0600,
0153 },
0154 };
0155
0156
0157 snprintf(drvdata->name, sizeof(drvdata->name), DRIVER_NAME"%u", dev_idx++);
0158
0159 ret = misc_register(&drvdata->misc);
0160 if (ret) {
0161 dev_err(dev, "failed to register misc device '%s': %d\n",
0162 drvdata->name, ret);
0163 return ret;
0164 }
0165
0166 platform_set_drvdata(pdev, drvdata);
0167 return 0;
0168 }
0169
0170 static int open_dice_remove(struct platform_device *pdev)
0171 {
0172 struct open_dice_drvdata *drvdata = platform_get_drvdata(pdev);
0173
0174 misc_deregister(&drvdata->misc);
0175 return 0;
0176 }
0177
0178 static const struct of_device_id open_dice_of_match[] = {
0179 { .compatible = "google,open-dice" },
0180 {},
0181 };
0182
0183 static struct platform_driver open_dice_driver = {
0184 .remove = open_dice_remove,
0185 .driver = {
0186 .name = DRIVER_NAME,
0187 .of_match_table = open_dice_of_match,
0188 },
0189 };
0190
0191 static int __init open_dice_init(void)
0192 {
0193 int ret = platform_driver_probe(&open_dice_driver, open_dice_probe);
0194
0195
0196 return (ret == -ENODEV) ? 0 : ret;
0197 }
0198
0199 static void __exit open_dice_exit(void)
0200 {
0201 platform_driver_unregister(&open_dice_driver);
0202 }
0203
0204 module_init(open_dice_init);
0205 module_exit(open_dice_exit);
0206
0207 MODULE_LICENSE("GPL v2");
0208 MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");