1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 1998 Michael Smith
5 * All rights reserved.
6 * Copyright (c) 2020 NetApp Inc.
7 * Copyright (c) 2020 Klara Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/linker.h>
34#include <sys/sbuf.h>
35#include <sys/sysctl.h>
36
37#include <machine/metadata.h>
38
39#include <vm/vm.h>
40#include <vm/vm_extern.h>
41
42/*
43 * Preloaded module support
44 */
45
46vm_offset_t preload_addr_relocate = 0;
47caddr_t preload_metadata;
48
49/*
50 * Search for the preloaded module (name)
51 */
52caddr_t
53preload_search_by_name(const char *name)
54{
55    caddr_t	curp;
56    uint32_t	*hdr;
57    int		next;
58
59    if (preload_metadata != NULL) {
60	curp = preload_metadata;
61	for (;;) {
62	    hdr = (uint32_t *)curp;
63	    if (hdr[0] == 0 && hdr[1] == 0)
64		break;
65
66	    /* Search for a MODINFO_NAME field */
67	    if ((hdr[0] == MODINFO_NAME) &&
68		!strcmp(name, curp + sizeof(uint32_t) * 2))
69		return(curp);
70
71	    /* skip to next field */
72	    next = sizeof(uint32_t) * 2 + hdr[1];
73	    next = roundup(next, sizeof(u_long));
74	    curp += next;
75	}
76    }
77    return(NULL);
78}
79
80/*
81 * Search for the first preloaded module of (type)
82 */
83caddr_t
84preload_search_by_type(const char *type)
85{
86    caddr_t	curp, lname;
87    uint32_t	*hdr;
88    int		next;
89
90    if (preload_metadata != NULL) {
91	curp = preload_metadata;
92	lname = NULL;
93	for (;;) {
94	    hdr = (uint32_t *)curp;
95	    if (hdr[0] == 0 && hdr[1] == 0)
96		break;
97
98	    /* remember the start of each record */
99	    if (hdr[0] == MODINFO_NAME)
100		lname = curp;
101
102	    /* Search for a MODINFO_TYPE field */
103	    if ((hdr[0] == MODINFO_TYPE) &&
104		!strcmp(type, curp + sizeof(uint32_t) * 2))
105		return(lname);
106
107	    /* skip to next field */
108	    next = sizeof(uint32_t) * 2 + hdr[1];
109	    next = roundup(next, sizeof(u_long));
110	    curp += next;
111	}
112    }
113    return(NULL);
114}
115
116/*
117 * Walk through the preloaded module list
118 */
119caddr_t
120preload_search_next_name(caddr_t base)
121{
122    caddr_t	curp;
123    uint32_t	*hdr;
124    int		next;
125
126    if (preload_metadata != NULL) {
127	/* Pick up where we left off last time */
128	if (base) {
129	    /* skip to next field */
130	    curp = base;
131	    hdr = (uint32_t *)curp;
132	    next = sizeof(uint32_t) * 2 + hdr[1];
133	    next = roundup(next, sizeof(u_long));
134	    curp += next;
135	} else
136	    curp = preload_metadata;
137
138	for (;;) {
139	    hdr = (uint32_t *)curp;
140	    if (hdr[0] == 0 && hdr[1] == 0)
141		break;
142
143	    /* Found a new record? */
144	    if (hdr[0] == MODINFO_NAME)
145		return curp;
146
147	    /* skip to next field */
148	    next = sizeof(uint32_t) * 2 + hdr[1];
149	    next = roundup(next, sizeof(u_long));
150	    curp += next;
151	}
152    }
153    return(NULL);
154}
155
156/*
157 * Given a preloaded module handle (mod), return a pointer
158 * to the data for the attribute (inf).
159 */
160caddr_t
161preload_search_info(caddr_t mod, int inf)
162{
163    caddr_t	curp;
164    uint32_t	*hdr;
165    uint32_t	type = 0;
166    int		next;
167
168    if (mod == NULL)
169    	return (NULL);
170
171    curp = mod;
172    for (;;) {
173	hdr = (uint32_t *)curp;
174	/* end of module data? */
175	if (hdr[0] == 0 && hdr[1] == 0)
176	    break;
177	/*
178	 * We give up once we've looped back to what we were looking at
179	 * first - this should normally be a MODINFO_NAME field.
180	 */
181	if (type == 0) {
182	    type = hdr[0];
183	} else {
184	    if (hdr[0] == type)
185		break;
186	}
187
188	/*
189	 * Attribute match? Return pointer to data.
190	 * Consumer may safely assume that size value precedes
191	 * data.
192	 */
193	if (hdr[0] == inf)
194	    return(curp + (sizeof(uint32_t) * 2));
195
196	/* skip to next field */
197	next = sizeof(uint32_t) * 2 + hdr[1];
198	next = roundup(next, sizeof(u_long));
199	curp += next;
200    }
201    return(NULL);
202}
203
204/*
205 * Delete a preload record by name.
206 */
207void
208preload_delete_name(const char *name)
209{
210    caddr_t	addr, curp;
211    uint32_t	*hdr, sz;
212    int		next;
213    int		clearing;
214
215    addr = 0;
216    sz = 0;
217
218    if (preload_metadata != NULL) {
219	clearing = 0;
220	curp = preload_metadata;
221	for (;;) {
222	    hdr = (uint32_t *)curp;
223	    if (hdr[0] == MODINFO_NAME || (hdr[0] == 0 && hdr[1] == 0)) {
224		/* Free memory used to store the file. */
225		if (addr != 0 && sz != 0)
226		    kmem_bootstrap_free((vm_offset_t)addr, sz);
227		addr = 0;
228		sz = 0;
229
230		if (hdr[0] == 0)
231		    break;
232		if (!strcmp(name, curp + sizeof(uint32_t) * 2))
233		    clearing = 1;	/* got it, start clearing */
234		else if (clearing) {
235		    clearing = 0;	/* at next one now.. better stop */
236		}
237	    }
238	    if (clearing) {
239		if (hdr[0] == MODINFO_ADDR)
240		    addr = *(caddr_t *)(curp + sizeof(uint32_t) * 2);
241		else if (hdr[0] == MODINFO_SIZE)
242		    sz = *(uint32_t *)(curp + sizeof(uint32_t) * 2);
243		hdr[0] = MODINFO_EMPTY;
244	    }
245
246	    /* skip to next field */
247	    next = sizeof(uint32_t) * 2 + hdr[1];
248	    next = roundup(next, sizeof(u_long));
249	    curp += next;
250	}
251    }
252}
253
254void *
255preload_fetch_addr(caddr_t mod)
256{
257	caddr_t *mdp;
258
259	mdp = (caddr_t *)preload_search_info(mod, MODINFO_ADDR);
260	if (mdp == NULL)
261		return (NULL);
262	return (*mdp + preload_addr_relocate);
263}
264
265size_t
266preload_fetch_size(caddr_t mod)
267{
268	size_t *mdp;
269
270	mdp = (size_t *)preload_search_info(mod, MODINFO_SIZE);
271	if (mdp == NULL)
272		return (0);
273	return (*mdp);
274}
275
276/* Called from locore.  Convert physical pointers to kvm. Sigh. */
277void
278preload_bootstrap_relocate(vm_offset_t offset)
279{
280    caddr_t	curp;
281    uint32_t	*hdr;
282    vm_offset_t	*ptr;
283    int		next;
284
285    if (preload_metadata != NULL) {
286	curp = preload_metadata;
287	for (;;) {
288	    hdr = (uint32_t *)curp;
289	    if (hdr[0] == 0 && hdr[1] == 0)
290		break;
291
292	    /* Deal with the ones that we know we have to fix */
293	    switch (hdr[0]) {
294	    case MODINFO_ADDR:
295	    case MODINFO_METADATA|MODINFOMD_FONT:
296	    case MODINFO_METADATA|MODINFOMD_SSYM:
297	    case MODINFO_METADATA|MODINFOMD_ESYM:
298		ptr = (vm_offset_t *)(curp + (sizeof(uint32_t) * 2));
299		*ptr += offset;
300		break;
301	    }
302	    /* The rest is beyond us for now */
303
304	    /* skip to next field */
305	    next = sizeof(uint32_t) * 2 + hdr[1];
306	    next = roundup(next, sizeof(u_long));
307	    curp += next;
308	}
309    }
310}
311
312/*
313 * Parse the modinfo type and append to the provided sbuf.
314 */
315static void
316preload_modinfo_type(struct sbuf *sbp, int type)
317{
318
319	if ((type & MODINFO_METADATA) == 0) {
320		switch (type) {
321		case MODINFO_END:
322			sbuf_cat(sbp, "MODINFO_END");
323			break;
324		case MODINFO_NAME:
325			sbuf_cat(sbp, "MODINFO_NAME");
326			break;
327		case MODINFO_TYPE:
328			sbuf_cat(sbp, "MODINFO_TYPE");
329			break;
330		case MODINFO_ADDR:
331			sbuf_cat(sbp, "MODINFO_ADDR");
332			break;
333		case MODINFO_SIZE:
334			sbuf_cat(sbp, "MODINFO_SIZE");
335			break;
336		case MODINFO_EMPTY:
337			sbuf_cat(sbp, "MODINFO_EMPTY");
338			break;
339		case MODINFO_ARGS:
340			sbuf_cat(sbp, "MODINFO_ARGS");
341			break;
342		default:
343			sbuf_cat(sbp, "unrecognized modinfo attribute");
344		}
345
346		return;
347	}
348
349	sbuf_cat(sbp, "MODINFO_METADATA | ");
350	switch (type & ~MODINFO_METADATA) {
351	case MODINFOMD_ELFHDR:
352		sbuf_cat(sbp, "MODINFOMD_ELFHDR");
353		break;
354	case MODINFOMD_SSYM:
355		sbuf_cat(sbp, "MODINFOMD_SSYM");
356		break;
357	case MODINFOMD_ESYM:
358		sbuf_cat(sbp, "MODINFOMD_ESYM");
359		break;
360	case MODINFOMD_DYNAMIC:
361		sbuf_cat(sbp, "MODINFOMD_DYNAMIC");
362		break;
363	case MODINFOMD_ENVP:
364		sbuf_cat(sbp, "MODINFOMD_ENVP");
365		break;
366	case MODINFOMD_HOWTO:
367		sbuf_cat(sbp, "MODINFOMD_HOWTO");
368		break;
369	case MODINFOMD_KERNEND:
370		sbuf_cat(sbp, "MODINFOMD_KERNEND");
371		break;
372	case MODINFOMD_SHDR:
373		sbuf_cat(sbp, "MODINFOMD_SHDR");
374		break;
375	case MODINFOMD_CTORS_ADDR:
376		sbuf_cat(sbp, "MODINFOMD_CTORS_ADDR");
377		break;
378	case MODINFOMD_CTORS_SIZE:
379		sbuf_cat(sbp, "MODINFOMD_CTORS_SIZE");
380		break;
381	case MODINFOMD_FW_HANDLE:
382		sbuf_cat(sbp, "MODINFOMD_FW_HANDLE");
383		break;
384	case MODINFOMD_KEYBUF:
385		sbuf_cat(sbp, "MODINFOMD_KEYBUF");
386		break;
387#ifdef MODINFOMD_SMAP
388	case MODINFOMD_SMAP:
389		sbuf_cat(sbp, "MODINFOMD_SMAP");
390		break;
391#endif
392#ifdef MODINFOMD_SMAP_XATTR
393	case MODINFOMD_SMAP_XATTR:
394		sbuf_cat(sbp, "MODINFOMD_SMAP_XATTR");
395		break;
396#endif
397#ifdef MODINFOMD_DTBP
398	case MODINFOMD_DTBP:
399		sbuf_cat(sbp, "MODINFOMD_DTBP");
400		break;
401#endif
402#ifdef MODINFOMD_EFI_MAP
403	case MODINFOMD_EFI_MAP:
404		sbuf_cat(sbp, "MODINFOMD_EFI_MAP");
405		break;
406#endif
407#ifdef MODINFOMD_EFI_FB
408	case MODINFOMD_EFI_FB:
409		sbuf_cat(sbp, "MODINFOMD_EFI_FB");
410		break;
411#endif
412#ifdef MODINFOMD_MODULEP
413	case MODINFOMD_MODULEP:
414		sbuf_cat(sbp, "MODINFOMD_MODULEP");
415		break;
416#endif
417#ifdef MODINFOMD_VBE_FB
418	case MODINFOMD_VBE_FB:
419		sbuf_cat(sbp, "MODINFOMD_VBE_FB");
420		break;
421#endif
422#ifdef MODINFOMD_FONT
423	case MODINFOMD_FONT:
424		sbuf_cat(sbp, "MODINFOMD_FONT");
425		break;
426#endif
427	default:
428		sbuf_cat(sbp, "unrecognized metadata type");
429	}
430}
431
432/*
433 * Print the modinfo value, depending on type.
434 */
435static void
436preload_modinfo_value(struct sbuf *sbp, uint32_t *bptr, int type, int len)
437{
438#ifdef __LP64__
439#define sbuf_print_vmoffset(sb, o)	sbuf_printf(sb, "0x%016lx", o);
440#else
441#define sbuf_print_vmoffset(sb, o)	sbuf_printf(sb, "0x%08x", o);
442#endif
443
444	switch (type) {
445	case MODINFO_NAME:
446	case MODINFO_TYPE:
447	case MODINFO_ARGS:
448		sbuf_printf(sbp, "%s", (char *)bptr);
449		break;
450	case MODINFO_SIZE:
451	case MODINFO_METADATA | MODINFOMD_CTORS_SIZE:
452		sbuf_printf(sbp, "%lu", *(u_long *)bptr);
453		break;
454	case MODINFO_ADDR:
455	case MODINFO_METADATA | MODINFOMD_SSYM:
456	case MODINFO_METADATA | MODINFOMD_ESYM:
457	case MODINFO_METADATA | MODINFOMD_DYNAMIC:
458	case MODINFO_METADATA | MODINFOMD_KERNEND:
459	case MODINFO_METADATA | MODINFOMD_ENVP:
460	case MODINFO_METADATA | MODINFOMD_CTORS_ADDR:
461#ifdef MODINFOMD_SMAP
462	case MODINFO_METADATA | MODINFOMD_SMAP:
463#endif
464#ifdef MODINFOMD_SMAP_XATTR
465	case MODINFO_METADATA | MODINFOMD_SMAP_XATTR:
466#endif
467#ifdef MODINFOMD_DTBP
468	case MODINFO_METADATA | MODINFOMD_DTBP:
469#endif
470#ifdef MODINFOMD_EFI_FB
471	case MODINFO_METADATA | MODINFOMD_EFI_FB:
472#endif
473#ifdef MODINFOMD_VBE_FB
474	case MODINFO_METADATA | MODINFOMD_VBE_FB:
475#endif
476#ifdef MODINFOMD_FONT
477	case MODINFO_METADATA | MODINFOMD_FONT:
478#endif
479		sbuf_print_vmoffset(sbp, *(vm_offset_t *)bptr);
480		break;
481	case MODINFO_METADATA | MODINFOMD_HOWTO:
482		sbuf_printf(sbp, "0x%08x", *bptr);
483		break;
484	case MODINFO_METADATA | MODINFOMD_SHDR:
485	case MODINFO_METADATA | MODINFOMD_ELFHDR:
486	case MODINFO_METADATA | MODINFOMD_FW_HANDLE:
487	case MODINFO_METADATA | MODINFOMD_KEYBUF:
488#ifdef MODINFOMD_EFI_MAP
489	case MODINFO_METADATA | MODINFOMD_EFI_MAP:
490#endif
491		/* Don't print data buffers. */
492		sbuf_cat(sbp, "buffer contents omitted");
493		break;
494	default:
495		break;
496	}
497#undef sbuf_print_vmoffset
498}
499
500static void
501preload_dump_internal(struct sbuf *sbp)
502{
503	uint32_t *bptr, type, len;
504
505	KASSERT(preload_metadata != NULL,
506	    ("%s called without setting up preload_metadata", __func__));
507
508	/*
509	 * Iterate through the TLV-encoded sections.
510	 */
511	bptr = (uint32_t *)preload_metadata;
512	sbuf_putc(sbp, '\n');
513	while (bptr[0] != MODINFO_END || bptr[1] != MODINFO_END) {
514		sbuf_printf(sbp, " %p:\n", bptr);
515		type = *bptr++;
516		len = *bptr++;
517
518		sbuf_printf(sbp, "\ttype:\t(%#04x) ", type);
519		preload_modinfo_type(sbp, type);
520		sbuf_putc(sbp, '\n');
521		sbuf_printf(sbp, "\tlen:\t%u\n", len);
522		sbuf_cat(sbp, "\tvalue:\t");
523		preload_modinfo_value(sbp, bptr, type, len);
524		sbuf_putc(sbp, '\n');
525
526		bptr += roundup(len, sizeof(u_long)) / sizeof(uint32_t);
527	}
528}
529
530/*
531 * Print the preloaded data to the console. Called from the machine-dependent
532 * initialization routines, e.g. hammer_time().
533 */
534void
535preload_dump(void)
536{
537	char buf[512];
538	struct sbuf sb;
539
540	/*
541	 * This function is expected to be called before malloc is available,
542	 * so use a static buffer and struct sbuf.
543	 */
544	sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
545	sbuf_set_drain(&sb, sbuf_printf_drain, NULL);
546	preload_dump_internal(&sb);
547
548	sbuf_finish(&sb);
549	sbuf_delete(&sb);
550}
551
552static int
553sysctl_preload_dump(SYSCTL_HANDLER_ARGS)
554{
555	struct sbuf sb;
556	int error;
557
558	if (preload_metadata == NULL)
559		return (EINVAL);
560
561	sbuf_new_for_sysctl(&sb, NULL, 512, req);
562	preload_dump_internal(&sb);
563
564	error = sbuf_finish(&sb);
565	sbuf_delete(&sb);
566
567	return (error);
568}
569SYSCTL_PROC(_debug, OID_AUTO, dump_modinfo,
570    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
571    NULL, 0, sysctl_preload_dump, "A",
572    "pretty-print the bootloader metadata");
573