// SPDX-License-Identifier: GPL-2.0-or-later // // Register access. See ldls/kpi/kpi.h for the provenance rule. // // These are the busiest symbols by a wide margin -- 538 of 8139too's 552 probe // calls are two of them -- and they are also the shortest, because once a BAR // is mapped a register access *is* a memory access. The whole of what these // add over a plain dereference is the `volatile`, which stops a compiler // deciding that reading the same address twice is redundant. For a status // register, reading it twice is the entire point. // // The argument order is worth noting because it is the opposite of what a C // programmer expects and getting it backwards produces something that almost // works: Linux's writes take the **value first** and the address second. #include "types.h" #include "kpi.h" // Every access is checked against the mapping `pci_iomap` returned. // // A driver that reaches outside its own BAR would otherwise take the process // down with a page fault, and the report would be an address with no context. // This turns that into a line naming the offset it wanted -- and since there // is no IOMMU, a hosted driver's stray accesses are exactly the class of thing // that has to be noticed early or not at all. int kpi_io_ok(void *addr, int width, int write) { unsigned long a = (unsigned long)addr; if (kpi.io_base && a >= kpi.io_base && a + (unsigned long)width <= kpi.io_base + kpi.io_len) return 1; kpi.io_stray++; if (kpi.io_stray <= 8) { kpi_log(" kpi: the driver tried to "); kpi_log(write ? "write " : "read "); kpi_lognum(width); kpi_log(" byte(s) at 0x"); kpi_loghex(a, 16); if (kpi.io_base && a < kpi.io_base) { kpi_log(", which is "); kpi_lognum((long long)(kpi.io_base - a)); kpi_log(" below the mapping"); } else if (kpi.io_base) { kpi_log(", which is past the end of the mapping"); } else { kpi_log(", but nothing is mapped yet"); } kpi_log("\n"); } return 0; } unsigned char kpi_ioread8(void *addr) { if (!kpi_io_ok(addr, 1, 0)) return 0xff; return *(volatile unsigned char *)addr; } unsigned short kpi_ioread16(void *addr) { if (!kpi_io_ok(addr, 2, 0)) return 0xffff; return *(volatile unsigned short *)addr; } unsigned int kpi_ioread32(void *addr) { if (!kpi_io_ok(addr, 4, 0)) return 0xffffffff; return *(volatile unsigned int *)addr; } void kpi_iowrite8(unsigned char v, void *addr) { if (!kpi_io_ok(addr, 1, 1)) return; *(volatile unsigned char *)addr = v; } void kpi_iowrite16(unsigned short v, void *addr) { if (!kpi_io_ok(addr, 2, 1)) return; *(volatile unsigned short *)addr = v; } void kpi_iowrite32(unsigned int v, void *addr) { if (!kpi_io_ok(addr, 4, 1)) return; *(volatile unsigned int *)addr = v; }