ldls: the top-2 GB window Linux modules link against

A Linux module is built with -mcmodel=kernel and can only reach addresses
in the top 2 GB, where this kernel is not linked. So the kernel image is
mapped a second time at LDLS_KALIAS, with a vmemmap area and a module area
beside it, in one PDPT shared by every address space. Nothing loads a
module yet; this is the address-space groundwork, set up at every boot,
and include/ldls/ is the interface the loader will use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-24 22:12:40 +02:00
commit 65617710fa
4 changed files with 253 additions and 4 deletions

View file

@ -91,6 +91,22 @@ extern uint64 lowtop;
#define KERNPHYS 0x1000000
#define KERNLINK (KERNBASE+KERNPHYS) // Address where kernel is linked
// The LDLS window, in the top 2 GB, where a Linux module expects to live. See
// ldls_vm_init() in kernel/arch/x86_64/vm.c.
//
// LDLS_VMEMMAP 1 GB Linux's `struct page` array, 64 bytes per frame --
// enough for 64 GB, not for all of PHYSTOP_MAX; a
// frame above that has no struct page. Backed on demand.
// LDLS_KALIAS 512 MB the kernel image again, from physical zero
// LDLS_MODBASE 512 MB modules, their stacks and their vmalloc
#define LDLS_VMEMMAP 0xFFFFFFFF40000000ULL
#define LDLS_KALIAS 0xFFFFFFFF80000000ULL
#define LDLS_MODBASE 0xFFFFFFFFA0000000ULL
#define LDLS_MODEND 0xFFFFFFFFC0000000ULL
// A kernel address, as a module sees it.
#define LDLS_ALIAS(a) ((uint64)(a) - KERNBASE + LDLS_KALIAS)
#ifndef __ASSEMBLER__
static inline addr_t v2p(void *a) {
return ((addr_t)(a)) - ((addr_t)KERNBASE);

26
include/ldls/kldls.h Normal file
View file

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// The kernel's side of LDLS, as the rest of the kernel sees it. The layer
// itself is kernel/ldls/; this is the handful of places it is called from.
struct trapframe;
// kernel/arch/x86_64/vm.c -- the window in the top 2 GB.
void ldls_vm_init(void);
int kmap_page(uint64 va, addr_t pa);
addr_t kmap_phys(uint64 va);
addr_t kunmap_page(uint64 va);
// kernel/ldls/core.c
void ldls_init(void);
int ldls_ioctl(uint64 request, uint64 argp);
// A trap in kernel mode. Returns 1 if LDLS dealt with it -- a WARN() that was
// reported and stepped over, or a hosted driver that faulted and has been
// stopped -- and 0 if it is the kernel's own and should panic as before.
int ldls_trap(struct trapframe *tf);
// The name of an address in a hosted module, for a backtrace. 0 if `a` is not
// in one.
int ldls_symbolize(uint64 a, char *out, int cap);

53
include/ldls/ldls.h Normal file
View file

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// /dev/ldls -- Linux drivers, in the kernel. docs/architecture/ldls.md.
//
// The device is a control channel and nothing else: a module is handed over
// whole, as bytes, together with the BTF of the kernel it was built for, and
// the kernel does the rest -- loading, linking, init_module, probe, and
// bringing the interface up -- on a thread of its own. What happened comes
// back as text, because the useful answer to "did it work" is the driver's
// own account of what it found.
//
// ldls load /lib/modules/e1000.ko
// ldls list
// ldls unload e1000
//
// Shared with userland (cmd/ldls), so it is spelled in plain C types.
#define LDLS_MAJOR 13
// Take the device even if a kernel driver holds it, provided that driver can
// be asked to let go. Without this LDLS only takes devices nothing else
// drives, which is the right default at boot: the kernel's own driver is on
// the path to a shell, and the hosted one is the better one only when someone
// says so.
#define LDLS_TAKE 0x1
// Load and link, run init_module, and stop: no device is probed. For a
// library module (mii, libphy) whose job is to export symbols to the next.
#define LDLS_NOPROBE 0x2
struct ldls_load {
unsigned long long ko; // in: the module image
unsigned long long kolen;
unsigned long long btf; // in: the kernel's BTF, /sys/kernel/btf/vmlinux
unsigned long long btflen;
unsigned long long msg; // out: what happened, NUL-terminated
unsigned int msgcap;
unsigned int flags; // LDLS_*
int bus, slot, func; // -1 for "whichever matches"
unsigned int hz; // CONFIG_HZ of that kernel; 0 means 1000
int id; // out: the module's id, for unload
};
struct ldls_text {
unsigned long long buf; // out
unsigned int cap;
unsigned int pad;
};
#define LDLSIOC_LOAD 0xD000 // struct ldls_load
#define LDLSIOC_UNLOAD 0xD001 // in: int id
#define LDLSIOC_LIST 0xD002 // struct ldls_text
#define LDLSIOC_LOG 0xD003 // struct ldls_text, in: pad = id