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
This commit is contained in:
parent
7dd2b03e36
commit
2d9a0ec6e3
8 changed files with 978 additions and 9 deletions
273
ldls/host/run.c
273
ldls/host/run.c
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "../ko.h"
|
||||
#include "../stub.h"
|
||||
#include "../btf.h"
|
||||
|
||||
// --- what the stubs report ---------------------------------------------------
|
||||
|
||||
|
|
@ -38,6 +39,21 @@ static int g_count;
|
|||
static int g_calls;
|
||||
static int g_verbose;
|
||||
|
||||
// What init_module handed to __pci_register_driver.
|
||||
//
|
||||
// In Linux that call ends at the PCI core, which matches the driver's id_table
|
||||
// against the bus and calls probe(). There is no PCI core here, so LDLS keeps
|
||||
// the pointer and plays that part itself -- which is the whole of L2.
|
||||
static unsigned long g_pci_driver;
|
||||
static int g_answer; // give allocators real memory
|
||||
|
||||
static int
|
||||
is_register_driver(const char *n)
|
||||
{
|
||||
return strcmp(n, "__pci_register_driver") == 0 ||
|
||||
strcmp(n, "pci_register_driver") == 0;
|
||||
}
|
||||
|
||||
// A handful of symbols are worth actually implementing rather than logging,
|
||||
// because what they do is print -- and a driver printing its own banner
|
||||
// through our code is the clearest evidence there is that the load worked.
|
||||
|
|
@ -80,6 +96,40 @@ printk_like(const char *fmt, unsigned long *args)
|
|||
printf("\n");
|
||||
}
|
||||
|
||||
// The smallest set of answers that lets probe() get past its first allocation.
|
||||
//
|
||||
// **This is not the shim.** It is six answers chosen to stop the driver
|
||||
// bailing out at the first call so that the *rest* of its sequence becomes
|
||||
// visible -- the same reason L1's stubs return zero instead of stopping.
|
||||
// Implementing these properly, against /dev/pci, is L3.
|
||||
//
|
||||
// sizeof(struct net_device) comes from BTF like everything else: a driver
|
||||
// reaches its private area with netdev_priv(), which is the net_device
|
||||
// rounded up to NETDEV_ALIGN, so getting it wrong hands the driver a pointer
|
||||
// into the middle of its own control block.
|
||||
static unsigned int g_netdev_size;
|
||||
|
||||
static unsigned long
|
||||
answer(const char *name, unsigned long *args)
|
||||
{
|
||||
if (strcmp(name, "alloc_etherdev_mqs") == 0) {
|
||||
unsigned long n = (g_netdev_size + 31u) & ~31u;
|
||||
return (unsigned long)calloc(1, n + args[0] + 64);
|
||||
}
|
||||
if (strcmp(name, "pci_iomap") == 0 ||
|
||||
strcmp(name, "pci_ioremap_bar") == 0 ||
|
||||
strcmp(name, "ioremap") == 0)
|
||||
return (unsigned long)calloc(1, 4096);
|
||||
if (strcmp(name, "__kmem_cache_create_args") == 0 ||
|
||||
strcmp(name, "kmem_cache_create") == 0)
|
||||
return (unsigned long)calloc(1, 256);
|
||||
if (strcmp(name, "dma_alloc_attrs") == 0)
|
||||
return (unsigned long)calloc(1, args[1] ? args[1] : 4096);
|
||||
// Success is zero for most of the kernel's int-returning calls, which is
|
||||
// already what a stub returns -- so nothing is listed for them here.
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ldls_stub(long idx, unsigned long *args)
|
||||
{
|
||||
|
|
@ -93,6 +143,17 @@ ldls_stub(long idx, unsigned long *args)
|
|||
if (is_printk(name))
|
||||
printk_like((const char *)args[0], args);
|
||||
|
||||
// netdev_info(dev, fmt, ...) and dev_info(dev, fmt, ...) put the format in
|
||||
// the *second* argument. Worth handling for the same reason as _printk: the
|
||||
// line a driver prints when it has found its hardware is the one that says
|
||||
// whether it really did.
|
||||
if (strcmp(name, "netdev_info") == 0 || strcmp(name, "netdev_err") == 0 ||
|
||||
strcmp(name, "_dev_info") == 0 || strcmp(name, "_dev_err") == 0)
|
||||
printk_like((const char *)args[1], args + 1);
|
||||
|
||||
if (is_register_driver(name) && !g_pci_driver)
|
||||
g_pci_driver = args[0];
|
||||
|
||||
if (g_verbose)
|
||||
printf(" args: %#lx %#lx %#lx %#lx %#lx %#lx\n",
|
||||
args[0], args[1], args[2], args[3], args[4], args[5]);
|
||||
|
|
@ -100,7 +161,7 @@ ldls_stub(long idx, unsigned long *args)
|
|||
// Zero, so the driver carries on and shows the next call. Stopping at the
|
||||
// first tells you one name; continuing tells you the sequence, which is what
|
||||
// this milestone is for.
|
||||
return 0;
|
||||
return g_answer ? answer(name, args) : 0;
|
||||
}
|
||||
|
||||
// --- surviving the module -----------------------------------------------------
|
||||
|
|
@ -115,6 +176,98 @@ on_fault(int sig)
|
|||
siglongjmp(g_jump, 1);
|
||||
}
|
||||
|
||||
// --- playing the PCI core -----------------------------------------------------
|
||||
//
|
||||
// Every offset here is read from the kernel's own BTF rather than written
|
||||
// down. That is the point of doing it this way: LDLS never claims to know
|
||||
// where `pci_driver.probe` is, it asks the kernel that the driver was built
|
||||
// for. See ldls/btf.h.
|
||||
|
||||
struct pcilayout {
|
||||
unsigned int drv_probe, drv_idtable, drv_name;
|
||||
unsigned int id_size, id_vendor, id_device, id_class, id_class_mask;
|
||||
unsigned int dev_size, dev_vendor, dev_device, dev_devfn, dev_irq,
|
||||
dev_resource;
|
||||
unsigned int res_size, res_start, res_end, res_flags;
|
||||
};
|
||||
|
||||
static int
|
||||
lay(struct btf *b, const char *type, const char *member, unsigned int *out)
|
||||
{
|
||||
unsigned int off, size;
|
||||
int id = btf_find(b, type, BTF_KIND_STRUCT);
|
||||
|
||||
if (!id) {
|
||||
fprintf(stderr, "ldls-run: this BTF has no struct %s\n", type);
|
||||
return -1;
|
||||
}
|
||||
if (!member) { *out = btf_type_size(b, id); return 0; }
|
||||
if (btf_member_off(b, id, member, &off, &size) < 0) {
|
||||
fprintf(stderr, "ldls-run: %s.%s: %s\n", type, member, btf_err(b));
|
||||
return -1;
|
||||
}
|
||||
*out = off;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pci_layout(struct btf *b, struct pcilayout *L)
|
||||
{
|
||||
return lay(b, "pci_driver", "probe", &L->drv_probe) ||
|
||||
lay(b, "pci_driver", "id_table", &L->drv_idtable) ||
|
||||
lay(b, "pci_driver", "name", &L->drv_name) ||
|
||||
lay(b, "pci_device_id", 0, &L->id_size) ||
|
||||
lay(b, "pci_device_id", "vendor", &L->id_vendor) ||
|
||||
lay(b, "pci_device_id", "device", &L->id_device) ||
|
||||
lay(b, "pci_device_id", "class", &L->id_class) ||
|
||||
lay(b, "pci_device_id", "class_mask", &L->id_class_mask) ||
|
||||
lay(b, "pci_dev", 0, &L->dev_size) ||
|
||||
lay(b, "pci_dev", "vendor", &L->dev_vendor) ||
|
||||
lay(b, "pci_dev", "device", &L->dev_device) ||
|
||||
lay(b, "pci_dev", "devfn", &L->dev_devfn) ||
|
||||
lay(b, "pci_dev", "irq", &L->dev_irq) ||
|
||||
lay(b, "pci_dev", "resource", &L->dev_resource)||
|
||||
lay(b, "resource", 0, &L->res_size) ||
|
||||
lay(b, "resource", "start", &L->res_start) ||
|
||||
lay(b, "resource", "end", &L->res_end) ||
|
||||
lay(b, "resource", "flags", &L->res_flags) ? -1 : 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
rd32(unsigned long base, unsigned int off)
|
||||
{
|
||||
unsigned int v;
|
||||
memcpy(&v, (void *)(base + off), 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
rd64(unsigned long base, unsigned int off)
|
||||
{
|
||||
unsigned long v;
|
||||
memcpy(&v, (void *)(base + off), 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
wr16(unsigned long base, unsigned int off, unsigned short v)
|
||||
{ memcpy((void *)(base + off), &v, 2); }
|
||||
|
||||
static void
|
||||
wr32(unsigned long base, unsigned int off, unsigned int v)
|
||||
{ memcpy((void *)(base + off), &v, 4); }
|
||||
|
||||
static void
|
||||
wr64(unsigned long base, unsigned int off, unsigned long v)
|
||||
{ memcpy((void *)(base + off), &v, 8); }
|
||||
|
||||
// The PCI resource flag bits. These are not layout and BTF does not carry
|
||||
// them -- they are #defines. They are ABI between the kernel and every driver
|
||||
// ever written against it, which makes them facts in the sense of
|
||||
// docs/plan/ldls.md rule 3, and there are two of them.
|
||||
#define LDLS_IORESOURCE_IO 0x00000100
|
||||
#define LDLS_IORESOURCE_MEM 0x00000200
|
||||
|
||||
// --- loading ------------------------------------------------------------------
|
||||
|
||||
static unsigned char *
|
||||
|
|
@ -185,10 +338,16 @@ main(int argc, char **argv)
|
|||
unsigned char *img, *mem, *code;
|
||||
unsigned long long len, span, entry;
|
||||
int nsec, i, rv = 0;
|
||||
const char *btfpath = 0;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "-v") == 0) { g_verbose = 1; argv++; argc--; }
|
||||
if (argc > 1 && strcmp(argv[1], "-a") == 0) { g_answer = 1; argv++; argc--; }
|
||||
if (argc > 2 && strcmp(argv[1], "--btf") == 0) {
|
||||
btfpath = argv[2]; argv += 2; argc -= 2;
|
||||
}
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: ldls-run [-v] <object.ko>\n");
|
||||
fprintf(stderr,
|
||||
"usage: ldls-run [-v] [-a] [--btf <btf>] <object.ko>\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
|
@ -292,9 +451,117 @@ main(int argc, char **argv)
|
|||
printf("\n stopped by signal %d after %d stub call(s)\n", g_signal, g_calls);
|
||||
printf(" that is a result, not a crash: the driver got that far before it\n"
|
||||
" needed one of those stubs to have done something real\n");
|
||||
rv = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// --- L2: be the PCI core -------------------------------------------------
|
||||
if (!btfpath)
|
||||
goto done;
|
||||
if (!g_pci_driver) {
|
||||
printf("\n no pci_driver was registered -- nothing to probe\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
{
|
||||
struct btf b;
|
||||
struct pcilayout L;
|
||||
unsigned char *bimg;
|
||||
unsigned long long blen;
|
||||
unsigned int *boff;
|
||||
long nt;
|
||||
unsigned long idtab, pdev;
|
||||
int nid = 0;
|
||||
|
||||
bimg = slurp(btfpath, &blen);
|
||||
if (!bimg) { fprintf(stderr, "ldls-run: cannot read %s\n", btfpath); goto done; }
|
||||
nt = btf_count(bimg, blen);
|
||||
if (nt < 0) { fprintf(stderr, "ldls-run: %s is not BTF\n", btfpath); goto done; }
|
||||
boff = calloc((size_t)nt + 2, sizeof(unsigned int));
|
||||
if (!boff || btf_open(&b, bimg, blen, boff, (unsigned int)nt + 2) < 0) {
|
||||
fprintf(stderr, "ldls-run: %s\n", btf_err(&b));
|
||||
goto done;
|
||||
}
|
||||
if (pci_layout(&b, &L) < 0)
|
||||
goto done;
|
||||
{
|
||||
int nd = btf_find(&b, "net_device", BTF_KIND_STRUCT);
|
||||
g_netdev_size = nd ? btf_type_size(&b, nd) : 4096;
|
||||
}
|
||||
|
||||
printf("\n pci_driver at %#lx, from BTF: probe +%u, id_table +%u\n",
|
||||
g_pci_driver, L.drv_probe, L.drv_idtable);
|
||||
{
|
||||
const char *dn = (const char *)rd64(g_pci_driver, L.drv_name);
|
||||
if (dn) printf(" driver name \"%s\"\n", dn);
|
||||
}
|
||||
|
||||
idtab = rd64(g_pci_driver, L.drv_idtable);
|
||||
if (!idtab) { printf(" it has no id_table\n"); goto done; }
|
||||
|
||||
printf("\n devices it claims:\n");
|
||||
for (;;) {
|
||||
unsigned long e = idtab + (unsigned long)nid * L.id_size;
|
||||
unsigned int v = rd32(e, L.id_vendor), d = rd32(e, L.id_device);
|
||||
unsigned int c = rd32(e, L.id_class);
|
||||
if (!v && !d && !c) break;
|
||||
if (nid < 8)
|
||||
printf(" %04x:%04x class %06x/%06x\n", v, d, c,
|
||||
rd32(e, L.id_class_mask));
|
||||
nid++;
|
||||
if (nid > 4096) break;
|
||||
}
|
||||
if (nid > 8) printf(" ... and %d more\n", nid - 8);
|
||||
if (!nid) { printf(" (none)\n"); goto done; }
|
||||
|
||||
// A device to hand it. Zeroed, then the few fields a driver reads before
|
||||
// it has called anything: what it is, where it lives, and its BARs.
|
||||
//
|
||||
// The BAR values are invented. The point of L2 is that probe() is reached
|
||||
// with a pci_dev whose *shape* is right; making the values real is L3's
|
||||
// job and belongs to /dev/pci rather than to a fake.
|
||||
pdev = (unsigned long)calloc(1, L.dev_size);
|
||||
if (!pdev) goto done;
|
||||
{
|
||||
unsigned long e = idtab; /* the first entry it claims */
|
||||
unsigned long r0 = pdev + L.dev_resource;
|
||||
unsigned long r1 = r0 + L.res_size;
|
||||
|
||||
wr16(pdev, L.dev_vendor, (unsigned short)rd32(e, L.id_vendor));
|
||||
wr16(pdev, L.dev_device, (unsigned short)rd32(e, L.id_device));
|
||||
wr32(pdev, L.dev_devfn, 0x18); /* 03.0 */
|
||||
wr32(pdev, L.dev_irq, 11);
|
||||
|
||||
wr64(r0, L.res_start, 0xc000);
|
||||
wr64(r0, L.res_end, 0xc0ff);
|
||||
wr64(r0, L.res_flags, LDLS_IORESOURCE_IO);
|
||||
wr64(r1, L.res_start, 0xfebc0000);
|
||||
wr64(r1, L.res_end, 0xfebc00ff);
|
||||
wr64(r1, L.res_flags, LDLS_IORESOURCE_MEM);
|
||||
|
||||
printf("\n calling probe(pci_dev %04x:%04x at %#lx, id %#lx)\n\n",
|
||||
rd32(e, L.id_vendor), rd32(e, L.id_device), pdev, idtab);
|
||||
|
||||
g_calls = 0;
|
||||
if (sigsetjmp(g_jump, 1) == 0) {
|
||||
int (*probe)(void *, void *) =
|
||||
(int (*)(void *, void *))(void *)rd64(g_pci_driver, L.drv_probe);
|
||||
if (!probe) { printf(" the driver has no probe\n"); goto done; }
|
||||
{
|
||||
int r = probe((void *)pdev, (void *)idtab);
|
||||
printf("\n probe returned %d after %d stub call(s)\n", r, g_calls);
|
||||
}
|
||||
} else {
|
||||
printf("\n probe stopped by signal %d after %d stub call(s)\n",
|
||||
g_signal, g_calls);
|
||||
printf(" which is how far a driver gets before one of those stubs\n"
|
||||
" has to do something real. That list is L3's work queue.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
;
|
||||
|
||||
free(sec);
|
||||
free(img);
|
||||
return rv;
|
||||
|
|
|
|||
140
ldls/host/types.c
Normal file
140
ldls/host/types.c
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// ldls-types -- ask a kernel's BTF where its fields are.
|
||||
//
|
||||
// docs/plan/ldls.md milestone L2. This is the tool that proves the layouts LDLS
|
||||
// needs can be read from the kernel the user already has, rather than written
|
||||
// into our source from theirs.
|
||||
//
|
||||
// ldls-types /sys/kernel/btf/vmlinux pci_driver probe id_table
|
||||
// ldls-types /sys/kernel/btf/vmlinux --need
|
||||
//
|
||||
// `--need` prints every offset the PCI path needs at once, which is both a
|
||||
// test and the thing L2's driver code consumes.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../btf.h"
|
||||
|
||||
static unsigned char *
|
||||
slurp(const char *path, unsigned long long *len)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
unsigned char *buf;
|
||||
long n;
|
||||
|
||||
if (!f) return 0;
|
||||
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return 0; }
|
||||
n = ftell(f);
|
||||
if (n <= 0) { fclose(f); return 0; }
|
||||
rewind(f);
|
||||
buf = malloc((size_t)n);
|
||||
if (!buf) { fclose(f); return 0; }
|
||||
if (fread(buf, 1, (size_t)n, f) != (size_t)n) { free(buf); fclose(f); return 0; }
|
||||
fclose(f);
|
||||
*len = (unsigned long long)n;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
show(struct btf *b, const char *type, const char *member)
|
||||
{
|
||||
unsigned int off, size;
|
||||
int id = btf_find(b, type, BTF_KIND_STRUCT);
|
||||
|
||||
if (!id)
|
||||
id = btf_find(b, type, BTF_KIND_UNION);
|
||||
if (!id) {
|
||||
printf(" %-22s %-22s (no such struct)\n", type, member ? member : "");
|
||||
return 1;
|
||||
}
|
||||
if (!member) {
|
||||
printf(" %-22s %-22s sizeof = %u\n", type, "", btf_type_size(b, id));
|
||||
return 0;
|
||||
}
|
||||
if (btf_member_off(b, id, member, &off, &size) < 0) {
|
||||
printf(" %-22s %-22s %s\n", type, member, btf_err(b));
|
||||
return 1;
|
||||
}
|
||||
printf(" %-22s %-22s offset %5u size %u\n", type, member, off, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Every layout the PCI path needs to get from init_module to probe().
|
||||
static const struct { const char *type, *member; } needed[] = {
|
||||
{ "pci_driver", 0 },
|
||||
{ "pci_driver", "name" },
|
||||
{ "pci_driver", "id_table" },
|
||||
{ "pci_driver", "probe" },
|
||||
{ "pci_driver", "remove" },
|
||||
{ "pci_device_id", 0 },
|
||||
{ "pci_device_id", "vendor" },
|
||||
{ "pci_device_id", "device" },
|
||||
{ "pci_device_id", "subvendor" },
|
||||
{ "pci_device_id", "subdevice" },
|
||||
{ "pci_device_id", "class" },
|
||||
{ "pci_device_id", "class_mask" },
|
||||
{ "pci_device_id", "driver_data"},
|
||||
{ "pci_dev", 0 },
|
||||
{ "pci_dev", "vendor" },
|
||||
{ "pci_dev", "device" },
|
||||
{ "pci_dev", "devfn" },
|
||||
{ "pci_dev", "irq" },
|
||||
{ "pci_dev", "resource" },
|
||||
{ "resource", 0 },
|
||||
{ "resource", "start" },
|
||||
{ "resource", "end" },
|
||||
{ "resource", "flags" },
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct btf b;
|
||||
unsigned char *img;
|
||||
unsigned long long len;
|
||||
unsigned int *off;
|
||||
long n;
|
||||
int i, bad = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: ldls-types <btf-file> [--need | <struct> [member...]]\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
img = slurp(argv[1], &len);
|
||||
if (!img) { fprintf(stderr, "ldls-types: cannot read %s\n", argv[1]); return 1; }
|
||||
|
||||
n = btf_count(img, len);
|
||||
if (n < 0) { fprintf(stderr, "ldls-types: %s is not BTF\n", argv[1]); return 1; }
|
||||
|
||||
off = calloc((size_t)n + 2, sizeof(unsigned int));
|
||||
if (!off) { fprintf(stderr, "ldls-types: out of memory\n"); return 1; }
|
||||
|
||||
if (btf_open(&b, img, len, off, (unsigned int)n + 2) < 0) {
|
||||
fprintf(stderr, "ldls-types: %s\n", btf_err(&b));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s\n %ld types, %u bytes of strings\n\n", argv[1], n, b.str_len);
|
||||
|
||||
if (argc == 2 || strcmp(argv[2], "--need") == 0) {
|
||||
printf(" what the PCI path needs\n");
|
||||
for (i = 0; i < (int)(sizeof(needed) / sizeof(needed[0])); i++)
|
||||
bad += show(&b, needed[i].type, needed[i].member);
|
||||
if (bad)
|
||||
printf("\n %d of %d not found -- this kernel's BTF cannot drive probe()\n",
|
||||
bad, (int)(sizeof(needed) / sizeof(needed[0])));
|
||||
else
|
||||
printf("\n all present\n");
|
||||
return bad ? 1 : 0;
|
||||
}
|
||||
|
||||
if (argc == 3)
|
||||
return show(&b, argv[2], 0);
|
||||
for (i = 3; i < argc; i++)
|
||||
bad += show(&b, argv[2], argv[i]);
|
||||
return bad ? 1 : 0;
|
||||
}
|
||||
Loading…
Reference in a new issue