SchOS/ldls/btf.h
Nexoniarz 2d9a0ec6e3 ldls: being the PCI core, with offsets nobody wrote down
docs/plan/ldls.md milestone L2, less the half that needs /dev/pci.

In Linux, __pci_register_driver ends at the PCI core, which matches a
driver's id_table against the bus and calls probe(). There is no PCI core
here, so LDLS keeps the pointer that call was given and plays the part.

To do that it must know where pci_driver.probe is, and it asks the kernel
rather than being told by us. ldls/btf.c reads /sys/kernel/btf/vmlinux --
122,279 types, every struct layout and every function prototype, as generated
data describing the kernel that is installed. Implemented from
Documentation/bpf/btf.rst, which is a published specification.

    $ ldls-types /sys/kernel/btf/vmlinux --need
      pci_driver   probe      offset    16  size 8
      pci_dev      resource   offset  1000  size 1088
      resource     start      offset     0  size 8
      all present

Cross-checked against pahole on the same file, field for field. No offset in
this is ours, which is rule 3's uncomfortable row disappearing rather than
being argued about.

And then the driver:

      driver name "8139too"
      devices it claims: 10ec:8139 ... and 16 more
      calling probe(pci_dev 10ec:8139, ...)
        1  alloc_etherdev_mqs(0x368, 1, 1)
        2  pci_enable_device / 3 pci_request_regions / 4 pci_set_master
        5  pci_iomap(pdev, 1, 0)
        6..544  ioread8 / iowrite8 -- 0x88, 0x8a, 0x8c, 0x8e, over and over
      545  dev_addr_mod / 547 netif_napi_add_weight_locked
      549  timer_init_key / 550 register_netdev / 551 netdev_info
      probe returned 0 after 552 stub call(s)

10ec:8139 is the real RTL8139 id. It mapped BAR 1, the one marked
IORESOURCE_MEM. Those 538 alternating byte accesses stepping 0x88/0x8a/0x8c/
0x8e are serial EEPROM clocking -- the driver bit-banging the chip for its MAC
address and handing the result to dev_addr_mod. Then NAPI, a watchdog timer,
register_netdev, its "found it" line, and zero.

Fifteen distinct symbols carry that entire probe, and three of them are 540 of
the 552 calls. L1 said 44 symbols carry 110 modules through init; L2 says 15
carry this one through probe. Neither is a number anyone would have guessed
and both came from running the thing.

Recorded as not done, because it is not: probe receives a pci_dev whose shape
is right and whose contents are invented. The BARs are made up, pci_iomap
returns a zeroed page, and 8139too read a MAC address of zeroes and was
perfectly happy about it. The "all through /dev/pci" half of L2 belongs to the
SchOS side. Also recorded: BTF has to come from somewhere, and SchOS has no
/sys/kernel/btf/vmlinux -- the user brings it with the module, from the kernel
the module was built for.

make ldls-check is 27 checks, four of them L2's, and it says so out loud when
it skips them on a machine with no BTF rather than passing silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
2026-09-14 08:14:57 +02:00

134 lines
4.7 KiB
C

// SPDX-License-Identifier: Apache-2.0
//
// Reading BTF: struct layouts and function prototypes, from the kernel that
// the driver was built for.
//
// docs/plan/ldls.md milestone L2, and the thing that makes rule 3 comfortable.
// The shim has to agree with a compiled driver about where `pci_driver.probe`
// sits, and a compiled driver is not negotiable about it. Writing those
// offsets into our source would mean a field-for-field match with Linux's
// headers, which *looks* like transcription however it was arrived at.
//
// It does not have to be in our source. A kernel built with
// CONFIG_DEBUG_INFO_BTF publishes `/sys/kernel/btf/vmlinux`: every struct
// layout and every function prototype, as generated binary type data
// describing the kernel that is installed. LDLS reads it at load time, on the
// user's machine, from the user's own kernel.
//
// Two things follow. The offsets are never ours to have copied, and they are
// right for the kernel actually present rather than one we pinned by guessing
// -- which is most of what rule 4 was worried about.
//
// The format is described in Documentation/bpf/btf.rst, which is a published
// specification; implementing it from that is the case ../docs/licensing.md §5
// covers. Freestanding, like the rest of ldls/.
// --- the format -------------------------------------------------------------
#define BTF_MAGIC 0xeB9F
struct btf_header {
unsigned short magic;
unsigned char version;
unsigned char flags;
unsigned int hdr_len;
// Both offsets are from the end of the header, not from the start of it.
unsigned int type_off;
unsigned int type_len;
unsigned int str_off;
unsigned int str_len;
};
struct btf_type {
unsigned int name_off;
// vlen in bits 0-15, kind in bits 24-28, kind_flag in bit 31.
unsigned int info;
union {
unsigned int size; // STRUCT, UNION, ENUM, INT, DATASEC, FLOAT
unsigned int type; // PTR, TYPEDEF, CONST, FUNC, VAR, ...
} u;
};
struct btf_member {
unsigned int name_off;
unsigned int type;
// With kind_flag set this is a bitfield: size in bits 24-31, bit offset in
// bits 0-23. Without it, a plain bit offset.
unsigned int offset;
};
struct btf_param {
unsigned int name_off;
unsigned int type;
};
#define BTF_KIND_INT 1
#define BTF_KIND_PTR 2
#define BTF_KIND_ARRAY 3
#define BTF_KIND_STRUCT 4
#define BTF_KIND_UNION 5
#define BTF_KIND_ENUM 6
#define BTF_KIND_FWD 7
#define BTF_KIND_TYPEDEF 8
#define BTF_KIND_VOLATILE 9
#define BTF_KIND_CONST 10
#define BTF_KIND_RESTRICT 11
#define BTF_KIND_FUNC 12
#define BTF_KIND_FUNC_PROTO 13
#define BTF_KIND_VAR 14
#define BTF_KIND_DATASEC 15
#define BTF_KIND_FLOAT 16
#define BTF_KIND_DECL_TAG 17
#define BTF_KIND_TYPE_TAG 18
#define BTF_KIND_ENUM64 19
#define BTF_INFO_KIND(i) (((i) >> 24) & 0x1f)
#define BTF_INFO_VLEN(i) ((i) & 0xffff)
#define BTF_INFO_KFLAG(i) (((i) >> 31) & 1)
// --- the reader ---------------------------------------------------------------
#define BTF_MAXTYPES 262144 // type ids we will index; vmlinux is ~150k
struct btf {
const unsigned char *img;
unsigned long long len;
const struct btf_header *h;
const unsigned char *types; // start of the type section
unsigned int types_len;
const char *str;
unsigned int str_len;
// Type id -> byte offset into the type section. Id 0 is void and has no
// entry, so this is 1-based and index 0 is unused.
unsigned int *off; // the caller's, ntypes + 1 entries
unsigned int ntypes;
char err[128];
};
// How many type ids this BTF holds, so the caller can size the index. -1 if
// the image is not BTF at all.
long btf_count(const void *image, unsigned long long len);
// Index it. `off` must have btf_count()+1 entries.
int btf_open(struct btf *b, const void *image, unsigned long long len,
unsigned int *off, unsigned int noff);
// The type id of a named type of a given kind, or 0.
int btf_find(struct btf *b, const char *name, int kind);
// Byte offset and size of a named member of a struct or union.
//
// Returns 0 on success. Bitfields are refused rather than rounded: a driver
// that reads one needs the bit position, and a byte offset would be a
// plausible-looking wrong answer.
int btf_member_off(struct btf *b, int struct_id, const char *member,
unsigned int *off_bytes, unsigned int *size_bytes);
// Size in bytes of a type, following typedefs and qualifiers. 0 if unknown.
unsigned int btf_type_size(struct btf *b, int id);
const char *btf_name_of(struct btf *b, int id);
int btf_kind_of(struct btf *b, int id);
const char *btf_err(const struct btf *b);