e1000 -- Linux's, unmodified -- probes, opens, gets an address by DHCP and
carries ping traffic in a SchOS process. The kernel was driving that NIC and
gave it up, which makes it the first real handover as well as the second
driver.
None of what it needed was a missing feature. Every one was a call that
succeeded, returned zero and did nothing:
_find_next_bit "bit 0 is set" -- for_each_cpu never ended
netif_carrier_off carrier already up -- TCTL.EN never set
__x86_indirect_thunk_r8 the jump discarded -- 255 rx buffers, none made
dma_map_page_attrs bus address 0 -- NIC aimed at physical 0
so the rule from memcpy is now general: a stub returning zero is unsafe
wherever zero is a meaningful answer.
The loader grew ko_fixup_percpu(). The stack guard turned out to be one
per-CPU variable among several; `current` cannot be defused because the driver
dereferences it. Taking the %gs off instead leaves the same opcode reading the
same width from storage the host supplies -- a per-CPU variable in a
single-threaded host is just a variable. Eight bytes become seven and a nop,
and the displacement is not always last, so there is now just enough of an x86
decoder in ko.c to know how long the instruction is.
ldls/kpi/thunk.c is fifteen lines of assembly and the highest-value file here:
retpolines are control flow, and a stub cannot stand in for a jump.
The kernel side:
- ioperm.c, the TSS I/O permission bitmap. A driver's outl is an instruction,
not a call, and e1000's reset path picks it deliberately. IOPL=3 would hand
over the PIC and the disk with it, so it is per-port. Writing it found that
the I/O map base had been written at offset 64 rather than 102, leaving the
CPU checking ports 0-831 against the TSS's own RSP0.
- claim masks the device's IRQ line: 450,000 a second, otherwise.
- net_detach(), the missing half of net_attach_late(). An interface left in
the stack keeps its route and sends every frame to a driver that refuses
it, which looks exactly like a userspace driver that does not work.
- e1000_release() gives up the interface, not just the hardware.
8139too still passes 4/4; ldls-check still passes 27; four CPUs, no panic.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
139 lines
4.9 KiB
C
139 lines
4.9 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);
|
|
|
|
// The value of a named enumerator, searched across every enum in the file.
|
|
// Constants a driver depends on are not all struct offsets -- see btf.c.
|
|
int btf_enum_value(struct btf *b, const char *name, long long *val);
|
|
|
|
// 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);
|