redpill-lkm5/shim/pci_shim.c
2022-12-16 14:34:30 +02:00

244 lines
9.4 KiB
C

#define SHIM_NAME "PCI devices"
#include "pci_shim.h"
#include "shim_base.h"
#include "../common.h"
#include "../config/vpci_types.h" //MAX_VPCI_DEVS, pci_shim_device_type
#include "../config/platform_types.h" //hw_config
#include "../internal/virtual_pci.h"
#include <linux/pci_ids.h>
unsigned int free_dev_idx = 0;
static void *devices[MAX_VPCI_DEVS] = { NULL };
static struct pci_dev_descriptor *allocate_vpci_dev_dsc(void) {
if (free_dev_idx >= MAX_VPCI_DEVS) {
/*index has to be at max MAX_VPCI_DEVS-1*/
pr_loc_bug("No more device indexes are available (max devs: %d)", MAX_VPCI_DEVS);
return ERR_PTR(-ENOMEM);
}
struct pci_dev_descriptor *dev_dsc;
kmalloc_or_exit_ptr(dev_dsc, sizeof(struct pci_dev_descriptor));
memcpy(dev_dsc, &pci_dev_conf_default_normal_dev, sizeof(struct pci_dev_descriptor));
devices[free_dev_idx++] = dev_dsc;
return dev_dsc;
}
#define allocate_vpci_dev_dsc_var() \
struct pci_dev_descriptor *dev_dsc = allocate_vpci_dev_dsc(); \
if (IS_ERR(dev_dsc)) return PTR_ERR(dev_dsc);
static int
add_vdev(struct pci_dev_descriptor *dev_dsc, unsigned char bus_no, unsigned char dev_no, unsigned char fn_no,
bool is_mf)
{
const struct virtual_device *vpci_vdev;
if (is_mf) {
vpci_vdev = vpci_add_multifunction_device(bus_no, dev_no, fn_no, dev_dsc);
} else if(unlikely(fn_no != 0x00)) {
//Making such config will either cause the device to not show up at all or only fn_no=0 one will show u
pr_loc_bug("%s called with non-MF device but non-zero fn_no", __FUNCTION__);
return -EINVAL;
} else {
vpci_vdev = vpci_add_single_device(bus_no, dev_no, dev_dsc);
}
return IS_ERR(vpci_vdev) ? PTR_ERR(vpci_vdev) : 0;
}
/**
* Adds a fake Marvell controller
*
* These errors in kernlog are normal (as we don't emulate the behavior of the controller as it's not needed):
* pci 0001:0a:00.0: Can't map mv9235 registers
* ahci: probe of 0001:0a:00.0 failed with error -22
*
* @return 0 on success or -E
*/
static inline int
vdev_add_generic_marvell_ahci(u16 dev, unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_MARVELL_EXT;
dev_dsc->dev = dev;
dev_dsc->rev_id = 0x11; //All Marvells so far use revision 11
dev_dsc->class = U24_CLASS_TO_U8_CLASS(PCI_CLASS_STORAGE_SATA_AHCI);
dev_dsc->subclass = U24_CLASS_TO_U8_SUBCLASS(PCI_CLASS_STORAGE_SATA_AHCI);
dev_dsc->prog_if = U24_CLASS_TO_U8_PROGIF(PCI_CLASS_STORAGE_SATA_AHCI);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_MARVELL_88SE9235(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_marvell_ahci(0x9235, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_MARVELL_88SE9215(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_marvell_ahci(0x9215, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_I211(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = 0x1539;
dev_dsc->rev_id = 0x03; //Not confirmed
dev_dsc->class = U16_CLASS_TO_U8_CLASS(PCI_CLASS_NETWORK_ETHERNET);
dev_dsc->subclass = U16_CLASS_TO_U8_SUBCLASS(PCI_CLASS_NETWORK_ETHERNET);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_X552(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = 0x15ad;
dev_dsc->rev_id = 0x03; //Not confirmed
dev_dsc->class = U16_CLASS_TO_U8_CLASS(PCI_CLASS_NETWORK_ETHERNET);
dev_dsc->subclass = U16_CLASS_TO_U8_SUBCLASS(PCI_CLASS_NETWORK_ETHERNET);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_AHCI_CTRL(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = 0x5ae3;
dev_dsc->class = U24_CLASS_TO_U8_CLASS(PCI_CLASS_STORAGE_SATA_AHCI);
dev_dsc->subclass = U24_CLASS_TO_U8_SUBCLASS(PCI_CLASS_STORAGE_SATA_AHCI);
dev_dsc->prog_if = U24_CLASS_TO_U8_PROGIF(PCI_CLASS_STORAGE_SATA_AHCI);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
//This technically should be a bridge but we don't have the info to recreate full tree
static inline int
vdev_add_generic_intel_pcie(u16 dev, unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf) {
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = dev;
dev_dsc->class = U16_CLASS_TO_U8_CLASS(PCI_CLASS_BRIDGE_PCI);
dev_dsc->subclass = U16_CLASS_TO_U8_SUBCLASS(PCI_CLASS_BRIDGE_PCI);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_PCIE_PA(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_intel_pcie(0x5ad8, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_PCIE_PB(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_intel_pcie(0x5ad6, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_USB_XHCI(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = 0x5aa8;
dev_dsc->class = U24_CLASS_TO_U8_CLASS(PCI_CLASS_SERIAL_USB_XHCI);
dev_dsc->subclass = U24_CLASS_TO_U8_SUBCLASS(PCI_CLASS_SERIAL_USB_XHCI);
dev_dsc->prog_if = U24_CLASS_TO_U8_PROGIF(PCI_CLASS_SERIAL_USB_XHCI);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static inline int
vdev_add_generic_intel_io(u16 dev, unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = dev;
dev_dsc->class = U16_CLASS_TO_U8_CLASS(PCI_CLASS_SP_OTHER);
dev_dsc->subclass = U16_CLASS_TO_U8_SUBCLASS(PCI_CLASS_SP_OTHER);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_I2C(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_intel_io(0x5aac, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_HSUART(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_intel_io(0x5abc, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_SPI(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
return vdev_add_generic_intel_io(0x5ac6, bus_no, dev_no, fn_no, is_mf);
}
static int vdev_add_INTEL_CPU_SMBUS(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf)
{
allocate_vpci_dev_dsc_var();
dev_dsc->vid = PCI_VENDOR_ID_INTEL;
dev_dsc->dev = 0x5ad4;
dev_dsc->class = U16_CLASS_TO_U8_CLASS(PCI_CLASS_SERIAL_SMBUS);
dev_dsc->subclass = U16_CLASS_TO_U8_SUBCLASS(PCI_CLASS_SERIAL_SMBUS);
return add_vdev(dev_dsc, bus_no, dev_no, fn_no, is_mf);
}
static int (*dev_type_handler_map[])(unsigned char bus_no, unsigned char dev_no, unsigned char fn_no, bool is_mf) = {
[VPD_MARVELL_88SE9235] = vdev_add_MARVELL_88SE9235,
[VPD_MARVELL_88SE9215] = vdev_add_MARVELL_88SE9215,
[VPD_INTEL_I211] = vdev_add_INTEL_I211,
[VPD_INTEL_X552] = vdev_add_INTEL_X552,
[VPD_INTEL_CPU_AHCI_CTRL] = vdev_add_INTEL_CPU_AHCI_CTRL,
[VPD_INTEL_CPU_PCIE_PA] = vdev_add_INTEL_CPU_PCIE_PA,
[VPD_INTEL_CPU_PCIE_PB] = vdev_add_INTEL_CPU_PCIE_PB,
[VPD_INTEL_CPU_USB_XHCI] = vdev_add_INTEL_CPU_USB_XHCI,
[VPD_INTEL_CPU_I2C] = vdev_add_INTEL_CPU_I2C,
[VPD_INTEL_CPU_HSUART] = vdev_add_INTEL_CPU_HSUART,
[VPD_INTEL_CPU_SPI] = vdev_add_INTEL_CPU_SPI,
[VPD_INTEL_CPU_SMBUS] = vdev_add_INTEL_CPU_SMBUS,
};
int register_pci_shim(const struct hw_config *hw)
{
shim_reg_in();
pr_loc_dbg("Creating vPCI devices for %s", hw->name);
int out;
for (int i = 0; i < MAX_VPCI_DEVS; i++) {
if (hw->pci_stubs[i].type == __VPD_TERMINATOR__)
break;
pr_loc_dbg("Calling %ps with B:D:F=%02x:%02x:%02x mf=%d", dev_type_handler_map[hw->pci_stubs[i].type],
hw->pci_stubs[i].bus, hw->pci_stubs[i].dev, hw->pci_stubs[i].fn,
hw->pci_stubs[i].multifunction ? 1 : 0);
out = dev_type_handler_map[hw->pci_stubs[i].type](hw->pci_stubs[i].bus, hw->pci_stubs[i].dev,
hw->pci_stubs[i].fn, hw->pci_stubs[i].multifunction);
if (out != 0) {
pr_loc_err("Failed to create vPCI device B:D:F=%02x:%02x:%02x - error=%d", hw->pci_stubs[i].bus,
hw->pci_stubs[i].dev, hw->pci_stubs[i].fn, out);
return out;
}
pr_loc_dbg("vPCI device %d created successfully", i+1);
}
shim_reg_ok();
return 0;
}
int unregister_pci_shim(void)
{
shim_ureg_in();
vpci_remove_all_devices_and_buses();
for (int i = 0; i < free_dev_idx; i++) {
pr_loc_dbg("Free PCI dev %d @ %p", i, devices[i]);
kfree(devices[i]);
}
shim_ureg_ok();
return -EIO; //vpci_remove_all_devices_and_buses has a bug - this is a canary to not forget
}