The correct way to identify the end of the metadata is two adjacent entries set to zero/MODINFO_END. I made a typo and this was checking the first entry twice. Reported by: rpokala Sponsored by: NetApp, Inc. Sponsored by: Klara, Inc.
559 lines
13 KiB
C
559 lines
13 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/linker.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <machine/metadata.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_extern.h>
|
|
|
|
/*
|
|
* Preloaded module support
|
|
*/
|
|
|
|
vm_offset_t preload_addr_relocate = 0;
|
|
caddr_t preload_metadata;
|
|
|
|
/*
|
|
* Search for the preloaded module (name)
|
|
*/
|
|
caddr_t
|
|
preload_search_by_name(const char *name)
|
|
{
|
|
caddr_t curp;
|
|
uint32_t *hdr;
|
|
int next;
|
|
|
|
if (preload_metadata != NULL) {
|
|
curp = preload_metadata;
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
if (hdr[0] == 0 && hdr[1] == 0)
|
|
break;
|
|
|
|
/* Search for a MODINFO_NAME field */
|
|
if ((hdr[0] == MODINFO_NAME) &&
|
|
!strcmp(name, curp + sizeof(uint32_t) * 2))
|
|
return(curp);
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Search for the first preloaded module of (type)
|
|
*/
|
|
caddr_t
|
|
preload_search_by_type(const char *type)
|
|
{
|
|
caddr_t curp, lname;
|
|
uint32_t *hdr;
|
|
int next;
|
|
|
|
if (preload_metadata != NULL) {
|
|
curp = preload_metadata;
|
|
lname = NULL;
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
if (hdr[0] == 0 && hdr[1] == 0)
|
|
break;
|
|
|
|
/* remember the start of each record */
|
|
if (hdr[0] == MODINFO_NAME)
|
|
lname = curp;
|
|
|
|
/* Search for a MODINFO_TYPE field */
|
|
if ((hdr[0] == MODINFO_TYPE) &&
|
|
!strcmp(type, curp + sizeof(uint32_t) * 2))
|
|
return(lname);
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Walk through the preloaded module list
|
|
*/
|
|
caddr_t
|
|
preload_search_next_name(caddr_t base)
|
|
{
|
|
caddr_t curp;
|
|
uint32_t *hdr;
|
|
int next;
|
|
|
|
if (preload_metadata != NULL) {
|
|
/* Pick up where we left off last time */
|
|
if (base) {
|
|
/* skip to next field */
|
|
curp = base;
|
|
hdr = (uint32_t *)curp;
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
} else
|
|
curp = preload_metadata;
|
|
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
if (hdr[0] == 0 && hdr[1] == 0)
|
|
break;
|
|
|
|
/* Found a new record? */
|
|
if (hdr[0] == MODINFO_NAME)
|
|
return curp;
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Given a preloaded module handle (mod), return a pointer
|
|
* to the data for the attribute (inf).
|
|
*/
|
|
caddr_t
|
|
preload_search_info(caddr_t mod, int inf)
|
|
{
|
|
caddr_t curp;
|
|
uint32_t *hdr;
|
|
uint32_t type = 0;
|
|
int next;
|
|
|
|
if (mod == NULL)
|
|
return (NULL);
|
|
|
|
curp = mod;
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
/* end of module data? */
|
|
if (hdr[0] == 0 && hdr[1] == 0)
|
|
break;
|
|
/*
|
|
* We give up once we've looped back to what we were looking at
|
|
* first - this should normally be a MODINFO_NAME field.
|
|
*/
|
|
if (type == 0) {
|
|
type = hdr[0];
|
|
} else {
|
|
if (hdr[0] == type)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Attribute match? Return pointer to data.
|
|
* Consumer may safely assume that size value precedes
|
|
* data.
|
|
*/
|
|
if (hdr[0] == inf)
|
|
return(curp + (sizeof(uint32_t) * 2));
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Delete a preload record by name.
|
|
*/
|
|
void
|
|
preload_delete_name(const char *name)
|
|
{
|
|
caddr_t addr, curp;
|
|
uint32_t *hdr, sz;
|
|
int next;
|
|
int clearing;
|
|
|
|
addr = 0;
|
|
sz = 0;
|
|
|
|
if (preload_metadata != NULL) {
|
|
clearing = 0;
|
|
curp = preload_metadata;
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
if (hdr[0] == MODINFO_NAME || (hdr[0] == 0 && hdr[1] == 0)) {
|
|
/* Free memory used to store the file. */
|
|
if (addr != 0 && sz != 0)
|
|
kmem_bootstrap_free((vm_offset_t)addr, sz);
|
|
addr = 0;
|
|
sz = 0;
|
|
|
|
if (hdr[0] == 0)
|
|
break;
|
|
if (!strcmp(name, curp + sizeof(uint32_t) * 2))
|
|
clearing = 1; /* got it, start clearing */
|
|
else if (clearing) {
|
|
clearing = 0; /* at next one now.. better stop */
|
|
}
|
|
}
|
|
if (clearing) {
|
|
if (hdr[0] == MODINFO_ADDR)
|
|
addr = *(caddr_t *)(curp + sizeof(uint32_t) * 2);
|
|
else if (hdr[0] == MODINFO_SIZE)
|
|
sz = *(uint32_t *)(curp + sizeof(uint32_t) * 2);
|
|
hdr[0] = MODINFO_EMPTY;
|
|
}
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *
|
|
preload_fetch_addr(caddr_t mod)
|
|
{
|
|
caddr_t *mdp;
|
|
|
|
mdp = (caddr_t *)preload_search_info(mod, MODINFO_ADDR);
|
|
if (mdp == NULL)
|
|
return (NULL);
|
|
return (*mdp + preload_addr_relocate);
|
|
}
|
|
|
|
size_t
|
|
preload_fetch_size(caddr_t mod)
|
|
{
|
|
size_t *mdp;
|
|
|
|
mdp = (size_t *)preload_search_info(mod, MODINFO_SIZE);
|
|
if (mdp == NULL)
|
|
return (0);
|
|
return (*mdp);
|
|
}
|
|
|
|
/* Called from locore. Convert physical pointers to kvm. Sigh. */
|
|
void
|
|
preload_bootstrap_relocate(vm_offset_t offset)
|
|
{
|
|
caddr_t curp;
|
|
uint32_t *hdr;
|
|
vm_offset_t *ptr;
|
|
int next;
|
|
|
|
if (preload_metadata != NULL) {
|
|
curp = preload_metadata;
|
|
for (;;) {
|
|
hdr = (uint32_t *)curp;
|
|
if (hdr[0] == 0 && hdr[1] == 0)
|
|
break;
|
|
|
|
/* Deal with the ones that we know we have to fix */
|
|
switch (hdr[0]) {
|
|
case MODINFO_ADDR:
|
|
case MODINFO_METADATA|MODINFOMD_SSYM:
|
|
case MODINFO_METADATA|MODINFOMD_ESYM:
|
|
ptr = (vm_offset_t *)(curp + (sizeof(uint32_t) * 2));
|
|
*ptr += offset;
|
|
break;
|
|
}
|
|
/* The rest is beyond us for now */
|
|
|
|
/* skip to next field */
|
|
next = sizeof(uint32_t) * 2 + hdr[1];
|
|
next = roundup(next, sizeof(u_long));
|
|
curp += next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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[1] != 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");
|