root: find the filesystem instead of being told at build time, and fix dmesg
`#define ROOTDEV 1` -- IDE disk 1 -- so a kernel could only ever root on the
disk it was *built* expecting. Booting the same image from a USB stick was
impossible by construction, and that was the one thing standing between this
kernel and its own acceptance test, which begins "boot on real hardware from a
USB stick".
rootdev_mount() probes. schfs_kmount() was already written for exactly this --
its own comment says "the uberblock scan doubles as the probe: no valid
uberblock means this is simply not a SchFS volume, which is not an error worth
reporting loudly" -- and a failed probe leaves the slot unmounted and reusable,
which is what makes a loop over candidates legal at all. So the change is one
function and one call site; the only real use of ROOTDEV was `schfs_kmount()`.
Order is preference, not confidence: a RAMDISK_ROOT build wins outright, then
USB, then IDE 1, then IDE 0. USB before the fixed disks because a stick that is
plugged in was plugged in on purpose. iderw() panics on an absent drive rather
than failing, so each candidate is checked with ide_disk_present() /
usb_storage_present() first -- which is what those predicates are for.
Verified with **no IDE disk attached at all**: boots, mounts the stick, writes
a file, and the file is still there on the next boot.
[ 0.000021] root: USB disk (device 3)
And when nothing matches, it names what it looked at, because "no root
filesystem" with no list is the least useful thing a kernel can say at the one
moment somebody needs to know which disk it expected:
root: no SchFS volume on any candidate device. Looked at:
device 3, USB disk: not present
device 1, IDE disk 1: not present
device 0, IDE disk 0: present, but no SchFS on it
**That message is also how dmesg turned out to be broken**, because it did not
appear in `dmesg` and should have. `dmesg` printed *nothing at all*, on a
system whose boot banner says "or dmesg once you are in" and whose entire
quiet-boot design is "route the detail into the kernel log and say how to read
it". The log was there the whole time -- `cat /proc/kmsg` has always worked --
but the one command anybody would reach for could not see it.
Two separate faults, each alone enough:
- **sys_syslog() was `return 0`.** dmesg does not read /proc/kmsg; it calls
klogctl(10) for the buffer size and klogctl(3) to read. 0 to the first one
means "the log is zero bytes", and a stub that answers 0 is
indistinguishable from a machine that logged nothing. Implemented for the
actions that mean something here; the rest return -EINVAL rather than 0,
because a console log level this kernel does not have is not a request that
succeeded.
- **With that fixed, dmesg was still empty.** It parses every line with
`sscanf(msg, "<%u>[%llu.%llu] %n", ...)` and silently drops anything that
does not match. The ring's `[ 0.00] text` already matches everything
after the priority -- scanf skips the leading spaces -- so the only missing
thing was the `<6>`. klog_render_syslog() adds it on the way out rather
than storing it: the ring is also what a person reads with `cat
/proc/kmsg` and what PSOGM points at after a crash, and an ABI detail
belongs at the boundary that has the ABI.
Also: the first version of the no-root diagnostic printed
%-11s device 1596400
because cprintf() has no field-width specifier -- it prints "%-11s" literally
and then reads the next argument for the %d. Plain %s now, with the note.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fdbd9bcf95
commit
7e88cea151
6 changed files with 307 additions and 21 deletions
|
|
@ -14,6 +14,7 @@
|
|||
#include "posix.h"
|
||||
#include "uaccess.h"
|
||||
#include "errno.h"
|
||||
#include "klog.h" /* KLOG_BYTES, for syslog(2) action 10 */
|
||||
|
||||
extern int linux_syscall_map[];
|
||||
extern int linux_syscall_map_size;
|
||||
|
|
@ -126,8 +127,71 @@ sys_uname(void)
|
|||
// socket/bind/listen/accept/connect live in sys_net.c now that they do
|
||||
// something.
|
||||
|
||||
// syslog: kernel logging facility — no-op for user processes.
|
||||
addr_t sys_syslog(void) { return 0; }
|
||||
// syslog(2) -- what `dmesg` actually calls.
|
||||
//
|
||||
// This returned 0 for every action, and 0 to action 10 means "the log buffer
|
||||
// is zero bytes". So `dmesg` printed nothing, on a system whose boot banner
|
||||
// says *"or dmesg once you are in"* and whose entire quiet-boot design is
|
||||
// "put the detail in the kernel log and tell them how to read it". The detail
|
||||
// was there the whole time -- `cat /proc/kmsg` has always worked -- but the
|
||||
// one command anybody would reach for could not see it.
|
||||
//
|
||||
// dmesg does not read /proc/kmsg. It calls klogctl(10) for the buffer size and
|
||||
// klogctl(3) to read, which is this syscall, and a stub that answers 0 is
|
||||
// indistinguishable from a machine that logged nothing.
|
||||
//
|
||||
// Only the actions that mean something here are implemented. The rest say so
|
||||
// rather than returning 0: a console log level this kernel does not have is
|
||||
// not a request that succeeded.
|
||||
#define SYSLOG_CLOSE 0
|
||||
#define SYSLOG_OPEN 1
|
||||
#define SYSLOG_READ 2
|
||||
#define SYSLOG_READ_ALL 3
|
||||
#define SYSLOG_CLEAR 5
|
||||
#define SYSLOG_SIZE_UNREAD 9
|
||||
#define SYSLOG_SIZE_BUFFER 10
|
||||
|
||||
addr_t sys_syslog(void)
|
||||
{
|
||||
int type, len;
|
||||
addr_t bufaddr;
|
||||
|
||||
argint(0, &type);
|
||||
argaddr(1, &bufaddr);
|
||||
argint(2, &len);
|
||||
|
||||
switch (type) {
|
||||
case SYSLOG_CLOSE:
|
||||
case SYSLOG_OPEN:
|
||||
return 0; // genuinely nothing to do, as on Linux
|
||||
|
||||
case SYSLOG_SIZE_BUFFER:
|
||||
return KLOG_BYTES;
|
||||
|
||||
case SYSLOG_SIZE_UNREAD:
|
||||
return (addr_t)klog_len();
|
||||
|
||||
case SYSLOG_READ_ALL: {
|
||||
char *dst;
|
||||
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
if (len == 0 || klog_len() == 0)
|
||||
return 0;
|
||||
if (argptr(1, &dst, len) < 0)
|
||||
return -EFAULT;
|
||||
return (addr_t)klog_render_syslog(dst, len);
|
||||
}
|
||||
|
||||
// Reading destructively needs a per-reader cursor the ring does not carry,
|
||||
// and clearing needs the ring to forget -- which is the one thing it exists
|
||||
// not to do, since its whole purpose is surviving the kernel that wrote it.
|
||||
case SYSLOG_READ:
|
||||
case SYSLOG_CLEAR:
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
// getrandom: fill buffer with pseudo-random bytes.
|
||||
addr_t sys_getrandom(void)
|
||||
|
|
|
|||
Loading…
Reference in a new issue