Index: head/sys/amd64/amd64/machdep.c =================================================================== --- head/sys/amd64/amd64/machdep.c +++ head/sys/amd64/amd64/machdep.c @@ -1853,6 +1853,15 @@ if (late_console) cninit(); + /* + * Dump the boot metadata. We have to wait for cninit() since console + * output is required. If it's grossly incorrect the kernel will never + * make it this far. + */ + if ((boothowto & RB_VERBOSE) && + getenv_is_true("debug.dump_modinfo_at_boot")) + preload_dump(); + #ifdef DEV_ISA #ifdef DEV_ATPIC elcr_probe(); Index: head/sys/arm/arm/machdep.c =================================================================== --- head/sys/arm/arm/machdep.c +++ head/sys/arm/arm/machdep.c @@ -1027,6 +1027,15 @@ debugf(" dtbp = 0x%08x\n", (uint32_t)dtbp); arm_print_kenv(); + /* + * Dump the boot metadata. We have to wait for cninit() since console + * output is required. If it's grossly incorrect the kernel will never + * make it this far. + */ + if ((boothowto & RB_VERBOSE) && + getenv_is_true("debug.dump_modinfo_at_boot")) + preload_dump(); + env = kern_getenv("kernelname"); if (env != NULL) { strlcpy(kernelname, env, sizeof(kernelname)); Index: head/sys/arm64/arm64/machdep.c =================================================================== --- head/sys/arm64/arm64/machdep.c +++ head/sys/arm64/arm64/machdep.c @@ -1242,6 +1242,15 @@ panic("Invalid bus configuration: %s", kern_getenv("kern.cfg.order")); + /* + * Dump the boot metadata. We have to wait for cninit() since console + * output is required. If it's grossly incorrect the kernel will never + * make it this far. + */ + if ((boothowto & RB_VERBOSE) && + getenv_is_true("debug.dump_modinfo_at_boot")) + preload_dump(); + init_proc0(abp->kern_stack); msgbufinit(msgbufp, msgbufsize); mutex_init(); Index: head/sys/kern/subr_module.c =================================================================== --- head/sys/kern/subr_module.c +++ head/sys/kern/subr_module.c @@ -3,6 +3,8 @@ * * Copyright (c) 1998 Michael Smith * All rights reserved. + * Copyright (c) 2020 NetApp Inc. + * Copyright (c) 2020 Klara Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +34,11 @@ #include #include #include +#include +#include +#include + #include #include @@ -304,3 +310,249 @@ } } } + +/* + * Parse the modinfo type and append to the provided sbuf. + */ +static void +preload_modinfo_type(struct sbuf *sbp, int type) +{ + + if ((type & MODINFO_METADATA) == 0) { + switch (type) { + case MODINFO_END: + sbuf_cat(sbp, "MODINFO_END"); + break; + case MODINFO_NAME: + sbuf_cat(sbp, "MODINFO_NAME"); + break; + case MODINFO_TYPE: + sbuf_cat(sbp, "MODINFO_TYPE"); + break; + case MODINFO_ADDR: + sbuf_cat(sbp, "MODINFO_ADDR"); + break; + case MODINFO_SIZE: + sbuf_cat(sbp, "MODINFO_SIZE"); + break; + case MODINFO_EMPTY: + sbuf_cat(sbp, "MODINFO_EMPTY"); + break; + case MODINFO_ARGS: + sbuf_cat(sbp, "MODINFO_ARGS"); + break; + default: + sbuf_cat(sbp, "unrecognized modinfo attribute"); + } + + return; + } + + sbuf_cat(sbp, "MODINFO_METADATA | "); + switch (type & ~MODINFO_METADATA) { + case MODINFOMD_ELFHDR: + sbuf_cat(sbp, "MODINFOMD_ELFHDR"); + break; + case MODINFOMD_SSYM: + sbuf_cat(sbp, "MODINFOMD_SSYM"); + break; + case MODINFOMD_ESYM: + sbuf_cat(sbp, "MODINFOMD_ESYM"); + break; + case MODINFOMD_DYNAMIC: + sbuf_cat(sbp, "MODINFOMD_DYNAMIC"); + break; + case MODINFOMD_ENVP: + sbuf_cat(sbp, "MODINFOMD_ENVP"); + break; + case MODINFOMD_HOWTO: + sbuf_cat(sbp, "MODINFOMD_HOWTO"); + break; + case MODINFOMD_KERNEND: + sbuf_cat(sbp, "MODINFOMD_KERNEND"); + break; + case MODINFOMD_SHDR: + sbuf_cat(sbp, "MODINFOMD_SHDR"); + break; + case MODINFOMD_CTORS_ADDR: + sbuf_cat(sbp, "MODINFOMD_CTORS_ADDR"); + break; + case MODINFOMD_CTORS_SIZE: + sbuf_cat(sbp, "MODINFOMD_CTORS_SIZE"); + break; + case MODINFOMD_FW_HANDLE: + sbuf_cat(sbp, "MODINFOMD_FW_HANDLE"); + break; + case MODINFOMD_KEYBUF: + sbuf_cat(sbp, "MODINFOMD_KEYBUF"); + break; +#ifdef MODINFOMD_SMAP + case MODINFOMD_SMAP: + sbuf_cat(sbp, "MODINFOMD_SMAP"); + break; +#endif +#ifdef MODINFOMD_SMAP_XATTR + case MODINFOMD_SMAP_XATTR: + sbuf_cat(sbp, "MODINFOMD_SMAP_XATTR"); + break; +#endif +#ifdef MODINFOMD_DTBP + case MODINFOMD_DTBP: + sbuf_cat(sbp, "MODINFOMD_DTBP"); + break; +#endif +#ifdef MODINFOMD_EFI_MAP + case MODINFOMD_EFI_MAP: + sbuf_cat(sbp, "MODINFOMD_EFI_MAP"); + break; +#endif +#ifdef MODINFOMD_EFI_FB + case MODINFOMD_EFI_FB: + sbuf_cat(sbp, "MODINFOMD_EFI_FB"); + break; +#endif +#ifdef MODINFOMD_MODULEP + case MODINFOMD_MODULEP: + sbuf_cat(sbp, "MODINFOMD_MODULEP"); + break; +#endif + default: + sbuf_cat(sbp, "unrecognized metadata type"); + } +} + +/* + * Print the modinfo value, depending on type. + */ +static void +preload_modinfo_value(struct sbuf *sbp, uint32_t *bptr, int type, int len) +{ +#ifdef __LP64__ +#define sbuf_print_vmoffset(sb, o) sbuf_printf(sb, "0x%016lx", o); +#else +#define sbuf_print_vmoffset(sb, o) sbuf_printf(sb, "0x%08x", o); +#endif + + switch (type) { + case MODINFO_NAME: + case MODINFO_TYPE: + case MODINFO_ARGS: + sbuf_printf(sbp, "%s", (char *)bptr); + break; + case MODINFO_SIZE: + case MODINFO_METADATA | MODINFOMD_CTORS_SIZE: + sbuf_printf(sbp, "%lu", *(u_long *)bptr); + break; + case MODINFO_ADDR: + case MODINFO_METADATA | MODINFOMD_SSYM: + case MODINFO_METADATA | MODINFOMD_ESYM: + case MODINFO_METADATA | MODINFOMD_DYNAMIC: + case MODINFO_METADATA | MODINFOMD_KERNEND: + case MODINFO_METADATA | MODINFOMD_ENVP: + case MODINFO_METADATA | MODINFOMD_CTORS_ADDR: +#ifdef MODINFOMD_SMAP + case MODINFO_METADATA | MODINFOMD_SMAP: +#endif +#ifdef MODINFOMD_SMAP_XATTR + case MODINFO_METADATA | MODINFOMD_SMAP_XATTR: +#endif +#ifdef MODINFOMD_DTBP + case MODINFO_METADATA | MODINFOMD_DTBP: +#endif +#ifdef MODINFOMD_EFI_FB + case MODINFO_METADATA | MODINFOMD_EFI_FB: +#endif + sbuf_print_vmoffset(sbp, *(vm_offset_t *)bptr); + break; + case MODINFO_METADATA | MODINFOMD_HOWTO: + sbuf_printf(sbp, "0x%08x", *bptr); + break; + case MODINFO_METADATA | MODINFOMD_SHDR: + case MODINFO_METADATA | MODINFOMD_ELFHDR: + case MODINFO_METADATA | MODINFOMD_FW_HANDLE: + case MODINFO_METADATA | MODINFOMD_KEYBUF: +#ifdef MODINFOMD_EFI_MAP + case MODINFO_METADATA | MODINFOMD_EFI_MAP: +#endif + /* Don't print data buffers. */ + sbuf_cat(sbp, "buffer contents omitted"); + break; + default: + break; + } +#undef sbuf_print_vmoffset +} + +static void +preload_dump_internal(struct sbuf *sbp) +{ + uint32_t *bptr, type, len; + + KASSERT(preload_metadata != NULL, + ("%s called without setting up preload_metadata", __func__)); + + /* + * Iterate through the TLV-encoded sections. + */ + bptr = (uint32_t *)preload_metadata; + sbuf_putc(sbp, '\n'); + while (bptr[0] != MODINFO_END || bptr[0] != MODINFO_END) { + sbuf_printf(sbp, " %p:\n", bptr); + type = *bptr++; + len = *bptr++; + + sbuf_printf(sbp, "\ttype:\t(%#04x) ", type); + preload_modinfo_type(sbp, type); + sbuf_putc(sbp, '\n'); + sbuf_printf(sbp, "\tlen:\t%u\n", len); + sbuf_cat(sbp, "\tvalue:\t"); + preload_modinfo_value(sbp, bptr, type, len); + sbuf_putc(sbp, '\n'); + + bptr += roundup(len, sizeof(u_long)) / sizeof(uint32_t); + } +} + +/* + * Print the preloaded data to the console. Called from the machine-dependent + * initialization routines, e.g. hammer_time(). + */ +void +preload_dump(void) +{ + char buf[512]; + struct sbuf sb; + + /* + * This function is expected to be called before malloc is available, + * so use a static buffer and struct sbuf. + */ + sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN); + sbuf_set_drain(&sb, sbuf_printf_drain, NULL); + preload_dump_internal(&sb); + + sbuf_finish(&sb); + sbuf_delete(&sb); +} + +static int +sysctl_preload_dump(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sb; + int error; + + if (preload_metadata == NULL) + return (EINVAL); + + sbuf_new_for_sysctl(&sb, NULL, 512, req); + preload_dump_internal(&sb); + + error = sbuf_finish(&sb); + sbuf_delete(&sb); + + return (error); +} +SYSCTL_PROC(_debug, OID_AUTO, dump_modinfo, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, sysctl_preload_dump, "A", + "pretty-print the bootloader metadata"); Index: head/sys/riscv/riscv/machdep.c =================================================================== --- head/sys/riscv/riscv/machdep.c +++ head/sys/riscv/riscv/machdep.c @@ -949,6 +949,15 @@ cninit(); + /* + * Dump the boot metadata. We have to wait for cninit() since console + * output is required. If it's grossly incorrect the kernel will never + * make it this far. + */ + if ((boothowto & RB_VERBOSE) && + getenv_is_true("debug.dump_modinfo_at_boot")) + preload_dump(); + init_proc0(rvbp->kern_stack); msgbufinit(msgbufp, msgbufsize); Index: head/sys/sys/linker.h =================================================================== --- head/sys/sys/linker.h +++ head/sys/sys/linker.h @@ -257,6 +257,7 @@ extern caddr_t preload_search_info(caddr_t _mod, int _inf); extern void preload_delete_name(const char *_name); extern void preload_bootstrap_relocate(vm_offset_t _offset); +extern void preload_dump(void); #ifdef KLD_DEBUG