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

ksysfs.c (8057B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Architecture specific sysfs attributes in /sys/kernel
      4 *
      5 * Copyright (C) 2007, Intel Corp.
      6 *      Huang Ying <ying.huang@intel.com>
      7 * Copyright (C) 2013, 2013 Red Hat, Inc.
      8 *      Dave Young <dyoung@redhat.com>
      9 */
     10
     11#include <linux/kobject.h>
     12#include <linux/string.h>
     13#include <linux/sysfs.h>
     14#include <linux/init.h>
     15#include <linux/stat.h>
     16#include <linux/slab.h>
     17#include <linux/mm.h>
     18#include <linux/io.h>
     19
     20#include <asm/setup.h>
     21
     22static ssize_t version_show(struct kobject *kobj,
     23			    struct kobj_attribute *attr, char *buf)
     24{
     25	return sprintf(buf, "0x%04x\n", boot_params.hdr.version);
     26}
     27
     28static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version);
     29
     30static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj,
     31				     struct bin_attribute *bin_attr,
     32				     char *buf, loff_t off, size_t count)
     33{
     34	memcpy(buf, (void *)&boot_params + off, count);
     35	return count;
     36}
     37
     38static struct bin_attribute boot_params_data_attr = {
     39	.attr = {
     40		.name = "data",
     41		.mode = S_IRUGO,
     42	},
     43	.read = boot_params_data_read,
     44	.size = sizeof(boot_params),
     45};
     46
     47static struct attribute *boot_params_version_attrs[] = {
     48	&boot_params_version_attr.attr,
     49	NULL,
     50};
     51
     52static struct bin_attribute *boot_params_data_attrs[] = {
     53	&boot_params_data_attr,
     54	NULL,
     55};
     56
     57static const struct attribute_group boot_params_attr_group = {
     58	.attrs = boot_params_version_attrs,
     59	.bin_attrs = boot_params_data_attrs,
     60};
     61
     62static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr)
     63{
     64	const char *name;
     65
     66	name = kobject_name(kobj);
     67	return kstrtoint(name, 10, nr);
     68}
     69
     70static int get_setup_data_paddr(int nr, u64 *paddr)
     71{
     72	int i = 0;
     73	struct setup_data *data;
     74	u64 pa_data = boot_params.hdr.setup_data;
     75
     76	while (pa_data) {
     77		if (nr == i) {
     78			*paddr = pa_data;
     79			return 0;
     80		}
     81		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
     82		if (!data)
     83			return -ENOMEM;
     84
     85		pa_data = data->next;
     86		memunmap(data);
     87		i++;
     88	}
     89	return -EINVAL;
     90}
     91
     92static int __init get_setup_data_size(int nr, size_t *size)
     93{
     94	u64 pa_data = boot_params.hdr.setup_data, pa_next;
     95	struct setup_indirect *indirect;
     96	struct setup_data *data;
     97	int i = 0;
     98	u32 len;
     99
    100	while (pa_data) {
    101		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
    102		if (!data)
    103			return -ENOMEM;
    104		pa_next = data->next;
    105
    106		if (nr == i) {
    107			if (data->type == SETUP_INDIRECT) {
    108				len = sizeof(*data) + data->len;
    109				memunmap(data);
    110				data = memremap(pa_data, len, MEMREMAP_WB);
    111				if (!data)
    112					return -ENOMEM;
    113
    114				indirect = (struct setup_indirect *)data->data;
    115
    116				if (indirect->type != SETUP_INDIRECT)
    117					*size = indirect->len;
    118				else
    119					*size = data->len;
    120			} else {
    121				*size = data->len;
    122			}
    123
    124			memunmap(data);
    125			return 0;
    126		}
    127
    128		pa_data = pa_next;
    129		memunmap(data);
    130		i++;
    131	}
    132	return -EINVAL;
    133}
    134
    135static ssize_t type_show(struct kobject *kobj,
    136			 struct kobj_attribute *attr, char *buf)
    137{
    138	struct setup_indirect *indirect;
    139	struct setup_data *data;
    140	int nr, ret;
    141	u64 paddr;
    142	u32 len;
    143
    144	ret = kobj_to_setup_data_nr(kobj, &nr);
    145	if (ret)
    146		return ret;
    147
    148	ret = get_setup_data_paddr(nr, &paddr);
    149	if (ret)
    150		return ret;
    151	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
    152	if (!data)
    153		return -ENOMEM;
    154
    155	if (data->type == SETUP_INDIRECT) {
    156		len = sizeof(*data) + data->len;
    157		memunmap(data);
    158		data = memremap(paddr, len, MEMREMAP_WB);
    159		if (!data)
    160			return -ENOMEM;
    161
    162		indirect = (struct setup_indirect *)data->data;
    163
    164		ret = sprintf(buf, "0x%x\n", indirect->type);
    165	} else {
    166		ret = sprintf(buf, "0x%x\n", data->type);
    167	}
    168
    169	memunmap(data);
    170	return ret;
    171}
    172
    173static ssize_t setup_data_data_read(struct file *fp,
    174				    struct kobject *kobj,
    175				    struct bin_attribute *bin_attr,
    176				    char *buf,
    177				    loff_t off, size_t count)
    178{
    179	struct setup_indirect *indirect;
    180	struct setup_data *data;
    181	int nr, ret = 0;
    182	u64 paddr, len;
    183	void *p;
    184
    185	ret = kobj_to_setup_data_nr(kobj, &nr);
    186	if (ret)
    187		return ret;
    188
    189	ret = get_setup_data_paddr(nr, &paddr);
    190	if (ret)
    191		return ret;
    192	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
    193	if (!data)
    194		return -ENOMEM;
    195
    196	if (data->type == SETUP_INDIRECT) {
    197		len = sizeof(*data) + data->len;
    198		memunmap(data);
    199		data = memremap(paddr, len, MEMREMAP_WB);
    200		if (!data)
    201			return -ENOMEM;
    202
    203		indirect = (struct setup_indirect *)data->data;
    204
    205		if (indirect->type != SETUP_INDIRECT) {
    206			paddr = indirect->addr;
    207			len = indirect->len;
    208		} else {
    209			/*
    210			 * Even though this is technically undefined, return
    211			 * the data as though it is a normal setup_data struct.
    212			 * This will at least allow it to be inspected.
    213			 */
    214			paddr += sizeof(*data);
    215			len = data->len;
    216		}
    217	} else {
    218		paddr += sizeof(*data);
    219		len = data->len;
    220	}
    221
    222	if (off > len) {
    223		ret = -EINVAL;
    224		goto out;
    225	}
    226
    227	if (count > len - off)
    228		count = len - off;
    229
    230	if (!count)
    231		goto out;
    232
    233	ret = count;
    234	p = memremap(paddr, len, MEMREMAP_WB);
    235	if (!p) {
    236		ret = -ENOMEM;
    237		goto out;
    238	}
    239	memcpy(buf, p + off, count);
    240	memunmap(p);
    241out:
    242	memunmap(data);
    243	return ret;
    244}
    245
    246static struct kobj_attribute type_attr = __ATTR_RO(type);
    247
    248static struct bin_attribute data_attr __ro_after_init = {
    249	.attr = {
    250		.name = "data",
    251		.mode = S_IRUGO,
    252	},
    253	.read = setup_data_data_read,
    254};
    255
    256static struct attribute *setup_data_type_attrs[] = {
    257	&type_attr.attr,
    258	NULL,
    259};
    260
    261static struct bin_attribute *setup_data_data_attrs[] = {
    262	&data_attr,
    263	NULL,
    264};
    265
    266static const struct attribute_group setup_data_attr_group = {
    267	.attrs = setup_data_type_attrs,
    268	.bin_attrs = setup_data_data_attrs,
    269};
    270
    271static int __init create_setup_data_node(struct kobject *parent,
    272					 struct kobject **kobjp, int nr)
    273{
    274	int ret = 0;
    275	size_t size;
    276	struct kobject *kobj;
    277	char name[16]; /* should be enough for setup_data nodes numbers */
    278	snprintf(name, 16, "%d", nr);
    279
    280	kobj = kobject_create_and_add(name, parent);
    281	if (!kobj)
    282		return -ENOMEM;
    283
    284	ret = get_setup_data_size(nr, &size);
    285	if (ret)
    286		goto out_kobj;
    287
    288	data_attr.size = size;
    289	ret = sysfs_create_group(kobj, &setup_data_attr_group);
    290	if (ret)
    291		goto out_kobj;
    292	*kobjp = kobj;
    293
    294	return 0;
    295out_kobj:
    296	kobject_put(kobj);
    297	return ret;
    298}
    299
    300static void __init cleanup_setup_data_node(struct kobject *kobj)
    301{
    302	sysfs_remove_group(kobj, &setup_data_attr_group);
    303	kobject_put(kobj);
    304}
    305
    306static int __init get_setup_data_total_num(u64 pa_data, int *nr)
    307{
    308	int ret = 0;
    309	struct setup_data *data;
    310
    311	*nr = 0;
    312	while (pa_data) {
    313		*nr += 1;
    314		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
    315		if (!data) {
    316			ret = -ENOMEM;
    317			goto out;
    318		}
    319		pa_data = data->next;
    320		memunmap(data);
    321	}
    322
    323out:
    324	return ret;
    325}
    326
    327static int __init create_setup_data_nodes(struct kobject *parent)
    328{
    329	struct kobject *setup_data_kobj, **kobjp;
    330	u64 pa_data;
    331	int i, j, nr, ret = 0;
    332
    333	pa_data = boot_params.hdr.setup_data;
    334	if (!pa_data)
    335		return 0;
    336
    337	setup_data_kobj = kobject_create_and_add("setup_data", parent);
    338	if (!setup_data_kobj) {
    339		ret = -ENOMEM;
    340		goto out;
    341	}
    342
    343	ret = get_setup_data_total_num(pa_data, &nr);
    344	if (ret)
    345		goto out_setup_data_kobj;
    346
    347	kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL);
    348	if (!kobjp) {
    349		ret = -ENOMEM;
    350		goto out_setup_data_kobj;
    351	}
    352
    353	for (i = 0; i < nr; i++) {
    354		ret = create_setup_data_node(setup_data_kobj, kobjp + i, i);
    355		if (ret)
    356			goto out_clean_nodes;
    357	}
    358
    359	kfree(kobjp);
    360	return 0;
    361
    362out_clean_nodes:
    363	for (j = i - 1; j >= 0; j--)
    364		cleanup_setup_data_node(*(kobjp + j));
    365	kfree(kobjp);
    366out_setup_data_kobj:
    367	kobject_put(setup_data_kobj);
    368out:
    369	return ret;
    370}
    371
    372static int __init boot_params_ksysfs_init(void)
    373{
    374	int ret;
    375	struct kobject *boot_params_kobj;
    376
    377	boot_params_kobj = kobject_create_and_add("boot_params",
    378						  kernel_kobj);
    379	if (!boot_params_kobj) {
    380		ret = -ENOMEM;
    381		goto out;
    382	}
    383
    384	ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
    385	if (ret)
    386		goto out_boot_params_kobj;
    387
    388	ret = create_setup_data_nodes(boot_params_kobj);
    389	if (ret)
    390		goto out_create_group;
    391
    392	return 0;
    393out_create_group:
    394	sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
    395out_boot_params_kobj:
    396	kobject_put(boot_params_kobj);
    397out:
    398	return ret;
    399}
    400
    401arch_initcall(boot_params_ksysfs_init);