Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 209 lines 5.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2021 - Google LLC 4 * Author: David Brazdil <dbrazdil@google.com> 5 * 6 * Driver for Open Profile for DICE. 7 * 8 * This driver takes ownership of a reserved memory region containing data 9 * generated by the Open Profile for DICE measured boot protocol. The memory 10 * contents are not interpreted by the kernel but can be mapped into a userspace 11 * process via a misc device. Userspace can also request a wipe of the memory. 12 * 13 * Userspace can access the data with (w/o error handling): 14 * 15 * fd = open("/dev/open-dice0", O_RDWR); 16 * read(fd, &size, sizeof(unsigned long)); 17 * data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 18 * write(fd, NULL, 0); // wipe 19 * close(fd); 20 */ 21 22#include <linux/io.h> 23#include <linux/miscdevice.h> 24#include <linux/mm.h> 25#include <linux/module.h> 26#include <linux/of_reserved_mem.h> 27#include <linux/platform_device.h> 28 29#define DRIVER_NAME "open-dice" 30 31struct open_dice_drvdata { 32 struct mutex lock; 33 char name[16]; 34 struct reserved_mem *rmem; 35 struct miscdevice misc; 36}; 37 38static inline struct open_dice_drvdata *to_open_dice_drvdata(struct file *filp) 39{ 40 return container_of(filp->private_data, struct open_dice_drvdata, misc); 41} 42 43static int open_dice_wipe(struct open_dice_drvdata *drvdata) 44{ 45 void *kaddr; 46 47 mutex_lock(&drvdata->lock); 48 kaddr = devm_memremap(drvdata->misc.this_device, drvdata->rmem->base, 49 drvdata->rmem->size, MEMREMAP_WC); 50 if (IS_ERR(kaddr)) { 51 mutex_unlock(&drvdata->lock); 52 return PTR_ERR(kaddr); 53 } 54 55 memset(kaddr, 0, drvdata->rmem->size); 56 devm_memunmap(drvdata->misc.this_device, kaddr); 57 mutex_unlock(&drvdata->lock); 58 return 0; 59} 60 61/* 62 * Copies the size of the reserved memory region to the user-provided buffer. 63 */ 64static ssize_t open_dice_read(struct file *filp, char __user *ptr, size_t len, 65 loff_t *off) 66{ 67 unsigned long val = to_open_dice_drvdata(filp)->rmem->size; 68 69 return simple_read_from_buffer(ptr, len, off, &val, sizeof(val)); 70} 71 72/* 73 * Triggers a wipe of the reserved memory region. The user-provided pointer 74 * is never dereferenced. 75 */ 76static ssize_t open_dice_write(struct file *filp, const char __user *ptr, 77 size_t len, loff_t *off) 78{ 79 if (open_dice_wipe(to_open_dice_drvdata(filp))) 80 return -EIO; 81 82 /* Consume the input buffer. */ 83 return len; 84} 85 86/* 87 * Creates a mapping of the reserved memory region in user address space. 88 */ 89static int open_dice_mmap_prepare(struct vm_area_desc *desc) 90{ 91 struct file *filp = desc->file; 92 struct open_dice_drvdata *drvdata = to_open_dice_drvdata(filp); 93 94 if (vma_desc_test(desc, VMA_MAYSHARE_BIT)) { 95 /* Do not allow userspace to modify the underlying data. */ 96 if (vma_desc_test(desc, VMA_WRITE_BIT)) 97 return -EPERM; 98 /* Ensure userspace cannot acquire VM_WRITE later. */ 99 vma_desc_clear_flags(desc, VMA_MAYWRITE_BIT); 100 } 101 102 /* Create write-combine mapping so all clients observe a wipe. */ 103 desc->page_prot = pgprot_writecombine(desc->page_prot); 104 vma_desc_set_flags(desc, VMA_DONTCOPY_BIT, VMA_DONTDUMP_BIT); 105 mmap_action_simple_ioremap(desc, drvdata->rmem->base, 106 drvdata->rmem->size); 107 return 0; 108} 109 110static const struct file_operations open_dice_fops = { 111 .owner = THIS_MODULE, 112 .read = open_dice_read, 113 .write = open_dice_write, 114 .mmap_prepare = open_dice_mmap_prepare, 115}; 116 117static int __init open_dice_probe(struct platform_device *pdev) 118{ 119 static unsigned int dev_idx; 120 struct device *dev = &pdev->dev; 121 struct reserved_mem *rmem; 122 struct open_dice_drvdata *drvdata; 123 int ret; 124 125 rmem = of_reserved_mem_lookup(dev->of_node); 126 if (!rmem) { 127 dev_err(dev, "failed to lookup reserved memory\n"); 128 return -EINVAL; 129 } 130 131 if (!rmem->size || (rmem->size > ULONG_MAX)) { 132 dev_err(dev, "invalid memory region size\n"); 133 return -EINVAL; 134 } 135 136 if (!PAGE_ALIGNED(rmem->base) || !PAGE_ALIGNED(rmem->size)) { 137 dev_err(dev, "memory region must be page-aligned\n"); 138 return -EINVAL; 139 } 140 141 drvdata = devm_kmalloc(dev, sizeof(*drvdata), GFP_KERNEL); 142 if (!drvdata) 143 return -ENOMEM; 144 145 *drvdata = (struct open_dice_drvdata){ 146 .rmem = rmem, 147 .misc = (struct miscdevice){ 148 .parent = dev, 149 .name = drvdata->name, 150 .minor = MISC_DYNAMIC_MINOR, 151 .fops = &open_dice_fops, 152 .mode = 0600, 153 }, 154 }; 155 mutex_init(&drvdata->lock); 156 157 /* Index overflow check not needed, misc_register() will fail. */ 158 snprintf(drvdata->name, sizeof(drvdata->name), DRIVER_NAME"%u", dev_idx++); 159 160 ret = misc_register(&drvdata->misc); 161 if (ret) { 162 dev_err(dev, "failed to register misc device '%s': %d\n", 163 drvdata->name, ret); 164 return ret; 165 } 166 167 platform_set_drvdata(pdev, drvdata); 168 return 0; 169} 170 171static void open_dice_remove(struct platform_device *pdev) 172{ 173 struct open_dice_drvdata *drvdata = platform_get_drvdata(pdev); 174 175 misc_deregister(&drvdata->misc); 176} 177 178static const struct of_device_id open_dice_of_match[] = { 179 { .compatible = "google,open-dice" }, 180 {}, 181}; 182 183static struct platform_driver open_dice_driver = { 184 .remove = open_dice_remove, 185 .driver = { 186 .name = DRIVER_NAME, 187 .of_match_table = open_dice_of_match, 188 }, 189}; 190 191static int __init open_dice_init(void) 192{ 193 int ret = platform_driver_probe(&open_dice_driver, open_dice_probe); 194 195 /* DICE regions are optional. Succeed even with zero instances. */ 196 return (ret == -ENODEV) ? 0 : ret; 197} 198 199static void __exit open_dice_exit(void) 200{ 201 platform_driver_unregister(&open_dice_driver); 202} 203 204module_init(open_dice_init); 205module_exit(open_dice_exit); 206 207MODULE_DESCRIPTION("Driver for Open Profile for DICE."); 208MODULE_LICENSE("GPL v2"); 209MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");