1/*-
2 * Copyright (c) 1998 Michael Smith
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/linker.h>
33
34/*
35 * Preloaded module support
36 */
37
38vm_offset_t preload_addr_relocate = 0;
39caddr_t preload_metadata;
40
41/*
42 * Search for the preloaded module (name)
43 */
44caddr_t
45preload_search_by_name(const char *name)
46{
47    caddr_t	curp;
48    uint32_t	*hdr;
49    int		next;
50
51    if (preload_metadata != NULL) {
52
53	curp = preload_metadata;
54	for (;;) {
55	    hdr = (uint32_t *)curp;
56	    if (hdr[0] == 0 && hdr[1] == 0)
57		break;
58
59	    /* Search for a MODINFO_NAME field */
60	    if ((hdr[0] == MODINFO_NAME) &&
61		!strcmp(name, curp + sizeof(uint32_t) * 2))
62		return(curp);
63
64	    /* skip to next field */
65	    next = sizeof(uint32_t) * 2 + hdr[1];
66	    next = roundup(next, sizeof(u_long));
67	    curp += next;
68	}
69    }
70    return(NULL);
71}
72
73/*
74 * Search for the first preloaded module of (type)
75 */
76caddr_t
77preload_search_by_type(const char *type)
78{
79    caddr_t	curp, lname;
80    uint32_t	*hdr;
81    int		next;
82
83    if (preload_metadata != NULL) {
84
85	curp = preload_metadata;
86	lname = NULL;
87	for (;;) {
88	    hdr = (uint32_t *)curp;
89	    if (hdr[0] == 0 && hdr[1] == 0)
90		break;
91
92	    /* remember the start of each record */
93	    if (hdr[0] == MODINFO_NAME)
94		lname = curp;
95
96	    /* Search for a MODINFO_TYPE field */
97	    if ((hdr[0] == MODINFO_TYPE) &&
98		!strcmp(type, curp + sizeof(uint32_t) * 2))
99		return(lname);
100
101	    /* skip to next field */
102	    next = sizeof(uint32_t) * 2 + hdr[1];
103	    next = roundup(next, sizeof(u_long));
104	    curp += next;
105	}
106    }
107    return(NULL);
108}
109
110/*
111 * Walk through the preloaded module list
112 */
113caddr_t
114preload_search_next_name(caddr_t base)
115{
116    caddr_t	curp;
117    uint32_t	*hdr;
118    int		next;
119
120    if (preload_metadata != NULL) {
121
122	/* Pick up where we left off last time */
123	if (base) {
124	    /* skip to next field */
125	    curp = base;
126	    hdr = (uint32_t *)curp;
127	    next = sizeof(uint32_t) * 2 + hdr[1];
128	    next = roundup(next, sizeof(u_long));
129	    curp += next;
130	} else
131	    curp = preload_metadata;
132
133	for (;;) {
134	    hdr = (uint32_t *)curp;
135	    if (hdr[0] == 0 && hdr[1] == 0)
136		break;
137
138	    /* Found a new record? */
139	    if (hdr[0] == MODINFO_NAME)
140		return curp;
141
142	    /* skip to next field */
143	    next = sizeof(uint32_t) * 2 + hdr[1];
144	    next = roundup(next, sizeof(u_long));
145	    curp += next;
146	}
147    }
148    return(NULL);
149}
150
151/*
152 * Given a preloaded module handle (mod), return a pointer
153 * to the data for the attribute (inf).
154 */
155caddr_t
156preload_search_info(caddr_t mod, int inf)
157{
158    caddr_t	curp;
159    uint32_t	*hdr;
160    uint32_t	type = 0;
161    int		next;
162
163    curp = mod;
164    for (;;) {
165	hdr = (uint32_t *)curp;
166	/* end of module data? */
167	if (hdr[0] == 0 && hdr[1] == 0)
168	    break;
169	/*
170	 * We give up once we've looped back to what we were looking at
171	 * first - this should normally be a MODINFO_NAME field.
172	 */
173	if (type == 0) {
174	    type = hdr[0];
175	} else {
176	    if (hdr[0] == type)
177		break;
178	}
179
180	/*
181	 * Attribute match? Return pointer to data.
182	 * Consumer may safely assume that size value precedes
183	 * data.
184	 */
185	if (hdr[0] == inf)
186	    return(curp + (sizeof(uint32_t) * 2));
187
188	/* skip to next field */
189	next = sizeof(uint32_t) * 2 + hdr[1];
190	next = roundup(next, sizeof(u_long));
191	curp += next;
192    }
193    return(NULL);
194}
195
196/*
197 * Delete a preload record by name.
198 */
199void
200preload_delete_name(const char *name)
201{
202    caddr_t	curp;
203    uint32_t	*hdr;
204    int		next;
205    int		clearing;
206
207    if (preload_metadata != NULL) {
208
209	clearing = 0;
210	curp = preload_metadata;
211	for (;;) {
212	    hdr = (uint32_t *)curp;
213	    if (hdr[0] == 0 && hdr[1] == 0)
214		break;
215
216	    /* Search for a MODINFO_NAME field */
217	    if (hdr[0] == MODINFO_NAME) {
218		if (!strcmp(name, curp + sizeof(uint32_t) * 2))
219		    clearing = 1;	/* got it, start clearing */
220		else if (clearing)
221		    clearing = 0;	/* at next one now.. better stop */
222	    }
223	    if (clearing)
224		hdr[0] = MODINFO_EMPTY;
225
226	    /* skip to next field */
227	    next = sizeof(uint32_t) * 2 + hdr[1];
228	    next = roundup(next, sizeof(u_long));
229	    curp += next;
230	}
231    }
232}
233
234void *
235preload_fetch_addr(caddr_t mod)
236{
237	caddr_t *mdp;
238
239	mdp = (caddr_t *)preload_search_info(mod, MODINFO_ADDR);
240	if (mdp == NULL)
241		return (NULL);
242	return (*mdp + preload_addr_relocate);
243}
244
245size_t
246preload_fetch_size(caddr_t mod)
247{
248	size_t *mdp;
249
250	mdp = (size_t *)preload_search_info(mod, MODINFO_SIZE);
251	if (mdp == NULL)
252		return (0);
253	return (*mdp);
254}
255
256/* Called from locore on i386.  Convert physical pointers to kvm. Sigh. */
257void
258preload_bootstrap_relocate(vm_offset_t offset)
259{
260    caddr_t	curp;
261    uint32_t	*hdr;
262    vm_offset_t	*ptr;
263    int		next;
264
265    if (preload_metadata != NULL) {
266
267	curp = preload_metadata;
268	for (;;) {
269	    hdr = (uint32_t *)curp;
270	    if (hdr[0] == 0 && hdr[1] == 0)
271		break;
272
273	    /* Deal with the ones that we know we have to fix */
274	    switch (hdr[0]) {
275	    case MODINFO_ADDR:
276	    case MODINFO_METADATA|MODINFOMD_SSYM:
277	    case MODINFO_METADATA|MODINFOMD_ESYM:
278		ptr = (vm_offset_t *)(curp + (sizeof(uint32_t) * 2));
279		*ptr += offset;
280		break;
281	    }
282	    /* The rest is beyond us for now */
283
284	    /* skip to next field */
285	    next = sizeof(uint32_t) * 2 + hdr[1];
286	    next = roundup(next, sizeof(u_long));
287	    curp += next;
288	}
289    }
290}
291