Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
96 lines
2.7 KiB
C
96 lines
2.7 KiB
C
// 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 "core/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;
|
|
}
|