1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2021 - Google LLC
4 * Author: David Brazdil <dbrazdil@google.com>
6 * Driver for Open Profile for DICE.
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.
13 * Userspace can access the data with (w/o error handling):
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
23 #include <linux/miscdevice.h>
25 #include <linux/module.h>
26 #include <linux/of_reserved_mem.h>
27 #include <linux/platform_device.h>
29 #define DRIVER_NAME "open-dice"
31 struct open_dice_drvdata
{
34 struct reserved_mem
*rmem
;
35 struct miscdevice misc
;
38 static inline struct open_dice_drvdata
*to_open_dice_drvdata(struct file
*filp
)
40 return container_of(filp
->private_data
, struct open_dice_drvdata
, misc
);
43 static int open_dice_wipe(struct open_dice_drvdata
*drvdata
)
47 mutex_lock(&drvdata
->lock
);
48 kaddr
= devm_memremap(drvdata
->misc
.this_device
, drvdata
->rmem
->base
,
49 drvdata
->rmem
->size
, MEMREMAP_WC
);
51 mutex_unlock(&drvdata
->lock
);
52 return PTR_ERR(kaddr
);
55 memset(kaddr
, 0, drvdata
->rmem
->size
);
56 devm_memunmap(drvdata
->misc
.this_device
, kaddr
);
57 mutex_unlock(&drvdata
->lock
);
62 * Copies the size of the reserved memory region to the user-provided buffer.
64 static ssize_t
open_dice_read(struct file
*filp
, char __user
*ptr
, size_t len
,
67 unsigned long val
= to_open_dice_drvdata(filp
)->rmem
->size
;
69 return simple_read_from_buffer(ptr
, len
, off
, &val
, sizeof(val
));
73 * Triggers a wipe of the reserved memory region. The user-provided pointer
74 * is never dereferenced.
76 static ssize_t
open_dice_write(struct file
*filp
, const char __user
*ptr
,
77 size_t len
, loff_t
*off
)
79 if (open_dice_wipe(to_open_dice_drvdata(filp
)))
82 /* Consume the input buffer. */
87 * Creates a mapping of the reserved memory region in user address space.
89 static int open_dice_mmap(struct file
*filp
, struct vm_area_struct
*vma
)
91 struct open_dice_drvdata
*drvdata
= to_open_dice_drvdata(filp
);
93 if (vma
->vm_flags
& VM_MAYSHARE
) {
94 /* Do not allow userspace to modify the underlying data. */
95 if (vma
->vm_flags
& VM_WRITE
)
97 /* Ensure userspace cannot acquire VM_WRITE later. */
98 vm_flags_clear(vma
, VM_MAYWRITE
);
101 /* Create write-combine mapping so all clients observe a wipe. */
102 vma
->vm_page_prot
= pgprot_writecombine(vma
->vm_page_prot
);
103 vm_flags_set(vma
, VM_DONTCOPY
| VM_DONTDUMP
);
104 return vm_iomap_memory(vma
, drvdata
->rmem
->base
, drvdata
->rmem
->size
);
107 static const struct file_operations open_dice_fops
= {
108 .owner
= THIS_MODULE
,
109 .read
= open_dice_read
,
110 .write
= open_dice_write
,
111 .mmap
= open_dice_mmap
,
114 static int __init
open_dice_probe(struct platform_device
*pdev
)
116 static unsigned int dev_idx
;
117 struct device
*dev
= &pdev
->dev
;
118 struct reserved_mem
*rmem
;
119 struct open_dice_drvdata
*drvdata
;
122 rmem
= of_reserved_mem_lookup(dev
->of_node
);
124 dev_err(dev
, "failed to lookup reserved memory\n");
128 if (!rmem
->size
|| (rmem
->size
> ULONG_MAX
)) {
129 dev_err(dev
, "invalid memory region size\n");
133 if (!PAGE_ALIGNED(rmem
->base
) || !PAGE_ALIGNED(rmem
->size
)) {
134 dev_err(dev
, "memory region must be page-aligned\n");
138 drvdata
= devm_kmalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
142 *drvdata
= (struct open_dice_drvdata
){
144 .misc
= (struct miscdevice
){
146 .name
= drvdata
->name
,
147 .minor
= MISC_DYNAMIC_MINOR
,
148 .fops
= &open_dice_fops
,
152 mutex_init(&drvdata
->lock
);
154 /* Index overflow check not needed, misc_register() will fail. */
155 snprintf(drvdata
->name
, sizeof(drvdata
->name
), DRIVER_NAME
"%u", dev_idx
++);
157 ret
= misc_register(&drvdata
->misc
);
159 dev_err(dev
, "failed to register misc device '%s': %d\n",
164 platform_set_drvdata(pdev
, drvdata
);
168 static void open_dice_remove(struct platform_device
*pdev
)
170 struct open_dice_drvdata
*drvdata
= platform_get_drvdata(pdev
);
172 misc_deregister(&drvdata
->misc
);
175 static const struct of_device_id open_dice_of_match
[] = {
176 { .compatible
= "google,open-dice" },
180 static struct platform_driver open_dice_driver
= {
181 .remove
= open_dice_remove
,
184 .of_match_table
= open_dice_of_match
,
188 static int __init
open_dice_init(void)
190 int ret
= platform_driver_probe(&open_dice_driver
, open_dice_probe
);
192 /* DICE regions are optional. Succeed even with zero instances. */
193 return (ret
== -ENODEV
) ? 0 : ret
;
196 static void __exit
open_dice_exit(void)
198 platform_driver_unregister(&open_dice_driver
);
201 module_init(open_dice_init
);
202 module_exit(open_dice_exit
);
204 MODULE_DESCRIPTION("Driver for Open Profile for DICE.");
205 MODULE_LICENSE("GPL v2");
206 MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");