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

ath9k_pci_owl_loader.c (6420B)


      1// SPDX-License-Identifier: ISC
      2/* Initialize Owl Emulation Devices
      3 *
      4 * Copyright (C) 2016 Christian Lamparter <chunkeey@gmail.com>
      5 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
      6 *
      7 * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
      8 * need to be able to initialize the PCIe wifi device. Normally, this is done
      9 * during the early stages as a pci quirk.
     10 * However, this isn't possible for devices which have the init code for the
     11 * Atheros chip stored on UBI Volume on NAND. Hence, this module can be used to
     12 * initialize the chip when the user-space is ready to extract the init code.
     13 */
     14#include <linux/module.h>
     15#include <linux/completion.h>
     16#include <linux/etherdevice.h>
     17#include <linux/firmware.h>
     18#include <linux/pci.h>
     19#include <linux/delay.h>
     20#include <linux/platform_device.h>
     21#include <linux/ath9k_platform.h>
     22#include <linux/nvmem-consumer.h>
     23#include <linux/workqueue.h>
     24
     25struct owl_ctx {
     26	struct pci_dev *pdev;
     27	struct completion eeprom_load;
     28	struct work_struct work;
     29	struct nvmem_cell *cell;
     30};
     31
     32#define EEPROM_FILENAME_LEN 100
     33
     34#define AR5416_EEPROM_MAGIC 0xa55a
     35
     36static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
     37			   size_t cal_len)
     38{
     39	void __iomem *mem;
     40	const void *cal_end = (void *)cal_data + cal_len;
     41	const struct {
     42		u16 reg;
     43		u16 low_val;
     44		u16 high_val;
     45	} __packed * data;
     46	u16 cmd;
     47	u32 bar0;
     48	bool swap_needed = false;
     49
     50	/* also note that we are doing *u16 operations on the file */
     51	if (cal_len > 4096 || cal_len < 0x200 || (cal_len & 1) == 1) {
     52		dev_err(&pdev->dev, "eeprom has an invalid size.\n");
     53		return -EINVAL;
     54	}
     55
     56	if (*cal_data != AR5416_EEPROM_MAGIC) {
     57		if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
     58			dev_err(&pdev->dev, "invalid calibration data\n");
     59			return -EINVAL;
     60		}
     61
     62		dev_dbg(&pdev->dev, "calibration data needs swapping\n");
     63		swap_needed = true;
     64	}
     65
     66	dev_info(&pdev->dev, "fixup device configuration\n");
     67
     68	mem = pcim_iomap(pdev, 0, 0);
     69	if (!mem) {
     70		dev_err(&pdev->dev, "ioremap error\n");
     71		return -EINVAL;
     72	}
     73
     74	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
     75	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
     76			       pci_resource_start(pdev, 0));
     77	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
     78	cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
     79	pci_write_config_word(pdev, PCI_COMMAND, cmd);
     80
     81	/* set pointer to first reg address */
     82	for (data = (const void *)(cal_data + 3);
     83	     (const void *)data <= cal_end && data->reg != (u16)~0;
     84	     data++) {
     85		u32 val;
     86		u16 reg;
     87
     88		reg = data->reg;
     89		val = data->low_val;
     90		val |= ((u32)data->high_val) << 16;
     91
     92		if (swap_needed) {
     93			reg = swab16(reg);
     94			val = swahb32(val);
     95		}
     96
     97		iowrite32(val, mem + reg);
     98		usleep_range(100, 120);
     99	}
    100
    101	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
    102	cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
    103	pci_write_config_word(pdev, PCI_COMMAND, cmd);
    104
    105	pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
    106	pcim_iounmap(pdev, mem);
    107
    108	pci_disable_device(pdev);
    109
    110	return 0;
    111}
    112
    113static void owl_rescan(struct pci_dev *pdev)
    114{
    115	struct pci_bus *bus = pdev->bus;
    116
    117	pci_lock_rescan_remove();
    118	pci_stop_and_remove_bus_device(pdev);
    119	/* the device should come back with the proper
    120	 * ProductId. But we have to initiate a rescan.
    121	 */
    122	pci_rescan_bus(bus);
    123	pci_unlock_rescan_remove();
    124}
    125
    126static void owl_fw_cb(const struct firmware *fw, void *context)
    127{
    128	struct owl_ctx *ctx = (struct owl_ctx *)context;
    129
    130	complete(&ctx->eeprom_load);
    131
    132	if (fw) {
    133		ath9k_pci_fixup(ctx->pdev, (const u16 *)fw->data, fw->size);
    134		owl_rescan(ctx->pdev);
    135	} else {
    136		dev_err(&ctx->pdev->dev, "no eeprom data received.\n");
    137	}
    138	release_firmware(fw);
    139}
    140
    141static const char *owl_get_eeprom_name(struct pci_dev *pdev)
    142{
    143	struct device *dev = &pdev->dev;
    144	char *eeprom_name;
    145
    146	dev_dbg(dev, "using auto-generated eeprom filename\n");
    147
    148	eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
    149	if (!eeprom_name)
    150		return NULL;
    151
    152	/* this should match the pattern used in ath9k/init.c */
    153	scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
    154		  dev_name(dev));
    155
    156	return eeprom_name;
    157}
    158
    159static void owl_nvmem_work(struct work_struct *work)
    160{
    161	struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
    162	void *buf;
    163	size_t len;
    164
    165	complete(&ctx->eeprom_load);
    166
    167	buf = nvmem_cell_read(ctx->cell, &len);
    168	if (!IS_ERR(buf)) {
    169		ath9k_pci_fixup(ctx->pdev, buf, len);
    170		kfree(buf);
    171		owl_rescan(ctx->pdev);
    172	} else {
    173		dev_err(&ctx->pdev->dev, "no nvmem data received.\n");
    174	}
    175}
    176
    177static int owl_nvmem_probe(struct owl_ctx *ctx)
    178{
    179	int err;
    180
    181	ctx->cell = devm_nvmem_cell_get(&ctx->pdev->dev, "calibration");
    182	if (IS_ERR(ctx->cell)) {
    183		err = PTR_ERR(ctx->cell);
    184		if (err == -ENOENT || err == -EOPNOTSUPP)
    185			return 1; /* not present, try firmware_request */
    186
    187		return err;
    188	}
    189
    190	INIT_WORK(&ctx->work, owl_nvmem_work);
    191	schedule_work(&ctx->work);
    192
    193	return 0;
    194}
    195
    196static int owl_probe(struct pci_dev *pdev,
    197		     const struct pci_device_id *id)
    198{
    199	struct owl_ctx *ctx;
    200	const char *eeprom_name;
    201	int err = 0;
    202
    203	if (pcim_enable_device(pdev))
    204		return -EIO;
    205
    206	pcim_pin_device(pdev);
    207
    208	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
    209	if (!ctx)
    210		return -ENOMEM;
    211
    212	init_completion(&ctx->eeprom_load);
    213	ctx->pdev = pdev;
    214
    215	pci_set_drvdata(pdev, ctx);
    216
    217	err = owl_nvmem_probe(ctx);
    218	if (err <= 0)
    219		return err;
    220
    221	eeprom_name = owl_get_eeprom_name(pdev);
    222	if (!eeprom_name) {
    223		dev_err(&pdev->dev, "no eeprom filename found.\n");
    224		return -ENODEV;
    225	}
    226
    227	err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
    228				      &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
    229	if (err)
    230		dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
    231
    232	return err;
    233}
    234
    235static void owl_remove(struct pci_dev *pdev)
    236{
    237	struct owl_ctx *ctx = pci_get_drvdata(pdev);
    238
    239	if (ctx) {
    240		wait_for_completion(&ctx->eeprom_load);
    241		pci_set_drvdata(pdev, NULL);
    242	}
    243}
    244
    245static const struct pci_device_id owl_pci_table[] = {
    246	{ PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
    247	{ PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
    248	{ },
    249};
    250MODULE_DEVICE_TABLE(pci, owl_pci_table);
    251
    252static struct pci_driver owl_driver = {
    253	.name		= KBUILD_MODNAME,
    254	.id_table	= owl_pci_table,
    255	.probe		= owl_probe,
    256	.remove		= owl_remove,
    257};
    258module_pci_driver(owl_driver);
    259MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
    260MODULE_DESCRIPTION("External EEPROM data loader for Atheros AR500X to AR92XX");
    261MODULE_LICENSE("Dual BSD/GPL");