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
55 lines
2.4 KiB
C
55 lines
2.4 KiB
C
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Stubs: giving every undefined symbol an address that says who it was.
|
|
//
|
|
// docs/architecture/ldls.md, rule 5. The loader measures what a module asks for; this is
|
|
// what answers. Every name resolves to a small block of generated code, and
|
|
// when the driver calls it the block says which name it was and what arguments
|
|
// it came with.
|
|
//
|
|
// The value is not the logging, it is the **order**. A module's undefined
|
|
// symbol list is a set and tells you nothing about sequence; what a driver
|
|
// does at init, and in what order, is the other thing that cannot be guessed
|
|
// and it falls out of running the thing.
|
|
//
|
|
// Freestanding, like ldls/ko.c, so the SchOS-side host gets the same code.
|
|
|
|
#define STUB_SIZE 32 // bytes per trampoline, generously aligned
|
|
|
|
// What a stub call reports. The embedder provides this; stub.c calls it from
|
|
// the thunk with the original argument registers intact, which is what lets a
|
|
// stub for `_printk` show the string the driver actually passed.
|
|
//
|
|
// Whatever it returns becomes the stub's return value, so returning 0 lets a
|
|
// driver carry on past a call and reveal the next one -- which is worth far
|
|
// more than stopping at the first.
|
|
unsigned long ldls_stub(long idx, unsigned long *args);
|
|
|
|
struct stubs {
|
|
unsigned char *code; // STUB_SIZE * n bytes, must be executable
|
|
int n;
|
|
};
|
|
|
|
// Build `n` trampolines into `code`. Each one, when called, ends up in
|
|
// ldls_stub() with its index.
|
|
//
|
|
// `code` has to be executable memory; this only writes the bytes.
|
|
void stub_build(struct stubs *s, unsigned char *code, int n);
|
|
|
|
// The address of stub `i`, to hand to the loader as a symbol value.
|
|
unsigned long long stub_addr(const struct stubs *s, int i);
|
|
|
|
// Some names must not be logged, because they are called so often that the log
|
|
// becomes the only thing you see, and because they have calling conventions of
|
|
// their own that a logging thunk would break.
|
|
//
|
|
// `__fentry__` is ftrace's hook, emitted at the top of every function, and it
|
|
// is required to preserve every register. `__x86_return_thunk` is the retpoline
|
|
// return. Both are correctly implemented as a bare `ret`, which is also the
|
|
// fastest thing they could possibly be.
|
|
//
|
|
// Returns 1 if the name got a bare-ret stub instead of a logging one.
|
|
int stub_is_bare(const char *name);
|
|
|
|
// Overwrite one stub with that bare `ret`.
|
|
void stub_make_bare(unsigned char *p);
|