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:
parent
edc4e0537d
commit
eab13241a0
25 changed files with 2520 additions and 65 deletions
|
|
@ -17,6 +17,7 @@
|
|||
// another kernel; they are all in chapter 5 of the published standard.
|
||||
|
||||
#include "types.h"
|
||||
#include "hwinfo.h"
|
||||
#include "defs.h"
|
||||
#include "schboot.h" // SCHBOOT_RSDP_ADDR: the loader/kernel hand-off
|
||||
#include "param.h"
|
||||
|
|
@ -252,6 +253,87 @@ find_table(struct acpi_rsdp *rsdp, const char *sig)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// The n-th table, for /sys/firmware/acpi/tables: 0, 1 for a slot with nothing
|
||||
// readable in it (go on to n + 1), or -1 past the end. The root's entries in order,
|
||||
// then the DSDT and FACS: no root lists those two, only the FADT does, and
|
||||
// they are the ones anybody debugging a machine wants first.
|
||||
int
|
||||
acpi_table_nth(int n, uint64 *pa, uint32 *len, char sig[4])
|
||||
{
|
||||
struct acpi_rsdp *rsdp;
|
||||
struct acpi_sdt *root;
|
||||
int wide;
|
||||
|
||||
if (rsdp_phys == 0)
|
||||
return -1;
|
||||
rsdp = (struct acpi_rsdp *)P2V(rsdp_phys);
|
||||
if (rsdp->revision >= 2 && rsdp->xsdt_addr) {
|
||||
root = (struct acpi_sdt *)P2V(rsdp->xsdt_addr);
|
||||
wide = 1;
|
||||
} else {
|
||||
root = (struct acpi_sdt *)P2V((uint64)rsdp->rsdt_addr);
|
||||
wide = 0;
|
||||
}
|
||||
if (!checksum_ok(root, root->length))
|
||||
return -1;
|
||||
|
||||
uint32 cnt = (root->length - sizeof(struct acpi_sdt)) / (wide ? 8 : 4);
|
||||
uint64 fadt = 0;
|
||||
for (uint32 i = 0; i < cnt; i++) {
|
||||
uint64 p;
|
||||
if (wide) {
|
||||
uint8 *b = (uint8 *)(root + 1) + i * 8;
|
||||
p = 0;
|
||||
for (int k = 7; k >= 0; k--)
|
||||
p = (p << 8) | b[k];
|
||||
} else {
|
||||
p = ((uint32 *)(root + 1))[i];
|
||||
}
|
||||
if (p == 0 || p >= 0x100000000ULL) { /* outside the direct map */
|
||||
if ((int)i == n)
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
struct acpi_sdt *t = (struct acpi_sdt *)P2V(p);
|
||||
if (memcmp(t->sig, "FACP", 4) == 0)
|
||||
fadt = p;
|
||||
if ((int)i == n) {
|
||||
*pa = p;
|
||||
*len = t->length;
|
||||
memmove(sig, t->sig, 4);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (fadt == 0)
|
||||
return -1;
|
||||
|
||||
// From the FADT: DSDT at 40 (32-bit) or X_DSDT at 140, FACS at 36 or
|
||||
// X_FIRMWARE_CTRL at 132. The wide ones win when the table is long enough
|
||||
// to have them and they are set.
|
||||
struct acpi_sdt *f = (struct acpi_sdt *)P2V(fadt);
|
||||
uint8 *fb = (uint8 *)f;
|
||||
uint64 dsdt = *(uint32 *)(fb + 40), facs = *(uint32 *)(fb + 36);
|
||||
if (f->length >= 148 && *(uint64 *)(fb + 140))
|
||||
dsdt = *(uint64 *)(fb + 140);
|
||||
if (f->length >= 140 && *(uint64 *)(fb + 132))
|
||||
facs = *(uint64 *)(fb + 132);
|
||||
|
||||
uint64 extra[2] = { dsdt, facs };
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if ((int)cnt + k != n)
|
||||
continue;
|
||||
uint64 p = extra[k];
|
||||
if (p == 0 || p >= 0x100000000ULL)
|
||||
return 1;
|
||||
uint8 *t = (uint8 *)P2V(p);
|
||||
memmove(sig, t, 4);
|
||||
*pa = p;
|
||||
*len = *(uint32 *)(t + 4); /* both keep the length at 4 */
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns 1 if the processors were enumerated, 0 to fall back to mpinit().
|
||||
// One processor from the MADT.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "dma.h" // DMA_ARENA_BYTES: the top of PHYSTOP must be able to hold it
|
||||
#include "hwinfo.h"
|
||||
|
||||
// Set before kinit1() and never again. Starts at the old constant so that a
|
||||
// crash between entry and e820_init() behaves the way it always did rather
|
||||
|
|
@ -60,12 +61,43 @@ struct e820entry {
|
|||
uint32 acpi_ext;
|
||||
} __attribute__((packed));
|
||||
|
||||
// The map exactly as handed over, for /sys/firmware/memmap. The low page it
|
||||
// came in is not the kernel's to keep.
|
||||
static struct e820_rec map_copy[E820_MAXENTRIES];
|
||||
static int nmap_copy;
|
||||
|
||||
int
|
||||
e820_nth(int i, struct e820_rec *out)
|
||||
{
|
||||
if (i < 0 || i >= nmap_copy)
|
||||
return -1;
|
||||
*out = map_copy[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64
|
||||
ram_total_bytes(void)
|
||||
{
|
||||
uint64 t = 0;
|
||||
for (int i = 0; i < nmem_usable; i++)
|
||||
t += mem_usable[i].end - mem_usable[i].base;
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
e820_init(void)
|
||||
{
|
||||
uint32 count = *(uint32 *)P2V(SCHBOOT_E820_COUNT);
|
||||
struct e820entry *e = (struct e820entry *)P2V(SCHBOOT_E820_MAP);
|
||||
|
||||
if (count <= E820_MAXENTRIES)
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
map_copy[i].base = e[i].base;
|
||||
map_copy[i].len = e[i].length;
|
||||
map_copy[i].type = e[i].type;
|
||||
nmap_copy = (int)i + 1;
|
||||
}
|
||||
|
||||
// A count of zero means the firmware declined; anything absurd means we are
|
||||
// reading something that is not a memory map. Either way the constant is the
|
||||
// only honest answer left.
|
||||
|
|
@ -105,6 +137,20 @@ e820_init(void)
|
|||
if (base >= end)
|
||||
continue;
|
||||
|
||||
// Full: the smallest region gives way to a larger one. A map that
|
||||
// fragments RAM into more than MEMREGION_MAX pieces loses its crumbs
|
||||
// rather than whatever happens to be listed last, which is the RAM above
|
||||
// 4 GB.
|
||||
if (nmem_usable == MEMREGION_MAX) {
|
||||
int s = 0;
|
||||
for (int k = 1; k < nmem_usable; k++)
|
||||
if (mem_usable[k].end - mem_usable[k].base <
|
||||
mem_usable[s].end - mem_usable[s].base)
|
||||
s = k;
|
||||
if (mem_usable[s].end - mem_usable[s].base >= end - base)
|
||||
continue;
|
||||
mem_usable[s] = mem_usable[--nmem_usable];
|
||||
}
|
||||
mem_usable[nmem_usable].base = base;
|
||||
mem_usable[nmem_usable].end = end;
|
||||
nmem_usable++;
|
||||
|
|
|
|||
Loading…
Reference in a new issue