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
332 lines
9.7 KiB
C
332 lines
9.7 KiB
C
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Reading BTF. See ldls/btf.h.
|
|
//
|
|
// Implemented from Documentation/bpf/btf.rst. The format is small enough to
|
|
// hold in your head: a header, a run of variable-length type records, and a
|
|
// string table. The only fiddly part is that a record's length depends on its
|
|
// kind, so iterating means knowing how much trails each one -- which is the
|
|
// table in trailing_bytes() below and nothing more.
|
|
|
|
#include "btf.h"
|
|
|
|
static void
|
|
btf_fail(struct btf *b, const char *msg, const char *what)
|
|
{
|
|
int i = 0, j = 0;
|
|
while (msg[i] && i < (int)sizeof(b->err) - 1) { b->err[i] = msg[i]; i++; }
|
|
if (what)
|
|
while (what[j] && i < (int)sizeof(b->err) - 1) b->err[i++] = what[j++];
|
|
b->err[i] = 0;
|
|
}
|
|
|
|
static int
|
|
btf_streq(const char *a, const char *c)
|
|
{
|
|
while (*a && *a == *c) { a++; c++; }
|
|
return *a == 0 && *c == 0;
|
|
}
|
|
|
|
const char *btf_err(const struct btf *b) { return b->err[0] ? b->err : "no error"; }
|
|
|
|
// How many bytes follow a type record, given its kind and vlen.
|
|
static unsigned int
|
|
trailing_bytes(unsigned int info)
|
|
{
|
|
unsigned int kind = BTF_INFO_KIND(info);
|
|
unsigned int vlen = BTF_INFO_VLEN(info);
|
|
|
|
switch (kind) {
|
|
case BTF_KIND_INT: return 4;
|
|
case BTF_KIND_ARRAY: return 12; /* type, index_type, nelems */
|
|
case BTF_KIND_STRUCT:
|
|
case BTF_KIND_UNION: return vlen * 12; /* struct btf_member */
|
|
case BTF_KIND_ENUM: return vlen * 8;
|
|
case BTF_KIND_ENUM64: return vlen * 12;
|
|
case BTF_KIND_FUNC_PROTO: return vlen * 8; /* struct btf_param */
|
|
case BTF_KIND_VAR: return 4;
|
|
case BTF_KIND_DATASEC: return vlen * 12;
|
|
case BTF_KIND_DECL_TAG: return 4;
|
|
default: return 0; /* PTR, TYPEDEF, CONST, ... */
|
|
}
|
|
}
|
|
|
|
long
|
|
btf_count(const void *image, unsigned long long len)
|
|
{
|
|
const struct btf_header *h = (const struct btf_header *)image;
|
|
const unsigned char *types;
|
|
unsigned int at = 0;
|
|
long n = 0;
|
|
|
|
if (len < sizeof(struct btf_header))
|
|
return -1;
|
|
if (h->magic != BTF_MAGIC || h->version != 1)
|
|
return -1;
|
|
if ((unsigned long long)h->hdr_len + h->type_off + h->type_len > len)
|
|
return -1;
|
|
|
|
types = (const unsigned char *)image + h->hdr_len + h->type_off;
|
|
|
|
while (at + sizeof(struct btf_type) <= h->type_len) {
|
|
const struct btf_type *t = (const struct btf_type *)(types + at);
|
|
unsigned int step = sizeof(struct btf_type) + trailing_bytes(t->info);
|
|
if (step == 0 || at + step > h->type_len)
|
|
break;
|
|
at += step;
|
|
n++;
|
|
if (n > BTF_MAXTYPES)
|
|
return -1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int
|
|
btf_open(struct btf *b, const void *image, unsigned long long len,
|
|
unsigned int *off, unsigned int noff)
|
|
{
|
|
const struct btf_header *h = (const struct btf_header *)image;
|
|
unsigned int at = 0, id = 1;
|
|
int i;
|
|
|
|
for (i = 0; i < (int)sizeof(b->err); i++)
|
|
b->err[i] = 0;
|
|
b->img = (const unsigned char *)image;
|
|
b->len = len;
|
|
b->off = off;
|
|
b->ntypes = 0;
|
|
|
|
if (len < sizeof(struct btf_header))
|
|
{ btf_fail(b, "too short to be BTF", 0); return -1; }
|
|
if (h->magic != BTF_MAGIC)
|
|
{ btf_fail(b, "not BTF: wrong magic", 0); return -1; }
|
|
if (h->version != 1)
|
|
{ btf_fail(b, "BTF version is not 1", 0); return -1; }
|
|
if ((unsigned long long)h->hdr_len + h->type_off + h->type_len > len ||
|
|
(unsigned long long)h->hdr_len + h->str_off + h->str_len > len)
|
|
{ btf_fail(b, "BTF sections run off the end of the file", 0); return -1; }
|
|
|
|
b->h = h;
|
|
b->types = b->img + h->hdr_len + h->type_off;
|
|
b->types_len = h->type_len;
|
|
b->str = (const char *)(b->img + h->hdr_len + h->str_off);
|
|
b->str_len = h->str_len;
|
|
|
|
// Index every type. Ids are assigned in order starting at 1; id 0 is void
|
|
// and is not stored.
|
|
while (at + sizeof(struct btf_type) <= b->types_len) {
|
|
const struct btf_type *t = (const struct btf_type *)(b->types + at);
|
|
unsigned int step = sizeof(struct btf_type) + trailing_bytes(t->info);
|
|
|
|
if (at + step > b->types_len)
|
|
break;
|
|
if (id >= noff)
|
|
{ btf_fail(b, "the caller's type index is too small", 0); return -1; }
|
|
off[id++] = at;
|
|
at += step;
|
|
}
|
|
b->ntypes = id - 1;
|
|
return 0;
|
|
}
|
|
|
|
static const struct btf_type *
|
|
btf_at(struct btf *b, int id)
|
|
{
|
|
if (id <= 0 || (unsigned int)id > b->ntypes)
|
|
return 0;
|
|
return (const struct btf_type *)(b->types + b->off[id]);
|
|
}
|
|
|
|
const char *
|
|
btf_name_of(struct btf *b, int id)
|
|
{
|
|
const struct btf_type *t = btf_at(b, id);
|
|
if (!t || t->name_off >= b->str_len)
|
|
return "";
|
|
return b->str + t->name_off;
|
|
}
|
|
|
|
int
|
|
btf_kind_of(struct btf *b, int id)
|
|
{
|
|
const struct btf_type *t = btf_at(b, id);
|
|
return t ? (int)BTF_INFO_KIND(t->info) : -1;
|
|
}
|
|
|
|
int
|
|
btf_find(struct btf *b, const char *name, int kind)
|
|
{
|
|
unsigned int id;
|
|
|
|
for (id = 1; id <= b->ntypes; id++) {
|
|
const struct btf_type *t = (const struct btf_type *)(b->types + b->off[id]);
|
|
if ((int)BTF_INFO_KIND(t->info) != kind)
|
|
continue;
|
|
if (t->name_off >= b->str_len)
|
|
continue;
|
|
if (btf_streq(b->str + t->name_off, name))
|
|
return (int)id;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// The value of a named enumerator, wherever it lives.
|
|
//
|
|
// Not every constant a driver depends on is a struct offset. `netif_carrier_ok`
|
|
// is an inline that tests bit `__LINK_STATE_NOCARRIER` of `net_device.state`,
|
|
// so a host that wants to say "the link is down" has to set the right bit --
|
|
// and getting it wrong means a driver that believes the carrier is already up
|
|
// and skips everything it would have done on link-up. e1000 skips enabling its
|
|
// transmitter, which is not an error anywhere.
|
|
//
|
|
// The enum is anonymous, so there is no type to look up by name; the search is
|
|
// over enumerators rather than over types. Which is the same rule 3½ bargain as
|
|
// the offsets: the number is the installed kernel's, not ours.
|
|
//
|
|
// Returns 0 and sets *val on success, -1 if no enumerator has that name.
|
|
int
|
|
btf_enum_value(struct btf *b, const char *name, long long *val)
|
|
{
|
|
unsigned int id;
|
|
|
|
for (id = 1; id <= b->ntypes; id++) {
|
|
const struct btf_type *t = (const struct btf_type *)(b->types + b->off[id]);
|
|
int kind = (int)BTF_INFO_KIND(t->info);
|
|
unsigned int n = BTF_INFO_VLEN(t->info), i;
|
|
|
|
if (kind == BTF_KIND_ENUM) {
|
|
const unsigned int *e = (const unsigned int *)(t + 1);
|
|
for (i = 0; i < n; i++) {
|
|
unsigned int noff = e[i * 2];
|
|
if (noff < b->str_len && btf_streq(b->str + noff, name)) {
|
|
*val = (int)e[i * 2 + 1];
|
|
return 0;
|
|
}
|
|
}
|
|
} else if (kind == BTF_KIND_ENUM64) {
|
|
const unsigned int *e = (const unsigned int *)(t + 1);
|
|
for (i = 0; i < n; i++) {
|
|
unsigned int noff = e[i * 3];
|
|
if (noff < b->str_len && btf_streq(b->str + noff, name)) {
|
|
*val = (long long)e[i * 3 + 1] |
|
|
((long long)e[i * 3 + 2] << 32);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
btf_fail(b, "no enumerator named ", name);
|
|
return -1;
|
|
}
|
|
|
|
// Follow the things that wrap a type without changing its layout.
|
|
static int
|
|
btf_strip(struct btf *b, int id)
|
|
{
|
|
int guard = 0;
|
|
|
|
while (id > 0 && guard++ < 32) {
|
|
const struct btf_type *t = btf_at(b, id);
|
|
if (!t)
|
|
return 0;
|
|
switch (BTF_INFO_KIND(t->info)) {
|
|
case BTF_KIND_TYPEDEF:
|
|
case BTF_KIND_CONST:
|
|
case BTF_KIND_VOLATILE:
|
|
case BTF_KIND_RESTRICT:
|
|
case BTF_KIND_TYPE_TAG:
|
|
id = (int)t->u.type;
|
|
continue;
|
|
default:
|
|
return id;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int
|
|
btf_type_size(struct btf *b, int id)
|
|
{
|
|
const struct btf_type *t;
|
|
|
|
id = btf_strip(b, id);
|
|
t = btf_at(b, id);
|
|
if (!t)
|
|
return 0;
|
|
|
|
switch (BTF_INFO_KIND(t->info)) {
|
|
case BTF_KIND_INT:
|
|
case BTF_KIND_STRUCT:
|
|
case BTF_KIND_UNION:
|
|
case BTF_KIND_ENUM:
|
|
case BTF_KIND_ENUM64:
|
|
case BTF_KIND_FLOAT:
|
|
return t->u.size;
|
|
case BTF_KIND_PTR:
|
|
return 8; // x86-64; this reader is built for one ABI
|
|
case BTF_KIND_ARRAY: {
|
|
const unsigned int *a = (const unsigned int *)(t + 1);
|
|
return btf_type_size(b, (int)a[0]) * a[2];
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
btf_member_off(struct btf *b, int struct_id, const char *member,
|
|
unsigned int *off_bytes, unsigned int *size_bytes)
|
|
{
|
|
const struct btf_type *t;
|
|
const struct btf_member *m;
|
|
unsigned int i, vlen, kflag;
|
|
|
|
struct_id = btf_strip(b, struct_id);
|
|
t = btf_at(b, struct_id);
|
|
if (!t)
|
|
{ btf_fail(b, "no such type", 0); return -1; }
|
|
if (BTF_INFO_KIND(t->info) != BTF_KIND_STRUCT &&
|
|
BTF_INFO_KIND(t->info) != BTF_KIND_UNION)
|
|
{ btf_fail(b, "not a struct or union", 0); return -1; }
|
|
|
|
vlen = BTF_INFO_VLEN(t->info);
|
|
kflag = BTF_INFO_KFLAG(t->info);
|
|
m = (const struct btf_member *)(t + 1);
|
|
|
|
for (i = 0; i < vlen; i++, m++) {
|
|
const char *nm = (m->name_off < b->str_len) ? b->str + m->name_off : "";
|
|
unsigned int bit;
|
|
|
|
// An unnamed member is an anonymous struct or union, and what looks like
|
|
// a field of the outer type is really a field of that. Recurse, adding
|
|
// its own offset -- without this, every kernel struct with an anonymous
|
|
// union in it reports its later fields as missing.
|
|
if (nm[0] == 0) {
|
|
unsigned int sub_off, sub_size;
|
|
unsigned int here = kflag ? (m->offset & 0xffffff) : m->offset;
|
|
if (btf_member_off(b, (int)m->type, member, &sub_off, &sub_size) == 0) {
|
|
*off_bytes = here / 8 + sub_off;
|
|
*size_bytes = sub_size;
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!btf_streq(nm, member))
|
|
continue;
|
|
|
|
if (kflag && (m->offset >> 24) != 0)
|
|
{ btf_fail(b, "that member is a bitfield: ", member); return -1; }
|
|
|
|
bit = kflag ? (m->offset & 0xffffff) : m->offset;
|
|
if (bit % 8)
|
|
{ btf_fail(b, "that member is not byte-aligned: ", member); return -1; }
|
|
|
|
*off_bytes = bit / 8;
|
|
*size_bytes = btf_type_size(b, (int)m->type);
|
|
return 0;
|
|
}
|
|
|
|
btf_fail(b, "no such member: ", member);
|
|
return -1;
|
|
}
|