SchOS/ldls/kpi/kpi.h
Nexoniarz 96389ada8f A text cursor in the tty; LDLS sets net_device.priv_len
The virtual terminals never drew a cursor. Each now paints a steady
block (the cell with its colours swapped), erased before and redrawn
after every output byte, redrawn by a repaint, and hidden by
ESC [ ? 25 l as curses programs ask.

r8169 under LDLS on the TUF A15 got through probe and died in ndo_open
on __fortify_panic. netdev_priv() is `dev->priv` now, a flexible array
counted by net_device.priv_len, and rtl_open's memset of its ring
bookkeeping is bounds-checked against it. The allocator left priv_len
zero, so the driver saw an area of no bytes. It is set from BTF when the
kernel has the member. __fortify_panic also says which check, how many
bytes it had and wanted, and where from.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX
2026-10-25 18:25:30 +01:00

170 lines
7.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// The KPI: the Linux symbols a hosted driver actually calls, implemented.
//
// docs/architecture/ldls.md, "The KPI". The stub passes measured what a driver asks for
// and in what order; this is what answers, for real, against `/dev/pci` and
// `/dev/netdev`.
//
// **Rule 3 governs every file here.** A symbol exists and takes these
// arguments is a fact and is reproduced; the comment above it, the macro body
// and the implementation are expression and are never carried over. What each
// function *does* is written from documentation and from what the driver's
// behaviour requires -- which is the part BTF cannot give and is the whole of
// the real work.
//
// Organised under Linux's own header names so it is navigable: a reader who
// knows where `pci_iomap` lives in Linux knows where it is here.
#include "core/types.h"
// One entry per symbol LDLS can answer. The loader consults this before
// falling back to a logging stub, so an unimplemented symbol still reports
// itself rather than silently returning zero -- which is what made L1's
// measurement possible and stays useful for exactly the same reason.
struct kpi_sym {
const char *name;
void *fn;
};
extern const struct kpi_sym kpi_table[];
extern const int kpi_count;
// Look one up. Returns 0 when LDLS has no implementation, which is not an
// error: it means the caller should install a stub and find out whether the
// driver ever calls it.
void *kpi_find(const char *name);
// The second table, in base.c. Searched first, so that a formatted printk
// there replaces the plain one in kpi.c.
extern const struct kpi_sym kpi_base_table[];
extern const int kpi_base_count;
void kpi_nop(void);
int kpi_zero(void);
unsigned long kpi_jiffies_ms(void);
// --- what the host gives the KPI ---------------------------------------------
//
// The KPI cannot open these for itself: `/dev/pci` is one device per process
// and the host has already claimed it. So the host hands over what it holds,
// once, before the driver runs.
struct kpi_ctx {
int pcifd; // /dev/pci, with the device claimed
int index; // which device, for the ioctls that name one
int netfd; // /dev/netdev, or -1 until register_netdev
// The BARs, as PCIIOC_INFO reported them. `pci_iomap` maps out of these and
// refuses anything else, which is the same check the kernel makes and is
// repeated here so the failure says which BAR rather than -EACCES.
unsigned long long bar_base[6];
unsigned long long bar_size[6];
unsigned int bar_flags[6];
// The mapping pci_iomap handed out, so register access can be checked
// against it. A foreign driver writing outside its own BAR is precisely the
// thing worth catching, and without an IOMMU it is also the thing nothing
// else will catch.
unsigned long io_base;
unsigned long io_len;
long io_stray; // accesses refused, so it is countable
unsigned char mac[6]; // filled in by dev_addr_mod
int have_mac;
};
extern struct kpi_ctx kpi;
// The struct offsets the KPI needs at run time, read from the kernel's own BTF
// by the host before the driver starts. See ldls/btf.h -- nothing here is
// written down, because a compiled driver and this code have to agree about
// them exactly and only the kernel that driver was built for can say.
struct kpi_layout {
unsigned int nd_netdev_ops;
unsigned int nd_dev_addr, nd_perm_addr, nd_addr_len;
unsigned int nd_priv_len; // 0 when this kernel has none (before 6.17)
unsigned int nd_mc, nd_uc;
unsigned int nd_tx, nd_num_tx, nd_real_num_tx;
unsigned int nq_size, nq_dev;
unsigned int nd_state, nd_flags;
unsigned int pd_dev, dev_kobj, kobj_name;
unsigned int ndo_open, ndo_stop, ndo_start_xmit;
// work_struct.func -- where a deferred work item keeps the function to call.
// `struct delayed_work` starts with a work_struct, so one offset serves both.
unsigned int work_func;
// __LINK_STATE_NOCARRIER, from BTF. `netif_carrier_ok` is an inline that
// tests this bit of net_device.state, so it is the one number that decides
// whether a driver believes its link came up.
unsigned int link_nocarrier;
unsigned int skb_size;
unsigned int skb_len, skb_data, skb_head, skb_tail, skb_end, skb_dev;
// The driver core (ldls/kpi/base.c): device.bus and .driver,
// device_driver.bus, .probe and .name, bus_type.match and .probe. core_ok
// says all of them were found; without them nothing is ever bound.
unsigned int dev_bus, dev_driver, drv_bus, drv_probe, drv_name;
unsigned int bus_match, bus_probe;
int core_ok, dev_kobj_ok;
// struct firmware: size and data, and how big it is.
unsigned int fw_size, fw_data, fw_size_of;
int fw_ok;
};
extern struct kpi_layout kpi_l;
// The driver's NAPI poll, captured when it registers one.
//
// A NAPI driver does almost nothing in its interrupt handler: it schedules,
// and the receive happens later in the poll. LDLS has no softirq to run that
// from, so it keeps the function pointer and calls it from its own loop --
// which is what a poll loop is, arrived at from the other direction.
extern int (*kpi_napi_poll)(void *napi, int budget);
extern void *kpi_napi;
// The driver's own interrupt handler, kept and called from the loop. See the
// note in kpi.c: this is not about receiving packets, it is about everything a
// driver only ever does from inside its handler.
int kpi_request_irq(unsigned int irq, void *handler, void *thread_fn,
unsigned long flags, const char *name, void *dev);
int kpi_free_irq(unsigned int irq, void *dev);
int kpi_irq_poll(void);
// A driver's deferred work, run from the same loop that polls. See work.c --
// e1000 enables its transmitter from a watchdog, so this is not optional.
int kpi_queue_work_on(int cpu, void *q, void *work);
int kpi_queue_delayed_work_on(int cpu, void *q, void *dwork, unsigned long delay);
int kpi_cancel_work_sync(void *work);
int kpi_cancel_delayed_work_sync(void *dwork);
int kpi_cancel_delayed_work(void *dwork);
int kpi_flush_work(void *work);
int kpi_work_run(void);
// The clock those deadlines are measured against, and the `jiffies` a driver
// reads directly.
extern unsigned long kpi_jiffies;
unsigned long kpi_jiffies_now(void);
// Output. The KPI is inside a process with no libc, and every implementation
// wants to be able to say what it did.
void kpi_log(const char *s);
void kpi_lognum(long long v);
void kpi_loghex(unsigned long long v, int digits);
// Whether to narrate every call. Register access is far too noisy to print by
// default -- 538 of 8139too's 552 probe calls are ioread8/iowrite8.
extern int kpi_verbose;
// Memory a device can be pointed at, in pieces smaller than a PCIIOC_DMA call.
// The skb path uses it, because a driver that maps an skb for its device needs
// the bus address of the data. See the long note in kpi.c.
void *kpi_dma_heap(unsigned long n, unsigned long align);
unsigned long long kpi_phys_of(const void *va);
// Is this a register address we handed out? Reports the first few that are
// not, then goes quiet -- a driver that is off the rails produces thousands.
int kpi_io_ok(void *addr, int width, int write);