#include <sys/inttypes.h>
#include <sys/param.h>
#include <sys/machparam.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/multiboot2.h>
#include <sys/multiboot2_impl.h>
#include <sys/efi.h>
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;
uint32_t dboot_iter_tag;
multiboot_tag_t *dboot_iter_tagp;
};
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));
}
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);
}
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));
}
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);
}
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));
}
multiboot_tag_efi_mmap_t *
dboot_multiboot2_get_efi_mmap_tagp(multiboot2_info_header_t *mbi)
{
multiboot_tag_efi_mmap_t *tagp;
tagp = dboot_multiboot2_find_tag(mbi, MULTIBOOT_TAG_TYPE_EFI_MMAP);
if (tagp != NULL &&
tagp->mb_descr_vers != EFI_MEMORY_DESCRIPTOR_VERSION)
tagp = NULL;
return (tagp);
}
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);
}
boolean_t
dboot_multiboot2_mmap_get_type(multiboot2_info_header_t *mbi, int index,
uint32_t *typep)
{
multiboot_tag_mmap_t *mb2_mmap_tagp;
multiboot_mmap_entry_t *mapentp;
if (dboot_multiboot2_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
if (mb2_mmap_tagp == NULL)
return (B_FALSE);
if (dboot_multiboot2_mmap_nentries(mbi) < index)
return (B_FALSE);
mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
index * mb2_mmap_tagp->mb_entry_size);
*typep = mapentp->mmap_type;
return (B_TRUE);
}
boolean_t
dboot_multiboot2_mmap_get_length(multiboot2_info_header_t *mbi, int index,
uint64_t *lengthp)
{
multiboot_tag_mmap_t *mb2_mmap_tagp;
multiboot_mmap_entry_t *mapentp;
if (dboot_multiboot2_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
if (mb2_mmap_tagp == NULL)
return (B_FALSE);
mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
index * mb2_mmap_tagp->mb_entry_size);
*lengthp = mapentp->mmap_len;
return (B_TRUE);
}
boolean_t
dboot_multiboot2_mmap_get_base(multiboot2_info_header_t *mbi, int index,
uint64_t *basep)
{
multiboot_tag_mmap_t *mb2_mmap_tagp;
multiboot_mmap_entry_t *mapentp;
if (dboot_multiboot2_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_mmap_tagp = dboot_multiboot2_get_mmap_tagp(mbi);
if (mb2_mmap_tagp == NULL)
return (B_FALSE);
mapentp = (multiboot_mmap_entry_t *)(mb2_mmap_tagp->mb_entries +
index * mb2_mmap_tagp->mb_entry_size);
*basep = mapentp->mmap_addr;
return (B_TRUE);
}
int
dboot_multiboot2_mmap_nentries(multiboot2_info_header_t *mbi)
{
multiboot_tag_mmap_t *mb2_mmap_tagp;
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);
}
boolean_t
dboot_multiboot2_efi_mmap_get_type(multiboot2_info_header_t *mbi, int index,
uint32_t *typep)
{
multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
EFI_MEMORY_DESCRIPTOR *mapentp;
if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
if (mb2_efi_mmap_tagp == NULL)
return (B_FALSE);
mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
index * mb2_efi_mmap_tagp->mb_descr_size);
*typep = mapentp->Type;
return (B_TRUE);
}
boolean_t
dboot_multiboot2_efi_mmap_get_length(multiboot2_info_header_t *mbi, int index,
uint64_t *lengthp)
{
multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
EFI_MEMORY_DESCRIPTOR *mapentp;
if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
if (mb2_efi_mmap_tagp == NULL)
return (B_FALSE);
mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
index * mb2_efi_mmap_tagp->mb_descr_size);
*lengthp = mapentp->NumberOfPages << PAGESHIFT;
return (B_TRUE);
}
boolean_t
dboot_multiboot2_efi_mmap_get_base(multiboot2_info_header_t *mbi, int index,
uint64_t *basep)
{
multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
EFI_MEMORY_DESCRIPTOR *mapentp;
if (dboot_multiboot2_efi_mmap_nentries(mbi) < index)
return (B_FALSE);
mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
if (mb2_efi_mmap_tagp == NULL)
return (B_FALSE);
mapentp = (EFI_MEMORY_DESCRIPTOR *)(mb2_efi_mmap_tagp->mb_efi_mmap +
index * mb2_efi_mmap_tagp->mb_descr_size);
*basep = mapentp->PhysicalStart;
return (B_TRUE);
}
int
dboot_multiboot2_efi_mmap_nentries(multiboot2_info_header_t *mbi)
{
multiboot_tag_efi_mmap_t *mb2_efi_mmap_tagp;
mb2_efi_mmap_tagp = dboot_multiboot2_get_efi_mmap_tagp(mbi);
if (mb2_efi_mmap_tagp != NULL) {
return ((mb2_efi_mmap_tagp->mb_size -
offsetof(multiboot_tag_efi_mmap_t, mb_efi_mmap)) /
mb2_efi_mmap_tagp->mb_descr_size);
}
return (0);
}
paddr_t
dboot_multiboot2_highest_addr(multiboot2_info_header_t *mbi)
{
return ((paddr_t)(uintptr_t)mbi + mbi->mbi_total_size);
}