L4 was listed as "free -- H2 built all of this" and never actually tested,
which is a claim rather than a result. Tested now, and it holds:
ldlsd running ping 2/2; holds index 7 and DMA slots 3 and 4
killed interface DOWN, device released, bus mastering
cleared, DMA reclaimed -- back to vgpud's 3 blocks
ping fails honestly: sendto: -5
a second ldlsd ping 3/3, and the arena hands back the same addresses
pciuser_release_all() and netuser_release_all() sit beside each other in
exit() for the same reason, and that they apply unchanged to a foreign driver
is the whole point of having built them against our own.
With that, every milestone on ldls.md is done, so it leaves docs/plan/ for
docs/architecture/ -- the rule docs/plan/README.md states and the one that
keeps the two directories meaning different things. The new document describes
what is, not what was planned: the five rules, the four pieces, what it costs
(polling, no stack protector, locks that are no-ops), that it can be killed,
and a "what it does not do" that leads with one driver, one kernel version,
tried once.
Sixteen source files pointed at docs/plan/ldls.md and would have been dangling
the moment it moved. All repointed, and the milestone references -- which the
architecture doc deliberately does not have -- rewritten to name sections
instead, so a reader lands somewhere real.
The register row closes with it. hybrid.md freed a slot, ldls.md took it, and
it is now free again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
135 lines
4.7 KiB
C
135 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/architecture/ldls.md, rule 3½ -- 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);
|