diff options
Diffstat (limited to 'usr/src/uts/i86pc/dboot/dboot_multiboot2.c')
-rw-r--r-- | usr/src/uts/i86pc/dboot/dboot_multiboot2.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/usr/src/uts/i86pc/dboot/dboot_multiboot2.c b/usr/src/uts/i86pc/dboot/dboot_multiboot2.c new file mode 100644 index 0000000000..4e01b0a222 --- /dev/null +++ b/usr/src/uts/i86pc/dboot/dboot_multiboot2.c @@ -0,0 +1,352 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Toomas Soome <tsoome@me.com> + */ + +/* + * dboot module utility functions for multiboot 2 tags processing. + */ + +#include <sys/inttypes.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sysmacros.h> +#include <sys/multiboot2.h> +#include <sys/multiboot2_impl.h> + +/* + * Remove offsetof definition when we have usable sys/stddef.h + */ +#if !defined(offsetof) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define offsetof(s, m) __builtin_offsetof(s, m) +#else +#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) +#endif +#endif /* !offsetof */ + +struct dboot_multiboot2_iterate_ctx; + +typedef boolean_t (*dboot_multiboot2_iterate_cb_t) + (int, multiboot_tag_t *, struct dboot_multiboot2_iterate_ctx *); + +struct dboot_multiboot2_iterate_ctx { + dboot_multiboot2_iterate_cb_t dboot_iter_callback; + int dboot_iter_index; /* item from set */ + uint32_t dboot_iter_tag; /* tag to search */ + multiboot_tag_t *dboot_iter_tagp; /* search result */ +}; + +/* + * Multiboot2 tag list elements are aligned to MULTIBOOT_TAG_ALIGN. + * To get the next item from the list, we first add the tag's size + * to the start of the current tag. Next, we round up that address to the + * nearest MULTIBOOT_TAG_ALIGN address. + */ + +static multiboot_tag_t * +dboot_multiboot2_first_tag(multiboot2_info_header_t *mbi) +{ + return (&mbi->mbi_tags[0]); +} + +static multiboot_tag_t * +dboot_multiboot2_next_tag(multiboot_tag_t *tag) +{ + if (tag == NULL || tag->mb_type == MULTIBOOT_TAG_TYPE_END) + return (NULL); + + return ((multiboot_tag_t *)P2ROUNDUP((uintptr_t)tag + + tag->mb_size, MULTIBOOT_TAG_ALIGN)); +} + +/* + * Walk the tag list until we hit the first instance of a given tag or + * the end of the list. + * MB2_NEXT_TAG() will return NULL on end of list. + */ +static void * +dboot_multiboot2_find_tag_impl(multiboot_tag_t *tagp, uint32_t tag) +{ + while (tagp != NULL && tagp->mb_type != tag) { + tagp = dboot_multiboot2_next_tag(tagp); + } + return (tagp); +} + +/* + * Walk the entire list to find the first instance of the given tag. + */ +void * +dboot_multiboot2_find_tag(multiboot2_info_header_t *mbi, uint32_t tag) +{ + multiboot_tag_t *tagp = dboot_multiboot2_first_tag(mbi); + + return (dboot_multiboot2_find_tag_impl(tagp, tag)); +} + +/* + * dboot_multiboot2_iterate() + * + * While most tags in tag list are unique, the modules are specified + * one module per tag and therefore we need an mechanism to process + * tags in set. + * + * Arguments: + * mbi: multiboot info header + * data: callback context. + * + * Return value: + * Processed item count. + * Callback returning B_TRUE will terminate the iteration. + */ +static int +dboot_multiboot2_iterate(multiboot2_info_header_t *mbi, + struct dboot_multiboot2_iterate_ctx *ctx) +{ + dboot_multiboot2_iterate_cb_t callback = ctx->dboot_iter_callback; + multiboot_tag_t *tagp; + uint32_t tag = ctx->dboot_iter_tag; + int index = 0; + + tagp = dboot_multiboot2_find_tag(mbi, tag); + while (tagp != NULL) { + if (callback != NULL) { + if (callback(index, tagp, ctx) == B_TRUE) { + return (index + 1); + } + } + tagp = dboot_multiboot2_next_tag(tagp); + tagp = dboot_multiboot2_find_tag_impl(tagp, tag); + index++; + } + return (index); +} + +char * +dboot_multiboot2_cmdline(multiboot2_info_header_t *mbi) +{ + multiboot_tag_string_t *tag; + + tag = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_CMDLINE); + + if (tag != NULL) + return (&tag->mb_string[0]); + else + return (NULL); +} + +/* + * Simple callback to index item in set. + * Terminates iteration if the indexed item is found. + */ +static boolean_t +dboot_multiboot2_iterate_callback(int index, multiboot_tag_t *tagp, + struct dboot_multiboot2_iterate_ctx *ctx) +{ + if (index == ctx->dboot_iter_index) { + ctx->dboot_iter_tagp = tagp; + return (B_TRUE); + } + return (B_FALSE); +} + +int +dboot_multiboot2_modcount(multiboot2_info_header_t *mbi) +{ + struct dboot_multiboot2_iterate_ctx ctx = { + .dboot_iter_callback = NULL, + .dboot_iter_index = 0, + .dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE, + .dboot_iter_tagp = NULL + }; + + return (dboot_multiboot2_iterate(mbi, &ctx)); +} + +uint32_t +dboot_multiboot2_modstart(multiboot2_info_header_t *mbi, int index) +{ + multiboot_tag_module_t *tagp; + struct dboot_multiboot2_iterate_ctx ctx = { + .dboot_iter_callback = dboot_multiboot2_iterate_callback, + .dboot_iter_index = index, + .dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE, + .dboot_iter_tagp = NULL + }; + + if (dboot_multiboot2_iterate(mbi, &ctx) != 0) { + tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp; + + if (tagp != NULL) + return (tagp->mb_mod_start); + } + return (0); +} + +uint32_t +dboot_multiboot2_modend(multiboot2_info_header_t *mbi, int index) +{ + multiboot_tag_module_t *tagp; + struct dboot_multiboot2_iterate_ctx ctx = { + .dboot_iter_callback = dboot_multiboot2_iterate_callback, + .dboot_iter_index = index, + .dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE, + .dboot_iter_tagp = NULL + }; + + if (dboot_multiboot2_iterate(mbi, &ctx) != 0) { + tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp; + + if (tagp != NULL) + return (tagp->mb_mod_end); + } + return (0); +} + +char * +dboot_multiboot2_modcmdline(multiboot2_info_header_t *mbi, int index) +{ + multiboot_tag_module_t *tagp; + struct dboot_multiboot2_iterate_ctx ctx = { + .dboot_iter_callback = dboot_multiboot2_iterate_callback, + .dboot_iter_index = index, + .dboot_iter_tag = MULTIBOOT_TAG_TYPE_MODULE, + .dboot_iter_tagp = NULL + }; + + if (dboot_multiboot2_iterate(mbi, &ctx) != 0) { + tagp = (multiboot_tag_module_t *)ctx.dboot_iter_tagp; + + if (tagp != NULL) + return (&tagp->mb_cmdline[0]); + } + return (NULL); +} + +multiboot_tag_mmap_t * +dboot_multiboot2_get_mmap_tagp(multiboot2_info_header_t *mbi) +{ + return (dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_MMAP)); +} + +boolean_t +dboot_multiboot2_basicmeminfo(multiboot2_info_header_t *mbi, + uint32_t *lower, uint32_t *upper) +{ + multiboot_tag_basic_meminfo_t *mip; + + mip = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_BASIC_MEMINFO); + if (mip != NULL) { + *lower = mip->mb_mem_lower; + *upper = mip->mb_mem_upper; + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Return the type of mmap entry referenced by index. + */ +uint32_t +dboot_multiboot2_mmap_get_type(multiboot2_info_header_t *mbi, + multiboot_tag_mmap_t *mb2_mmap_tagp, int index) +{ + multiboot_mmap_entry_t *mapentp; + + if (mb2_mmap_tagp == NULL) + mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi); + + if (mb2_mmap_tagp == NULL) + return (0); + + if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index) + return (0); + + mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries + + index * mb2_mmap_tagp->mb_entry_size); + return (mapentp->mmap_type); +} + +/* + * Return the length of mmap entry referenced by index. + */ +uint64_t +dboot_multiboot2_mmap_get_length(multiboot2_info_header_t *mbi, + multiboot_tag_mmap_t *mb2_mmap_tagp, int index) +{ + multiboot_mmap_entry_t *mapentp; + + if (mb2_mmap_tagp == NULL) + mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi); + + if (mb2_mmap_tagp == NULL) + return (0); + + if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index) + return (0); + + mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries + + index * mb2_mmap_tagp->mb_entry_size); + return (mapentp->mmap_len); +} + +/* + * Return the address from mmap entry referenced by index. + */ +uint64_t +dboot_multiboot2_mmap_get_base(multiboot2_info_header_t *mbi, + multiboot_tag_mmap_t *mb2_mmap_tagp, int index) +{ + multiboot_mmap_entry_t *mapentp; + + if (mb2_mmap_tagp == NULL) + mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi); + + if (mb2_mmap_tagp == NULL) + return (0); + + if (dboot_multiboot2_mmap_nentries(mbi, mb2_mmap_tagp) < index) + return (0); + + mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries + + index * mb2_mmap_tagp->mb_entry_size); + return (mapentp->mmap_addr); +} + +/* + * Count and return the number of mmap entries provided by the tag. + */ +int +dboot_multiboot2_mmap_nentries(multiboot2_info_header_t *mbi, + multiboot_tag_mmap_t *mb2_mmap_tagp) +{ + if (mb2_mmap_tagp == NULL) + mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi); + + if (mb2_mmap_tagp != NULL) { + return ((mb2_mmap_tagp->mb_size - + offsetof(multiboot_tag_mmap_t, mb_entries)) / + mb2_mmap_tagp->mb_entry_size); + } + return (0); +} + +/* + * Return the highest address used by info header. + */ +paddr_t +dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi) +{ + return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size); +} |