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

simatic-ipc.c (5081B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Siemens SIMATIC IPC platform driver
      4 *
      5 * Copyright (c) Siemens AG, 2018-2021
      6 *
      7 * Authors:
      8 *  Henning Schild <henning.schild@siemens.com>
      9 *  Jan Kiszka <jan.kiszka@siemens.com>
     10 *  Gerd Haeussler <gerd.haeussler.ext@siemens.com>
     11 */
     12
     13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     14
     15#include <linux/dmi.h>
     16#include <linux/kernel.h>
     17#include <linux/module.h>
     18#include <linux/pci.h>
     19#include <linux/platform_data/x86/simatic-ipc.h>
     20#include <linux/platform_device.h>
     21
     22static struct platform_device *ipc_led_platform_device;
     23static struct platform_device *ipc_wdt_platform_device;
     24
     25static const struct dmi_system_id simatic_ipc_whitelist[] = {
     26	{
     27		.matches = {
     28			DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
     29		},
     30	},
     31	{}
     32};
     33
     34static struct simatic_ipc_platform platform_data;
     35
     36static struct {
     37	u32 station_id;
     38	u8 led_mode;
     39	u8 wdt_mode;
     40} device_modes[] = {
     41	{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
     42	{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
     43	{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
     44	{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
     45	{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
     46	{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
     47	{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
     48};
     49
     50static int register_platform_devices(u32 station_id)
     51{
     52	u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
     53	u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
     54	int i;
     55
     56	platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
     57
     58	for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
     59		if (device_modes[i].station_id == station_id) {
     60			ledmode = device_modes[i].led_mode;
     61			wdtmode = device_modes[i].wdt_mode;
     62			break;
     63		}
     64	}
     65
     66	if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
     67		platform_data.devmode = ledmode;
     68		ipc_led_platform_device =
     69			platform_device_register_data(NULL,
     70				KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE,
     71				&platform_data,
     72				sizeof(struct simatic_ipc_platform));
     73		if (IS_ERR(ipc_led_platform_device))
     74			return PTR_ERR(ipc_led_platform_device);
     75
     76		pr_debug("device=%s created\n",
     77			 ipc_led_platform_device->name);
     78	}
     79
     80	if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
     81		platform_data.devmode = wdtmode;
     82		ipc_wdt_platform_device =
     83			platform_device_register_data(NULL,
     84				KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
     85				&platform_data,
     86				sizeof(struct simatic_ipc_platform));
     87		if (IS_ERR(ipc_wdt_platform_device))
     88			return PTR_ERR(ipc_wdt_platform_device);
     89
     90		pr_debug("device=%s created\n",
     91			 ipc_wdt_platform_device->name);
     92	}
     93
     94	if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
     95	    wdtmode == SIMATIC_IPC_DEVICE_NONE) {
     96		pr_warn("unsupported IPC detected, station id=%08x\n",
     97			station_id);
     98		return -EINVAL;
     99	}
    100
    101	return 0;
    102}
    103
    104/* FIXME: this should eventually be done with generic P2SB discovery code
    105 * the individual drivers for watchdogs and LEDs access memory that implements
    106 * GPIO, but pinctrl will not come up because of missing ACPI entries
    107 *
    108 * While there is no conflict a cleaner solution would be to somehow bring up
    109 * pinctrl even with these ACPI entries missing, and base the drivers on pinctrl.
    110 * After which the following function could be dropped, together with the code
    111 * poking the memory.
    112 */
    113/*
    114 * Get membase address from PCI, used in leds and wdt module. Here we read
    115 * the bar0. The final address calculation is done in the appropriate modules
    116 */
    117u32 simatic_ipc_get_membase0(unsigned int p2sb)
    118{
    119	struct pci_bus *bus;
    120	u32 bar0 = 0;
    121	/*
    122	 * The GPIO memory is in bar0 of the hidden P2SB device.
    123	 * Unhide the device to have a quick look at it, before we hide it
    124	 * again.
    125	 * Also grab the pci rescan lock so that device does not get discovered
    126	 * and remapped while it is visible.
    127	 * This code is inspired by drivers/mfd/lpc_ich.c
    128	 */
    129	bus = pci_find_bus(0, 0);
    130	pci_lock_rescan_remove();
    131	pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0);
    132	pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0);
    133
    134	bar0 &= ~0xf;
    135	pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1);
    136	pci_unlock_rescan_remove();
    137
    138	return bar0;
    139}
    140EXPORT_SYMBOL(simatic_ipc_get_membase0);
    141
    142static int __init simatic_ipc_init_module(void)
    143{
    144	const struct dmi_system_id *match;
    145	u32 station_id;
    146	int err;
    147
    148	match = dmi_first_match(simatic_ipc_whitelist);
    149	if (!match)
    150		return 0;
    151
    152	err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
    153
    154	if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
    155		pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
    156		return 0;
    157	}
    158
    159	return register_platform_devices(station_id);
    160}
    161
    162static void __exit simatic_ipc_exit_module(void)
    163{
    164	platform_device_unregister(ipc_led_platform_device);
    165	ipc_led_platform_device = NULL;
    166
    167	platform_device_unregister(ipc_wdt_platform_device);
    168	ipc_wdt_platform_device = NULL;
    169}
    170
    171module_init(simatic_ipc_init_module);
    172module_exit(simatic_ipc_exit_module);
    173
    174MODULE_LICENSE("GPL v2");
    175MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
    176MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");