SchOS/ldls/stub.h
Nexoniarz 4ad49ed5f2 Linux's own r8169 through LDLS, deep idle, and the GPU plan
The RTL8168 is now driven by Linux's r8169, unmodified, in a process.
`make ldls-r8169` stages it from the running kernel together with libphy,
mdio-bus, mdio_devres and the Realtek PHY driver, the kernel's BTF and
the RTL PHY firmware, and etc/schinit/ldls-r8169.job starts it at boot
with its log in /var/log/ldls-r8169.log. The native driver is out of the
registry; its code, now with Linux's 8168H setup, stays as the fallback.

LDLS grows what that needed:
- ldlsd loads a comma-separated module set in dependency order, each
  linking against the ones before it, with BTF read before any
  init_module runs;
- a driver core (bus match, driver probe), devres, kernel printf, real
  udelay/msleep/ktime and 1000 Hz jiffies, phylib's bitmaps and
  link_mode_params, firmware from /lib/firmware, and the symbols whose
  stub zero was a lie (list validity, try_module_get, dev_err_probe,
  eth_platform_get_mac_address);
- pdev->bus is a real pci_bus, and cmovcc from per-CPU memory is
  rewritten like the other %gs forms.
In QEMU all five modules load, link and initialise, and r8169 registers
and finds no device. Probe and traffic are untested until real hardware.

The kernel turns ASPM off on Realtek NIC links at boot, as r8169 does
through pci_disable_link_state, which a process cannot.

Idle CPUs enter ACPI _CST's C2 (CC6 on Zen) by port read or MWAIT when
the APIC timer keeps running, instead of only ever halting at C1.

docs/plan/gpu.md: amdgpu and nouveau through LDLS, with measurements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-25 14:26:16 +01:00

58 lines
2.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// 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 same, numbering the stubs from `first`: with several modules loaded,
// each module's stubs follow the previous module's in one shared name table.
void stub_build_from(struct stubs *s, unsigned char *code, int n, int first);
// 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);