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

l440gx.c (4080B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * BIOS Flash chip on Intel 440GX board.
      4 *
      5 * Bugs this currently does not work under linuxBIOS.
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/pci.h>
     10#include <linux/kernel.h>
     11#include <linux/init.h>
     12#include <asm/io.h>
     13#include <linux/mtd/mtd.h>
     14#include <linux/mtd/map.h>
     15
     16#define PIIXE_IOBASE_RESOURCE	11
     17
     18#define WINDOW_ADDR 0xfff00000
     19#define WINDOW_SIZE 0x00100000
     20#define BUSWIDTH 1
     21
     22static u32 iobase;
     23#define IOBASE iobase
     24#define TRIBUF_PORT (IOBASE+0x37)
     25#define VPP_PORT (IOBASE+0x28)
     26
     27static struct mtd_info *mymtd;
     28
     29
     30/* Is this really the vpp port? */
     31static DEFINE_SPINLOCK(l440gx_vpp_lock);
     32static int l440gx_vpp_refcnt;
     33static void l440gx_set_vpp(struct map_info *map, int vpp)
     34{
     35	unsigned long flags;
     36
     37	spin_lock_irqsave(&l440gx_vpp_lock, flags);
     38	if (vpp) {
     39		if (++l440gx_vpp_refcnt == 1)   /* first nested 'on' */
     40			outl(inl(VPP_PORT) | 1, VPP_PORT);
     41	} else {
     42		if (--l440gx_vpp_refcnt == 0)   /* last nested 'off' */
     43			outl(inl(VPP_PORT) & ~1, VPP_PORT);
     44	}
     45	spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
     46}
     47
     48static struct map_info l440gx_map = {
     49	.name = "L440GX BIOS",
     50	.size = WINDOW_SIZE,
     51	.bankwidth = BUSWIDTH,
     52	.phys = WINDOW_ADDR,
     53#if 0
     54	/* FIXME verify that this is the
     55	 * appripriate code for vpp enable/disable
     56	 */
     57	.set_vpp = l440gx_set_vpp
     58#endif
     59};
     60
     61static int __init init_l440gx(void)
     62{
     63	struct pci_dev *dev, *pm_dev;
     64	struct resource *pm_iobase;
     65	__u16 word;
     66
     67	dev = pci_get_device(PCI_VENDOR_ID_INTEL,
     68		PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
     69
     70	pm_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
     71		PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
     72
     73	pci_dev_put(dev);
     74
     75	if (!dev || !pm_dev) {
     76		printk(KERN_NOTICE "L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n");
     77		pci_dev_put(pm_dev);
     78		return -ENODEV;
     79	}
     80
     81	l440gx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
     82
     83	if (!l440gx_map.virt) {
     84		printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
     85		pci_dev_put(pm_dev);
     86		return -ENOMEM;
     87	}
     88	simple_map_init(&l440gx_map);
     89	pr_debug("window_addr = %p\n", l440gx_map.virt);
     90
     91	/* Setup the pm iobase resource
     92	 * This code should move into some kind of generic bridge
     93	 * driver but for the moment I'm content with getting the
     94	 * allocation correct.
     95	 */
     96	pm_iobase = &pm_dev->resource[PIIXE_IOBASE_RESOURCE];
     97	if (!(pm_iobase->flags & IORESOURCE_IO)) {
     98		pm_iobase->name = "pm iobase";
     99		pm_iobase->start = 0;
    100		pm_iobase->end = 63;
    101		pm_iobase->flags = IORESOURCE_IO;
    102
    103		/* Put the current value in the resource */
    104		pci_read_config_dword(pm_dev, 0x40, &iobase);
    105		iobase &= ~1;
    106		pm_iobase->start += iobase & ~1;
    107		pm_iobase->end += iobase & ~1;
    108
    109		pci_dev_put(pm_dev);
    110
    111		/* Allocate the resource region */
    112		if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
    113			pci_dev_put(dev);
    114			pci_dev_put(pm_dev);
    115			printk(KERN_WARNING "Could not allocate pm iobase resource\n");
    116			iounmap(l440gx_map.virt);
    117			return -ENXIO;
    118		}
    119	}
    120	/* Set the iobase */
    121	iobase = pm_iobase->start;
    122	pci_write_config_dword(pm_dev, 0x40, iobase | 1);
    123
    124
    125	/* Set XBCS# */
    126	pci_read_config_word(dev, 0x4e, &word);
    127	word |= 0x4;
    128        pci_write_config_word(dev, 0x4e, word);
    129
    130	/* Supply write voltage to the chip */
    131	l440gx_set_vpp(&l440gx_map, 1);
    132
    133	/* Enable the gate on the WE line */
    134	outb(inb(TRIBUF_PORT) & ~1, TRIBUF_PORT);
    135
    136       	printk(KERN_NOTICE "Enabled WE line to L440GX BIOS flash chip.\n");
    137
    138	mymtd = do_map_probe("jedec_probe", &l440gx_map);
    139	if (!mymtd) {
    140		printk(KERN_NOTICE "JEDEC probe on BIOS chip failed. Using ROM\n");
    141		mymtd = do_map_probe("map_rom", &l440gx_map);
    142	}
    143	if (mymtd) {
    144		mymtd->owner = THIS_MODULE;
    145
    146		mtd_device_register(mymtd, NULL, 0);
    147		return 0;
    148	}
    149
    150	iounmap(l440gx_map.virt);
    151	return -ENXIO;
    152}
    153
    154static void __exit cleanup_l440gx(void)
    155{
    156	mtd_device_unregister(mymtd);
    157	map_destroy(mymtd);
    158
    159	iounmap(l440gx_map.virt);
    160}
    161
    162module_init(init_l440gx);
    163module_exit(cleanup_l440gx);
    164
    165MODULE_LICENSE("GPL");
    166MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
    167MODULE_DESCRIPTION("MTD map driver for BIOS chips on Intel L440GX motherboards");