1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * dboot module utility functions for multiboot 2 tags processing.
18  */
19 
20 #include <sys/inttypes.h>
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/sysmacros.h>
24 #include <sys/multiboot2.h>
25 #include <sys/multiboot2_impl.h>
26 
27 struct dboot_multiboot2_iterate_ctx;
28 
29 typedef boolean_t (*dboot_multiboot2_iterate_cb_t)
30 	(int, multiboot_tag_t *, struct dboot_multiboot2_iterate_ctx *);
31 
32 struct dboot_multiboot2_iterate_ctx {
33 	dboot_multiboot2_iterate_cb_t dboot_iter_callback;
34 	int dboot_iter_index;			/* item from set */
35 	uint32_t dboot_iter_tag;		/* tag to search */
36 	multiboot_tag_t *dboot_iter_tagp;	/* search result */
37 };
38 
39 /*
40  * Multiboot2 tag list elements are aligned to MULTIBOOT_TAG_ALIGN.
41  * To get the next item from the list, we first add the tag's size
42  * to the start of the current tag. Next, we round up that address to the
43  * nearest MULTIBOOT_TAG_ALIGN address.
44  */
45 
46 static multiboot_tag_t *
dboot_multiboot2_first_tag(multiboot2_info_header_t * mbi)47 dboot_multiboot2_first_tag(multiboot2_info_header_t *mbi)
48 {
49 	return (&mbi->mbi_tags[0]);
50 }
51 
52 static multiboot_tag_t *
dboot_multiboot2_next_tag(multiboot_tag_t * tag)53 dboot_multiboot2_next_tag(multiboot_tag_t *tag)
54 {
55 	if (tag == NULL || tag->mb_type == MULTIBOOT_TAG_TYPE_END)
56 		return (NULL);
57 
58 	return ((multiboot_tag_t *)P2ROUNDUP((uintptr_t)tag +
59 	    tag->mb_size, MULTIBOOT_TAG_ALIGN));
60 }
61 
62 /*
63  * Walk the tag list until we hit the first instance of a given tag or
64  * the end of the list.
65  * MB2_NEXT_TAG() will return NULL on end of list.
66  */
67 static void *
dboot_multiboot2_find_tag_impl(multiboot_tag_t * tagp,uint32_t tag)68 dboot_multiboot2_find_tag_impl(multiboot_tag_t *tagp, uint32_t tag)
69 {
70 	while (tagp != NULL && tagp->mb_type != tag) {
71 		tagp = dboot_multiboot2_next_tag(tagp);
72 	}
73 	return (tagp);
74 }
75 
76 /*
77  * Walk the entire list to find the first instance of the given tag.
78  */
79 void *
dboot_multiboot2_find_tag(multiboot2_info_header_t * mbi,uint32_t tag)80 dboot_multiboot2_find_tag(multiboot2_info_header_t *mbi, uint32_t tag)
81 {
82 	multiboot_tag_t *tagp = dboot_multiboot2_first_tag(mbi);
83 
84 	return (dboot_multiboot2_find_tag_impl(tagp, tag));
85 }
86 
87 /*
88  * dboot_multiboot2_iterate()
89  *
90  * While most tags in tag list are unique, the modules are specified
91  * one module per tag and therefore we need an mechanism to process
92  * tags in set.
93  *
94  * Arguments:
95  *	mbi: multiboot info header
96  *	data: callback context.
97  *
98  * Return value:
99  *	Processed item count.
100  * Callback returning B_TRUE will terminate the iteration.
101  */
102 static int
dboot_multiboot2_iterate(multiboot2_info_header_t * mbi,struct dboot_multiboot2_iterate_ctx * ctx)103 dboot_multiboot2_iterate(multiboot2_info_header_t *mbi,
104     struct dboot_multiboot2_iterate_ctx *ctx)
105 {
106 	dboot_multiboot2_iterate_cb_t callback = ctx->dboot_iter_callback;
107 	multiboot_tag_t *tagp;
108 	uint32_t tag = ctx->dboot_iter_tag;
109 	int index = 0;
110 
111 	tagp = dboot_multiboot2_find_tag(mbi, tag);
112 	while (tagp != NULL) {
113 		if (callback != NULL) {
114 			if (callback(index, tagp, ctx) == B_TRUE) {
115 				return (index + 1);
116 			}
117 		}
118 		tagp = dboot_multiboot2_next_tag(tagp);
119 		tagp = dboot_multiboot2_find_tag_impl(tagp, tag);
120 		index++;
121 	}
122 	return (index);
123 }
124 
125 char *
dboot_multiboot2_cmdline(multiboot2_info_header_t * mbi)126 dboot_multiboot2_cmdline(multiboot2_info_header_t *mbi)
127 {
128 	multiboot_tag_string_t *tag;
129 
130 	tag = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_CMDLINE);
131 
132 	if (tag != NULL)
133 		return (&tag->mb_string[0]);
134 	else
135 		return (NULL);
136 }
137 
138 /*
139  * Simple callback to index item in set.
140  * Terminates iteration if the indexed item is found.
141  */
142 static boolean_t
dboot_multiboot2_iterate_callback(int index,multiboot_tag_t * tagp,struct dboot_multiboot2_iterate_ctx * ctx)143 dboot_multiboot2_iterate_callback(int index, multiboot_tag_t *tagp,
144     struct dboot_multiboot2_iterate_ctx *ctx)
145 {
146 	if (index == ctx->dboot_iter_index) {
147 		ctx->dboot_iter_tagp = tagp;
148 		return (B_TRUE);
149 	}
150 	return (B_FALSE);
151 }
152 
153 int
dboot_multiboot2_modcount(multiboot2_info_header_t * mbi)154 dboot_multiboot2_modcount(multiboot2_info_header_t *mbi)
155 {
156 	struct dboot_multiboot2_iterate_ctx ctx = {
157 		.dboot_iter_callback = NULL,
158 		.dboot_iter_index = 0,
159 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
160 		.dboot_iter_tagp = NULL
161 	};
162 
163 	return (dboot_multiboot2_iterate(mbi, &ctx));
164 }
165 
166 uint32_t
dboot_multiboot2_modstart(multiboot2_info_header_t * mbi,int index)167 dboot_multiboot2_modstart(multiboot2_info_header_t *mbi, int index)
168 {
169 	multiboot_tag_module_t *tagp;
170 	struct dboot_multiboot2_iterate_ctx ctx = {
171 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
172 		.dboot_iter_index = index,
173 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
174 		.dboot_iter_tagp = NULL
175 	};
176 
177 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
178 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
179 
180 		if (tagp != NULL)
181 			return (tagp->mb_mod_start);
182 	}
183 	return (0);
184 }
185 
186 uint32_t
dboot_multiboot2_modend(multiboot2_info_header_t * mbi,int index)187 dboot_multiboot2_modend(multiboot2_info_header_t *mbi, int index)
188 {
189 	multiboot_tag_module_t *tagp;
190 	struct dboot_multiboot2_iterate_ctx ctx = {
191 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
192 		.dboot_iter_index = index,
193 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
194 		.dboot_iter_tagp = NULL
195 	};
196 
197 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
198 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
199 
200 		if (tagp != NULL)
201 			return (tagp->mb_mod_end);
202 	}
203 	return (0);
204 }
205 
206 char *
dboot_multiboot2_modcmdline(multiboot2_info_header_t * mbi,int index)207 dboot_multiboot2_modcmdline(multiboot2_info_header_t *mbi, int index)
208 {
209 	multiboot_tag_module_t *tagp;
210 	struct dboot_multiboot2_iterate_ctx ctx = {
211 		.dboot_iter_callback = dboot_multiboot2_iterate_callback,
212 		.dboot_iter_index = index,
213 		.dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE,
214 		.dboot_iter_tagp = NULL
215 	};
216 
217 	if (dboot_multiboot2_iterate(mbi, &ctx) != 0) {
218 		tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp;
219 
220 		if (tagp != NULL)
221 			return (&tagp->mb_cmdline[0]);
222 	}
223 	return (NULL);
224 }
225 
226 multiboot_tag_mmap_t *
dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t * mbi)227 dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t *mbi)
228 {
229 	return (dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_MMAP));
230 }
231 
232 boolean_t
dboot_multiboot2_basicmeminfo(multiboot2_info_header_t * mbi,uint32_t * lower,uint32_t * upper)233 dboot_multiboot2_basicmeminfo(multiboot2_info_header_t *mbi,
234     uint32_t *lower, uint32_t *upper)
235 {
236 	multiboot_tag_basic_meminfo_t *mip;
237 
238 	mip = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_BASIC_MEMINFO);
239 	if (mip != NULL) {
240 		*lower = mip->mb_mem_lower;
241 		*upper = mip->mb_mem_upper;
242 		return (B_TRUE);
243 	}
244 	return (B_FALSE);
245 }
246 
247 /*
248  * Return the type of mmap entry referenced by index.
249  */
250 uint32_t
dboot_multiboot2_mmap_get_type(multiboot2_info_header_t * mbi,multiboot_tag_mmap_t * mb2_mmap_tagp,int index)251 dboot_multiboot2_mmap_get_type(multiboot2_info_header_t *mbi,
252     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
253 {
254 	multiboot_mmap_entry_t *mapentp;
255 
256 	if (mb2_mmap_tagp == NULL)
257 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
258 
259 	if (mb2_mmap_tagp == NULL)
260 		return (0);
261 
262 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
263 		return (0);
264 
265 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
266 	    index * mb2_mmap_tagp->mb_entry_size);
267 	return (mapentp->mmap_type);
268 }
269 
270 /*
271  * Return the length of mmap entry referenced by index.
272  */
273 uint64_t
dboot_multiboot2_mmap_get_length(multiboot2_info_header_t * mbi,multiboot_tag_mmap_t * mb2_mmap_tagp,int index)274 dboot_multiboot2_mmap_get_length(multiboot2_info_header_t *mbi,
275     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
276 {
277 	multiboot_mmap_entry_t *mapentp;
278 
279 	if (mb2_mmap_tagp == NULL)
280 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
281 
282 	if (mb2_mmap_tagp == NULL)
283 		return (0);
284 
285 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
286 		return (0);
287 
288 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
289 	    index * mb2_mmap_tagp->mb_entry_size);
290 	return (mapentp->mmap_len);
291 }
292 
293 /*
294  * Return the address from mmap entry referenced by index.
295  */
296 uint64_t
dboot_multiboot2_mmap_get_base(multiboot2_info_header_t * mbi,multiboot_tag_mmap_t * mb2_mmap_tagp,int index)297 dboot_multiboot2_mmap_get_base(multiboot2_info_header_t *mbi,
298     multiboot_tag_mmap_t *mb2_mmap_tagp, int index)
299 {
300 	multiboot_mmap_entry_t *mapentp;
301 
302 	if (mb2_mmap_tagp == NULL)
303 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
304 
305 	if (mb2_mmap_tagp == NULL)
306 		return (0);
307 
308 	if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index)
309 		return (0);
310 
311 	mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
312 	    index * mb2_mmap_tagp->mb_entry_size);
313 	return (mapentp->mmap_addr);
314 }
315 
316 /*
317  * Count and return the number of mmap entries provided by the tag.
318  */
319 int
dboot_multiboot2_mmap_nentries(multiboot2_info_header_t * mbi,multiboot_tag_mmap_t * mb2_mmap_tagp)320 dboot_multiboot2_mmap_nentries(multiboot2_info_header_t *mbi,
321     multiboot_tag_mmap_t *mb2_mmap_tagp)
322 {
323 	if (mb2_mmap_tagp == NULL)
324 		mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
325 
326 	if (mb2_mmap_tagp != NULL) {
327 		return ((mb2_mmap_tagp->mb_size -
328 		    offsetof(multiboot_tag_mmap_t, mb_entries)) /
329 		    mb2_mmap_tagp->mb_entry_size);
330 	}
331 	return (0);
332 }
333 
334 /*
335  * Return the highest address used by info header.
336  */
337 paddr_t
dboot_multiboot2_highest_addr(multiboot2_info_header_t * mbi)338 dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi)
339 {
340 	return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size);
341 }
342