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
170 lines
7.2 KiB
C
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);
|