hwreport: describe a machine onto a USB stick, from a Linux-style /sys

hwreport writes one folder to the first read-write FAT mount, or mounts
the first USB stick that takes vfat itself and unmounts it afterwards:
summary.txt (machine, firmware, CPU, memory, every PCI and USB device
with its driver or "-- no driver"), dmesg.txt, lspci.txt in the shape of
lspci -nnk -xxx, lsusb.txt, dmi.txt, memmap.txt, the ACPI tables as raw
.dat files iasl -d reads, and copies of /proc. Serials, the UUID and the
MSDM/SLIC tables (a Windows licence key) are left out.

It reads /sys, which is new: sysfs at Linux's paths and in Linux's
formats -- /sys/bus/pci/devices with uevent, config and resource,
/sys/bus/usb/devices, /sys/firmware/acpi/tables, dmi/tables, memmap
and efi, /sys/class/dmi/id. toybox's lspci and lsusb work unmodified
against it. The USB core now keeps a table of attached devices for it.

The machine's name comes from SMBIOS (kernel/dev/dmi.c). The UEFI loader
hands over the entry point from the configuration table, the kernel
falls back to the 0xF0000 scan, and the log gets Linux's "DMI:" line.

Two bugs found on the way:

- The loader handed over at most 40 memory-map entries, in firmware
  order. A laptop's UEFI map is 60-150 descriptors with RAM above 4 GB
  listed last, so that is what was dropped. The entries now have their
  own area at 0x31000 with room for 256, are sorted before merging, and
  the kernel's 32 usable regions keep the largest when there are more.
- MemTotal and sysinfo's totalram were PHYSTOP, an address, which counts
  the PCI hole: a 6 GB machine reported 8. Now the usable RAM, summed.

usb.ids moves to /usr/share/hwdata, where lsusb looks for it, and
pci.ids joins it (`make pci-ids`, BSD-3-Clause like usb.ids).

Checked under QEMU q35: the report written to a FAT32 stick, fsck.fat
clean, DSDT.dat disassembled by iasl on the host, lspci -k and lsusb
showing names, 6 GB of RAM seen as 6137 MB.

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-23 12:54:11 +02:00
commit eab13241a0
25 changed files with 2520 additions and 65 deletions

View file

@ -26,7 +26,7 @@ KERN_CORE = $(addprefix $(BUILD)/kernel/core/, \
KERN_DEV = $(addprefix $(BUILD)/kernel/dev/, \
bio.o blkdev.o cdev.o device.o disk.o fb.o ide.o input.o irq.o kbd.o keymap.o mouse.o \
pci.o pciuser.o text.o ttf.o uart.o gfx.o vt.o ramdisk.o procfs.o \
dbg.o netuser.o audio.o)
dbg.o netuser.o audio.o dmi.o)
# Drivers for hardware that has to be *found* rather than assumed. Anything
# discovered by walking a bus belongs here; the fixed-address legacy devices
@ -55,7 +55,7 @@ KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/storage/usb_storage.o
KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/hub/usb_hub.o
KERN_FS = $(addprefix $(BUILD)/kernel/fs/, \
exec.o file.o fs.o log.o pipe.o procfs.o devfs.o)
exec.o file.o fs.o log.o pipe.o procfs.o devfs.o sysfs.o)
# SchFS. Every one of these is the very same source the host tools compile, so
# the kernel and mkschfs cannot disagree about the on-disk format: there is one
@ -128,7 +128,7 @@ KERN_NOENTRY = $(filter-out $(BUILD)/kernel/arch/x86_64/entry.o, $(KERNOBJS))
# Third-party software does NOT go here — see ports/.
# -----------------------------------------------------------------------
ULIB = $(addprefix $(BUILD)/lib/, start.o glue.o usys.o)
CMDS = schinit schinitd fbinfo shmtest socktest inputtest nettest reflink schattr ping chvt loadkeys sigtest memfdtest pcitest vgpud schdbg schdbgd resolvconf httpd schmode netecho ldlsd tone
CMDS = schinit schinitd fbinfo shmtest socktest inputtest nettest reflink schattr ping chvt loadkeys sigtest memfdtest pcitest vgpud schdbg schdbgd resolvconf httpd schmode netecho ldlsd tone hwreport
CMDBINS = $(addprefix $(BUILD)/cmd/, $(CMDS))
# -----------------------------------------------------------------------
@ -478,10 +478,10 @@ RAMDISK_BLOCKS ?= 8000
# This is not hypothetical: a 33 MB test payload once landed here and overflowed
# the 6 MB image, and mkfs's "increase FSSIZE" message then pointed at a constant
# that had nothing to do with it.
# usb.ids is 730 KB of a 6 MB image -- 12% of the ramdisk root -- and nothing
# needs it to boot. It is a lookup table for a userspace tool, so it stays on
# the disk image and out of the kernel binary.
RAMDISK_EXCLUDE = usr/share/usb.ids
# usb.ids and pci.ids are 2 MB of a 6 MB image and nothing needs them to boot.
# They are lookup tables for userspace tools, so they stay on the disk image
# and out of the kernel binary.
RAMDISK_EXCLUDE = usr/share/hwdata
RAMDISK_EXCLUDE_ARGS = $(foreach p,$(RAMDISK_EXCLUDE),--exclude $(p))
ifdef RAMROOT
@ -1282,6 +1282,12 @@ etc-install: $(SCHINIT_JOBS) $(ETC_PLAIN) $(ETC_ZSH) $(LOGO_ASCII) $(SRV_FILES)
@cp $(ETC_ZSH) $(SYSROOT)/etc/zsh/
@cp $(LOGO_ASCII) $(SYSROOT)/usr/share/schos/logo.txt
@cp $(SRV_FILES) $(SYSROOT)/srv/
@# The device-name databases, where lspci and lsusb look for them: the
@# hwdata package's directory. They were at /usr/share/usb.ids, which no
@# tool searches, so lsusb printed numbers only.
@mkdir -p $(SYSROOT)/usr/share/hwdata
@rm -f $(SYSROOT)/usr/share/usb.ids
@for f in usb.ids pci.ids; do [ -f dist/$$f ] && cp dist/$$f $(SYSROOT)/usr/share/hwdata/; done; true
@echo "installed $(words $(ETC_PLAIN)) /etc file(s), $(words $(ETC_ZSH)) zsh file(s) and $(words $(SCHINIT_JOBS)) schinit manifest(s)"
# The mount points. Created here rather than by init, because a mkdir on a
@ -1515,20 +1521,28 @@ logo:
# critical path of `make`. dist/ exists precisely so a rebuild never needs the
# network -- see docs/layout.md. `make usb-ids` is a deliberate act; the file it
# writes is committed and dated, so what shipped is always knowable.
#
# pci.ids is the same arrangement for PCI, from the PCI ID Repository, under
# the same two licences. `make hwdata` refreshes both.
USB_IDS_URL = http://www.linux-usb.org/usb.ids
PCI_IDS_URL = https://pci-ids.ucw.cz/v2.2/pci.ids
usb-ids:
@echo "==> fetching $(USB_IDS_URL)"
@curl -sfL --max-time 60 -o dist/usb.ids.new "$(USB_IDS_URL)" || \
{ echo " fetch failed; keeping the cached copy"; rm -f dist/usb.ids.new; exit 1; }
usb-ids pci-ids:
@url="$(if $(filter usb-ids,$@),$(USB_IDS_URL),$(PCI_IDS_URL))"; \
f=$(subst -,.,$@); \
echo "==> fetching $$url"; \
curl -sfL --max-time 120 -o dist/$$f.new "$$url" || \
{ echo " fetch failed; keeping the cached copy"; rm -f dist/$$f.new; exit 1; }; \
grep -q "^#.*Version:" dist/$$f.new || \
{ echo " that is not a $$f file; keeping the cached copy"; \
rm -f dist/$$f.new; exit 1; }; \
mv dist/$$f.new dist/$$f; \
mkdir -p $(SYSROOT)/usr/share/hwdata; cp dist/$$f $(SYSROOT)/usr/share/hwdata/; \
grep -m1 "^#.*Version:" dist/$$f
@# A truncated or error-page download would otherwise silently replace a
@# good database with rubbish. Check it looks like what it claims to be.
@grep -q "^# Version:" dist/usb.ids.new || \
{ echo " that is not a usb.ids file; keeping the cached copy"; \
rm -f dist/usb.ids.new; exit 1; }
@mv dist/usb.ids.new dist/usb.ids
@cp dist/usb.ids $(SYSROOT)/usr/share/usb.ids
@grep -m1 "^# Version:" dist/usb.ids
@# good database with rubbish, which is what the Version check is for.
hwdata: usb-ids pci-ids
# Build the kernel's QR encoder on the host and check it.
#
@ -1559,7 +1573,7 @@ qrcheck: $(QRTEST)
.PHONY: disk all kernel ramroot cmd ports projects sysroot-install qemu qemu-console qemu-kvm schcom-test \
schfs-test schfs-objtest schfs-image-test schfs-check kmem-test mkschfs \
schfs-fsck crashshot qemu-schfs iso qemu-iso clean distclean \
logo qrtest qrcheck crashqr usb-ids ovmf esp uefi
logo qrtest qrcheck crashqr usb-ids pci-ids hwdata ovmf esp uefi
.PRECIOUS: $(BUILD)/%.o
# 'clean' removes generated output only. dist/ (upstream tarballs) survives,