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

brcm_nvram.c (3822B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
      4 */
      5
      6#include <linux/io.h>
      7#include <linux/mod_devicetable.h>
      8#include <linux/module.h>
      9#include <linux/nvmem-consumer.h>
     10#include <linux/nvmem-provider.h>
     11#include <linux/of.h>
     12#include <linux/platform_device.h>
     13#include <linux/slab.h>
     14
     15#define NVRAM_MAGIC			"FLSH"
     16
     17struct brcm_nvram {
     18	struct device *dev;
     19	void __iomem *base;
     20	struct nvmem_cell_info *cells;
     21	int ncells;
     22};
     23
     24struct brcm_nvram_header {
     25	char magic[4];
     26	__le32 len;
     27	__le32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:31 sdram_init */
     28	__le32 config_refresh;	/* 0:15 sdram_config, 16:31 sdram_refresh */
     29	__le32 config_ncdl;	/* ncdl values for memc */
     30};
     31
     32static int brcm_nvram_read(void *context, unsigned int offset, void *val,
     33			   size_t bytes)
     34{
     35	struct brcm_nvram *priv = context;
     36	u8 *dst = val;
     37
     38	while (bytes--)
     39		*dst++ = readb(priv->base + offset++);
     40
     41	return 0;
     42}
     43
     44static int brcm_nvram_add_cells(struct brcm_nvram *priv, uint8_t *data,
     45				size_t len)
     46{
     47	struct device *dev = priv->dev;
     48	char *var, *value, *eq;
     49	int idx;
     50
     51	priv->ncells = 0;
     52	for (var = data + sizeof(struct brcm_nvram_header);
     53	     var < (char *)data + len && *var;
     54	     var += strlen(var) + 1) {
     55		priv->ncells++;
     56	}
     57
     58	priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL);
     59	if (!priv->cells)
     60		return -ENOMEM;
     61
     62	for (var = data + sizeof(struct brcm_nvram_header), idx = 0;
     63	     var < (char *)data + len && *var;
     64	     var = value + strlen(value) + 1, idx++) {
     65		eq = strchr(var, '=');
     66		if (!eq)
     67			break;
     68		*eq = '\0';
     69		value = eq + 1;
     70
     71		priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL);
     72		if (!priv->cells[idx].name)
     73			return -ENOMEM;
     74		priv->cells[idx].offset = value - (char *)data;
     75		priv->cells[idx].bytes = strlen(value);
     76		priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name);
     77	}
     78
     79	return 0;
     80}
     81
     82static int brcm_nvram_parse(struct brcm_nvram *priv)
     83{
     84	struct device *dev = priv->dev;
     85	struct brcm_nvram_header header;
     86	uint8_t *data;
     87	size_t len;
     88	int err;
     89
     90	memcpy_fromio(&header, priv->base, sizeof(header));
     91
     92	if (memcmp(header.magic, NVRAM_MAGIC, 4)) {
     93		dev_err(dev, "Invalid NVRAM magic\n");
     94		return -EINVAL;
     95	}
     96
     97	len = le32_to_cpu(header.len);
     98
     99	data = kcalloc(1, len, GFP_KERNEL);
    100	memcpy_fromio(data, priv->base, len);
    101	data[len - 1] = '\0';
    102
    103	err = brcm_nvram_add_cells(priv, data, len);
    104	if (err) {
    105		dev_err(dev, "Failed to add cells: %d\n", err);
    106		return err;
    107	}
    108
    109	kfree(data);
    110
    111	return 0;
    112}
    113
    114static int brcm_nvram_probe(struct platform_device *pdev)
    115{
    116	struct nvmem_config config = {
    117		.name = "brcm-nvram",
    118		.reg_read = brcm_nvram_read,
    119	};
    120	struct device *dev = &pdev->dev;
    121	struct resource *res;
    122	struct brcm_nvram *priv;
    123	int err;
    124
    125	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    126	if (!priv)
    127		return -ENOMEM;
    128	priv->dev = dev;
    129
    130	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    131	priv->base = devm_ioremap_resource(dev, res);
    132	if (IS_ERR(priv->base))
    133		return PTR_ERR(priv->base);
    134
    135	err = brcm_nvram_parse(priv);
    136	if (err)
    137		return err;
    138
    139	config.dev = dev;
    140	config.cells = priv->cells;
    141	config.ncells = priv->ncells;
    142	config.priv = priv;
    143	config.size = resource_size(res);
    144
    145	return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
    146}
    147
    148static const struct of_device_id brcm_nvram_of_match_table[] = {
    149	{ .compatible = "brcm,nvram", },
    150	{},
    151};
    152
    153static struct platform_driver brcm_nvram_driver = {
    154	.probe = brcm_nvram_probe,
    155	.driver = {
    156		.name = "brcm_nvram",
    157		.of_match_table = brcm_nvram_of_match_table,
    158	},
    159};
    160
    161static int __init brcm_nvram_init(void)
    162{
    163	return platform_driver_register(&brcm_nvram_driver);
    164}
    165
    166subsys_initcall_sync(brcm_nvram_init);
    167
    168MODULE_AUTHOR("Rafał Miłecki");
    169MODULE_LICENSE("GPL");
    170MODULE_DEVICE_TABLE(of, brcm_nvram_of_match_table);