aboutsummaryrefslogblamecommitdiff
path: root/sys/kern/subr_module.c
blob: 1ada33c669dcf2281ec07ba57bc31ec9c8182d79 (plain) (tree)
1
2
3
4
5
6
7
   

                                                

                                   

                                 




















                                                                             

   


                      
                      

                       



                             
 


                         



                           

                                      




                                         
                                        

                     
                     
                     
    
                                   
                                
                  
                                   
                                           



                                                 
                                                           


                                    
                                                 
                                                 
                         








                                                  
                                        

                            
                     
                     
 
                                   
                                

                     
                                   
                                           







                                                   
                                                           


                                    
                                                 
                                                 












                                         
                     


                                   



                                                 

                                                 
                                                 




                                    
                                   







                                           
                                                 
                                                 
                         









                                                          
                                         

                     

                         
                     
 


                      

               
                               
                                 
                                       










                                                                        
 

                                                   
                                                                


                          
                                                  

                                
                                             
                                             
                     



                 





                                     

                           

                         


             

                                   


                                
                                   





                                                                         
 

                                
                                                               
                                                                    
                                    
                                                                           
                 
             




                                                                     
                                       
             

                                    
                                                 
                                                 




                         





















                                                                
                                                                  



                                              
                     



                                   

                                
                                   


                                           


                                                                
                                                 

                                                 
                                                                     
                               
                      



                                               
                                                 
                                                 



                         









































































































                                                                        




                                                  




                                                













































                                                                          


                                                 


                                               

































                                                                         
                                                                  


























































                                                                             
/*-
 * 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_FONT:
	    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
#ifdef MODINFOMD_VBE_FB
	case MODINFOMD_VBE_FB:
		sbuf_cat(sbp, "MODINFOMD_VBE_FB");
		break;
#endif
#ifdef MODINFOMD_FONT
	case MODINFOMD_FONT:
		sbuf_cat(sbp, "MODINFOMD_FONT");
		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
#ifdef MODINFOMD_VBE_FB
	case MODINFO_METADATA | MODINFOMD_VBE_FB:
#endif
#ifdef MODINFOMD_FONT
	case MODINFO_METADATA | MODINFOMD_FONT:
#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");