cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

devcoredump.c (9371B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright(c) 2014 Intel Mobile Communications GmbH
      4 * Copyright(c) 2015 Intel Deutschland GmbH
      5 *
      6 * Author: Johannes Berg <johannes@sipsolutions.net>
      7 */
      8#include <linux/module.h>
      9#include <linux/device.h>
     10#include <linux/devcoredump.h>
     11#include <linux/list.h>
     12#include <linux/slab.h>
     13#include <linux/fs.h>
     14#include <linux/workqueue.h>
     15
     16static struct class devcd_class;
     17
     18/* global disable flag, for security purposes */
     19static bool devcd_disabled;
     20
     21/* if data isn't read by userspace after 5 minutes then delete it */
     22#define DEVCD_TIMEOUT	(HZ * 60 * 5)
     23
     24struct devcd_entry {
     25	struct device devcd_dev;
     26	void *data;
     27	size_t datalen;
     28	struct module *owner;
     29	ssize_t (*read)(char *buffer, loff_t offset, size_t count,
     30			void *data, size_t datalen);
     31	void (*free)(void *data);
     32	struct delayed_work del_wk;
     33	struct device *failing_dev;
     34};
     35
     36static struct devcd_entry *dev_to_devcd(struct device *dev)
     37{
     38	return container_of(dev, struct devcd_entry, devcd_dev);
     39}
     40
     41static void devcd_dev_release(struct device *dev)
     42{
     43	struct devcd_entry *devcd = dev_to_devcd(dev);
     44
     45	devcd->free(devcd->data);
     46	module_put(devcd->owner);
     47
     48	/*
     49	 * this seems racy, but I don't see a notifier or such on
     50	 * a struct device to know when it goes away?
     51	 */
     52	if (devcd->failing_dev->kobj.sd)
     53		sysfs_delete_link(&devcd->failing_dev->kobj, &dev->kobj,
     54				  "devcoredump");
     55
     56	put_device(devcd->failing_dev);
     57	kfree(devcd);
     58}
     59
     60static void devcd_del(struct work_struct *wk)
     61{
     62	struct devcd_entry *devcd;
     63
     64	devcd = container_of(wk, struct devcd_entry, del_wk.work);
     65
     66	device_del(&devcd->devcd_dev);
     67	put_device(&devcd->devcd_dev);
     68}
     69
     70static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
     71			       struct bin_attribute *bin_attr,
     72			       char *buffer, loff_t offset, size_t count)
     73{
     74	struct device *dev = kobj_to_dev(kobj);
     75	struct devcd_entry *devcd = dev_to_devcd(dev);
     76
     77	return devcd->read(buffer, offset, count, devcd->data, devcd->datalen);
     78}
     79
     80static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
     81				struct bin_attribute *bin_attr,
     82				char *buffer, loff_t offset, size_t count)
     83{
     84	struct device *dev = kobj_to_dev(kobj);
     85	struct devcd_entry *devcd = dev_to_devcd(dev);
     86
     87	mod_delayed_work(system_wq, &devcd->del_wk, 0);
     88
     89	return count;
     90}
     91
     92static struct bin_attribute devcd_attr_data = {
     93	.attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
     94	.size = 0,
     95	.read = devcd_data_read,
     96	.write = devcd_data_write,
     97};
     98
     99static struct bin_attribute *devcd_dev_bin_attrs[] = {
    100	&devcd_attr_data, NULL,
    101};
    102
    103static const struct attribute_group devcd_dev_group = {
    104	.bin_attrs = devcd_dev_bin_attrs,
    105};
    106
    107static const struct attribute_group *devcd_dev_groups[] = {
    108	&devcd_dev_group, NULL,
    109};
    110
    111static int devcd_free(struct device *dev, void *data)
    112{
    113	struct devcd_entry *devcd = dev_to_devcd(dev);
    114
    115	flush_delayed_work(&devcd->del_wk);
    116	return 0;
    117}
    118
    119static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
    120			     char *buf)
    121{
    122	return sysfs_emit(buf, "%d\n", devcd_disabled);
    123}
    124
    125static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
    126			      const char *buf, size_t count)
    127{
    128	long tmp = simple_strtol(buf, NULL, 10);
    129
    130	/*
    131	 * This essentially makes the attribute write-once, since you can't
    132	 * go back to not having it disabled. This is intentional, it serves
    133	 * as a system lockdown feature.
    134	 */
    135	if (tmp != 1)
    136		return -EINVAL;
    137
    138	devcd_disabled = true;
    139
    140	class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
    141
    142	return count;
    143}
    144static CLASS_ATTR_RW(disabled);
    145
    146static struct attribute *devcd_class_attrs[] = {
    147	&class_attr_disabled.attr,
    148	NULL,
    149};
    150ATTRIBUTE_GROUPS(devcd_class);
    151
    152static struct class devcd_class = {
    153	.name		= "devcoredump",
    154	.owner		= THIS_MODULE,
    155	.dev_release	= devcd_dev_release,
    156	.dev_groups	= devcd_dev_groups,
    157	.class_groups	= devcd_class_groups,
    158};
    159
    160static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
    161			   void *data, size_t datalen)
    162{
    163	return memory_read_from_buffer(buffer, count, &offset, data, datalen);
    164}
    165
    166static void devcd_freev(void *data)
    167{
    168	vfree(data);
    169}
    170
    171/**
    172 * dev_coredumpv - create device coredump with vmalloc data
    173 * @dev: the struct device for the crashed device
    174 * @data: vmalloc data containing the device coredump
    175 * @datalen: length of the data
    176 * @gfp: allocation flags
    177 *
    178 * This function takes ownership of the vmalloc'ed data and will free
    179 * it when it is no longer used. See dev_coredumpm() for more information.
    180 */
    181void dev_coredumpv(struct device *dev, void *data, size_t datalen,
    182		   gfp_t gfp)
    183{
    184	dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev);
    185}
    186EXPORT_SYMBOL_GPL(dev_coredumpv);
    187
    188static int devcd_match_failing(struct device *dev, const void *failing)
    189{
    190	struct devcd_entry *devcd = dev_to_devcd(dev);
    191
    192	return devcd->failing_dev == failing;
    193}
    194
    195/**
    196 * devcd_free_sgtable - free all the memory of the given scatterlist table
    197 * (i.e. both pages and scatterlist instances)
    198 * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
    199 * using the sg_chain function then that function should be called only once
    200 * on the chained table
    201 * @data: pointer to sg_table to free
    202 */
    203static void devcd_free_sgtable(void *data)
    204{
    205	_devcd_free_sgtable(data);
    206}
    207
    208/**
    209 * devcd_read_from_sgtable - copy data from sg_table to a given buffer
    210 * and return the number of bytes read
    211 * @buffer: the buffer to copy the data to it
    212 * @buf_len: the length of the buffer
    213 * @data: the scatterlist table to copy from
    214 * @offset: start copy from @offset@ bytes from the head of the data
    215 *	in the given scatterlist
    216 * @data_len: the length of the data in the sg_table
    217 */
    218static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
    219				       size_t buf_len, void *data,
    220				       size_t data_len)
    221{
    222	struct scatterlist *table = data;
    223
    224	if (offset > data_len)
    225		return -EINVAL;
    226
    227	if (offset + buf_len > data_len)
    228		buf_len = data_len - offset;
    229	return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len,
    230				  offset);
    231}
    232
    233/**
    234 * dev_coredumpm - create device coredump with read/free methods
    235 * @dev: the struct device for the crashed device
    236 * @owner: the module that contains the read/free functions, use %THIS_MODULE
    237 * @data: data cookie for the @read/@free functions
    238 * @datalen: length of the data
    239 * @gfp: allocation flags
    240 * @read: function to read from the given buffer
    241 * @free: function to free the given buffer
    242 *
    243 * Creates a new device coredump for the given device. If a previous one hasn't
    244 * been read yet, the new coredump is discarded. The data lifetime is determined
    245 * by the device coredump framework and when it is no longer needed the @free
    246 * function will be called to free the data.
    247 */
    248void dev_coredumpm(struct device *dev, struct module *owner,
    249		   void *data, size_t datalen, gfp_t gfp,
    250		   ssize_t (*read)(char *buffer, loff_t offset, size_t count,
    251				   void *data, size_t datalen),
    252		   void (*free)(void *data))
    253{
    254	static atomic_t devcd_count = ATOMIC_INIT(0);
    255	struct devcd_entry *devcd;
    256	struct device *existing;
    257
    258	if (devcd_disabled)
    259		goto free;
    260
    261	existing = class_find_device(&devcd_class, NULL, dev,
    262				     devcd_match_failing);
    263	if (existing) {
    264		put_device(existing);
    265		goto free;
    266	}
    267
    268	if (!try_module_get(owner))
    269		goto free;
    270
    271	devcd = kzalloc(sizeof(*devcd), gfp);
    272	if (!devcd)
    273		goto put_module;
    274
    275	devcd->owner = owner;
    276	devcd->data = data;
    277	devcd->datalen = datalen;
    278	devcd->read = read;
    279	devcd->free = free;
    280	devcd->failing_dev = get_device(dev);
    281
    282	device_initialize(&devcd->devcd_dev);
    283
    284	dev_set_name(&devcd->devcd_dev, "devcd%d",
    285		     atomic_inc_return(&devcd_count));
    286	devcd->devcd_dev.class = &devcd_class;
    287
    288	if (device_add(&devcd->devcd_dev))
    289		goto put_device;
    290
    291	/*
    292	 * These should normally not fail, but there is no problem
    293	 * continuing without the links, so just warn instead of
    294	 * failing.
    295	 */
    296	if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
    297			      "failing_device") ||
    298	    sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
    299		              "devcoredump"))
    300		dev_warn(dev, "devcoredump create_link failed\n");
    301
    302	INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
    303	schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
    304
    305	return;
    306 put_device:
    307	put_device(&devcd->devcd_dev);
    308 put_module:
    309	module_put(owner);
    310 free:
    311	free(data);
    312}
    313EXPORT_SYMBOL_GPL(dev_coredumpm);
    314
    315/**
    316 * dev_coredumpsg - create device coredump that uses scatterlist as data
    317 * parameter
    318 * @dev: the struct device for the crashed device
    319 * @table: the dump data
    320 * @datalen: length of the data
    321 * @gfp: allocation flags
    322 *
    323 * Creates a new device coredump for the given device. If a previous one hasn't
    324 * been read yet, the new coredump is discarded. The data lifetime is determined
    325 * by the device coredump framework and when it is no longer needed
    326 * it will free the data.
    327 */
    328void dev_coredumpsg(struct device *dev, struct scatterlist *table,
    329		    size_t datalen, gfp_t gfp)
    330{
    331	dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable,
    332		      devcd_free_sgtable);
    333}
    334EXPORT_SYMBOL_GPL(dev_coredumpsg);
    335
    336static int __init devcoredump_init(void)
    337{
    338	return class_register(&devcd_class);
    339}
    340__initcall(devcoredump_init);
    341
    342static void __exit devcoredump_exit(void)
    343{
    344	class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
    345	class_unregister(&devcd_class);
    346}
    347__exitcall(devcoredump_exit);