summaryrefslogtreecommitdiff
path: root/usr/src/boot
diff options
context:
space:
mode:
authorToomas Soome <tsoome@me.com>2016-10-09 17:30:28 +0300
committerRichard Lowe <richlowe@richlowe.net>2017-08-30 14:18:41 -0400
commitf9feecc12f00b97b49d542398fb3b5a452da0bbc (patch)
tree8af199520a67a45cc90e9161e09f52aa7e7e2c71 /usr/src/boot
parenta058d1cc571af5fbcfe7f1d719df1abbfdb722f3 (diff)
downloadillumos-joyent-f9feecc12f00b97b49d542398fb3b5a452da0bbc.tar.gz
8139 loader: efi multiboot2 update
Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/boot')
-rw-r--r--usr/src/boot/lib/libstand/bootp.c13
-rw-r--r--usr/src/boot/lib/libstand/bootp.h5
-rw-r--r--usr/src/boot/sys/boot/common/bootstrap.h14
-rw-r--r--usr/src/boot/sys/boot/common/multiboot2.c310
-rw-r--r--usr/src/boot/sys/boot/efi/boot1/Makefile2
-rw-r--r--usr/src/boot/sys/boot/efi/loader/Makefile7
-rw-r--r--usr/src/boot/sys/boot/efi/loader/arch/amd64/Makefile.inc6
-rw-r--r--usr/src/boot/sys/boot/efi/loader/arch/amd64/multiboot_tramp.S119
-rw-r--r--usr/src/boot/sys/boot/efi/loader/conf.c11
-rw-r--r--usr/src/boot/sys/boot/efi/loader/copy.c193
-rw-r--r--usr/src/boot/sys/boot/efi/loader/loader_efi.h53
-rw-r--r--usr/src/boot/sys/boot/efi/loader/main.c107
-rw-r--r--usr/src/boot/sys/boot/efi/loader/memmap.c180
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/libi386.h6
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/multiboot.c22
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/pxe.c2
16 files changed, 924 insertions, 126 deletions
diff --git a/usr/src/boot/lib/libstand/bootp.c b/usr/src/boot/lib/libstand/bootp.c
index 98ecb2750b..117229753c 100644
--- a/usr/src/boot/lib/libstand/bootp.c
+++ b/usr/src/boot/lib/libstand/bootp.c
@@ -88,6 +88,7 @@ static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts);
static char expected_dhcpmsgtype = -1, dhcp_ok;
struct in_addr dhcp_serverip;
#endif
+struct bootp *bootp_response;
/* Fetch required bootp infomation */
void
@@ -320,6 +321,18 @@ bootprecv(struct iodesc *d, void *pkt, size_t len, time_t tleft)
if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0)
goto bad;
+
+ /* Save copy of bootp reply or DHCP ACK message */
+ if (bp->bp_op == BOOTREPLY &&
+ ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) ||
+ dhcp_ok == 0)) {
+ free(bootp_response);
+ bootp_response = malloc(sizeof (*bootp_response));
+ if (bootp_response != NULL) {
+ bcopy(bp, bootp_response,
+ sizeof (*bootp_response));
+ }
+ }
}
#ifdef BOOTP_VEND_CMU
else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
diff --git a/usr/src/boot/lib/libstand/bootp.h b/usr/src/boot/lib/libstand/bootp.h
index 3c5c333180..72c781994b 100644
--- a/usr/src/boot/lib/libstand/bootp.h
+++ b/usr/src/boot/lib/libstand/bootp.h
@@ -20,6 +20,8 @@
* without express or implied warranty.
*/
+#include <netinet/in.h>
+
#ifndef _BOOTP_H_
#define _BOOTP_H_
@@ -145,6 +147,9 @@ struct cmu_vend {
/* v_flags values */
#define VF_SMASK 1 /* Subnet mask field contains valid data */
+/* cached bootp response/dhcp ack */
+extern struct bootp *bootp_response;
+
int dhcp_try_rfc1048(uint8_t *cp, size_t len);
#endif /* _BOOTP_H_ */
diff --git a/usr/src/boot/sys/boot/common/bootstrap.h b/usr/src/boot/sys/boot/common/bootstrap.h
index 8b0d9c50f0..83495af92c 100644
--- a/usr/src/boot/sys/boot/common/bootstrap.h
+++ b/usr/src/boot/sys/boot/common/bootstrap.h
@@ -245,15 +245,17 @@ extern u_int64_t __elfN(relocation_offset);
struct elf_file;
typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx);
-int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result);
-int __elfN(obj_loadfile)(char *filename, u_int64_t dest,
- struct preloaded_file **result);
+int elf64_loadfile(char *, uint64_t, struct preloaded_file **);
+int elf32_loadfile(char *, uint64_t, struct preloaded_file **);
+int elf64_obj_loadfile(char *, uint64_t, struct preloaded_file **);
+int elf32_obj_loadfile(char *, uint64_t, struct preloaded_file **);
int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr,
const void *reldata, int reltype, Elf_Addr relbase,
Elf_Addr dataaddr, void *data, size_t len);
-int __elfN(loadfile_raw)(char *filename, u_int64_t dest,
- struct preloaded_file **result, int multiboot);
-int __elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest);
+int elf64_loadfile_raw(char *, uint64_t, struct preloaded_file **, int);
+int elf32_loadfile_raw(char *, uint64_t, struct preloaded_file **, int);
+int elf64_load_modmetadata(struct preloaded_file *, uint64_t);
+int elf32_load_modmetadata(struct preloaded_file *, uint64_t);
#endif
/*
diff --git a/usr/src/boot/sys/boot/common/multiboot2.c b/usr/src/boot/sys/boot/common/multiboot2.c
index 74f6e9c4cd..1808d09c89 100644
--- a/usr/src/boot/sys/boot/common/multiboot2.c
+++ b/usr/src/boot/sys/boot/common/multiboot2.c
@@ -18,6 +18,7 @@
* kernel. This code is only built to support the illumos kernel, it does
* not support xen.
*/
+
#include <sys/cdefs.h>
#include <sys/stddef.h>
@@ -29,6 +30,7 @@
#include <sys/multiboot2.h>
#include <stand.h>
#include <stdbool.h>
+#include <machine/elf.h>
#include "libzfs.h"
#include "bootstrap.h"
@@ -36,12 +38,20 @@
#include <machine/metadata.h>
#include <machine/pc/bios.h>
+#define SUPPORT_DHCP
+#include <bootp.h>
+
+#if !defined(EFI)
#include "../i386/libi386/libi386.h"
#include "../i386/btx/lib/btxv86.h"
-#include "pxe.h"
-extern BOOTPLAYER bootplayer; /* dhcp info */
-extern void multiboot_tramp();
+#else
+#include <efi.h>
+#include <efilib.h>
+#include "loader_efi.h"
+
+static void (*trampoline)(uint32_t, struct relocator *, uint64_t);
+#endif
#include "platform/acfreebsd.h"
#include "acconfig.h"
@@ -55,8 +65,6 @@ extern ACPI_TABLE_RSDP *rsdp;
static vm_offset_t last_addr;
extern char bootprog_info[];
-extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
- struct preloaded_file **result, int multiboot);
static int multiboot2_loadfile(char *, u_int64_t, struct preloaded_file **);
static int multiboot2_exec(struct preloaded_file *);
@@ -276,6 +284,12 @@ multiboot2_loadfile(char *filename, u_int64_t dest,
fp->f_metadata = NULL;
error = 0;
} else {
+#if defined(EFI)
+ /* 32-bit kernel is not yet supported for EFI */
+ printf("32-bit kernel is not supported by UEFI loader\n");
+ error = ENOTSUP;
+ goto out;
+#endif
/* elf32_loadfile_raw will fill the attributes in fp. */
error = elf32_loadfile_raw(filename, dest, &fp, 2);
if (error != 0) {
@@ -295,7 +309,11 @@ multiboot2_loadfile(char *filename, u_int64_t dest,
}
setenv("kernelname", fp->f_name, 1);
+#if defined(EFI)
+ efi_addsmapdata(fp);
+#else
bios_addsmapdata(fp);
+#endif
*result = fp;
out:
free(header_search);
@@ -656,6 +674,42 @@ module_size(struct preloaded_file *fp)
return (size);
}
+#if defined (EFI)
+/*
+ * Calculate size for UEFI memory map tag.
+ */
+static int
+efimemmap_size(void)
+{
+ UINTN size, cur_size, desc_size;
+ EFI_MEMORY_DESCRIPTOR *mmap;
+ EFI_STATUS ret;
+
+ size = EFI_PAGE_SIZE; /* Start with 4k. */
+ while (1) {
+ cur_size = size;
+ mmap = malloc(cur_size);
+ if (mmap == NULL)
+ return (0);
+ ret = BS->GetMemoryMap(&cur_size, mmap, NULL, &desc_size, NULL);
+ free(mmap);
+ if (ret == EFI_SUCCESS)
+ break;
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ if (size < cur_size)
+ size = cur_size;
+ size += (EFI_PAGE_SIZE);
+ } else
+ return (0);
+ }
+
+ /* EFI MMAP will grow when we allocate MBI, set some buffer. */
+ size += (3 << EFI_PAGE_SHIFT);
+ size = roundup(size, desc_size);
+ return (sizeof (multiboot_tag_efi_mmap_t) + size);
+}
+#endif
+
/*
* Calculate size for bios smap tag.
*/
@@ -684,15 +738,29 @@ mbi_size(struct preloaded_file *fp, char *cmdline)
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
size += sizeof (multiboot_tag_string_t) + strlen(bootprog_info) + 1;
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+#if !defined (EFI)
size += sizeof (multiboot_tag_basic_meminfo_t);
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+#endif
size += module_size(fp);
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+#if defined (EFI)
+ size += sizeof (multiboot_tag_efi64_t);
+ size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+ size += efimemmap_size();
+ size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+
+ if (have_framebuffer == true) {
+ size += sizeof (multiboot_tag_framebuffer_t);
+ size = roundup2(size, MULTIBOOT_TAG_ALIGN);
+ }
+#endif
size += biossmap_size(fp);
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
- if (strstr(getenv("loaddev"), "pxe") != NULL) {
- size += sizeof(multiboot_tag_network_t) + sizeof (BOOTPLAYER);
+ if (bootp_response != NULL) {
+ size += sizeof(multiboot_tag_network_t) +
+ sizeof (*bootp_response);
size = roundup2(size, MULTIBOOT_TAG_ALIGN);
}
@@ -724,7 +792,17 @@ multiboot2_exec(struct preloaded_file *fp)
size_t size;
struct bios_smap *smap;
vm_offset_t tmp;
+#if defined (EFI)
+ multiboot_tag_module_t *module;
+ EFI_MEMORY_DESCRIPTOR *map;
+ struct relocator *relocator;
+ struct chunk_head *head;
+ struct chunk *chunk;
+
+ efi_getdev((void **)(&rootdev), NULL, NULL);
+#else
i386_getdev((void **)(&rootdev), NULL, NULL);
+#endif
error = EINVAL;
if (rootdev == NULL) {
@@ -756,8 +834,28 @@ multiboot2_exec(struct preloaded_file *fp)
size = mbi_size(fp, cmdline); /* Get the size for MBI. */
/* Set up the base for mb_malloc. */
- for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next);
+ i = 0;
+ for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next)
+ i++;
+
+#if defined (EFI)
+ /* We need space for kernel + MBI + # modules */
+ num = (EFI_PAGE_SIZE - offsetof(struct relocator, rel_chunklist)) /
+ sizeof (struct chunk);
+ if (i + 2 >= num) {
+ printf("Too many modules, do not have space for relocator.\n");
+ error = ENOMEM;
+ goto error;
+ }
+ last_addr = efi_loadaddr(LOAD_MEM, &size, mfp->f_addr + mfp->f_size);
+ mbi = (multiboot2_info_header_t *)last_addr;
+ if (mbi == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ last_addr = (vm_offset_t)mbi->mbi_tags;
+#else
/* Start info block from the new page. */
last_addr = roundup(mfp->f_addr + mfp->f_size, MULTIBOOT_MOD_ALIGN);
@@ -769,6 +867,7 @@ multiboot2_exec(struct preloaded_file *fp)
mbi = (multiboot2_info_header_t *)PTOV(last_addr);
last_addr = (vm_offset_t)mbi->mbi_tags;
+#endif /* EFI */
{
multiboot_tag_string_t *tag;
@@ -793,6 +892,8 @@ multiboot2_exec(struct preloaded_file *fp)
strlen(bootprog_info) + 1);
}
+#if !defined (EFI)
+ /* Only set in case of BIOS. */
{
multiboot_tag_basic_meminfo_t *tag;
tag = (multiboot_tag_basic_meminfo_t *)
@@ -803,6 +904,7 @@ multiboot2_exec(struct preloaded_file *fp)
tag->mb_mem_lower = bios_basemem / 1024;
tag->mb_mem_upper = bios_extmem / 1024;
}
+#endif
num = 0;
for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
@@ -824,8 +926,12 @@ multiboot2_exec(struct preloaded_file *fp)
* - Modules are aligned to page boundary.
* - MBI is aligned to page boundary.
* - Set the tmp to point to physical address of the first module.
+ * - tmp != mfp->f_addr only in case of EFI.
*/
tmp = roundup2(load_addr + fp->f_size, MULTIBOOT_MOD_ALIGN);
+#if defined (EFI)
+ module = (multiboot_tag_module_t *)last_addr;
+#endif
for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
multiboot_tag_module_t *tag;
@@ -893,14 +999,15 @@ multiboot2_exec(struct preloaded_file *fp)
}
}
- if (strstr(getenv("loaddev"), "pxe") != NULL) {
+ if (bootp_response != NULL) {
multiboot_tag_network_t *tag;
tag = (multiboot_tag_network_t *)
- mb_malloc(sizeof(*tag) + sizeof (BOOTPLAYER));
+ mb_malloc(sizeof (*tag) + sizeof (*bootp_response));
tag->mb_type = MULTIBOOT_TAG_TYPE_NETWORK;
- tag->mb_size = sizeof(*tag) + sizeof (BOOTPLAYER);
- memcpy(tag->mb_dhcpack, &bootplayer, sizeof (BOOTPLAYER));
+ tag->mb_size = sizeof (*tag) + sizeof (*bootp_response);
+ memcpy(tag->mb_dhcpack, bootp_response,
+ sizeof (*bootp_response));
}
if (rsdp != NULL) {
@@ -923,6 +1030,132 @@ multiboot2_exec(struct preloaded_file *fp)
}
}
+#if defined (EFI)
+ {
+ multiboot_tag_efi64_t *tag;
+ tag = (multiboot_tag_efi64_t *)
+ mb_malloc(sizeof (*tag));
+
+ tag->mb_type = MULTIBOOT_TAG_TYPE_EFI64;
+ tag->mb_size = sizeof (*tag);
+ tag->mb_pointer = (uint64_t)(uintptr_t)ST;
+ }
+
+ if (have_framebuffer == true) {
+ multiboot_tag_framebuffer_t *tag;
+ int bpp;
+ struct efi_fb fb;
+ extern int efi_find_framebuffer(struct efi_fb *efifb);
+
+ if (efi_find_framebuffer(&fb) == 0) {
+ tag = (multiboot_tag_framebuffer_t *)
+ mb_malloc(sizeof (*tag));
+
+ /*
+ * We assume contiguous color bitmap, and use
+ * the msb for bits per pixel calculation.
+ */
+ bpp = fls(fb.fb_mask_red | fb.fb_mask_green |
+ fb.fb_mask_blue | fb.fb_mask_reserved);
+
+ tag->framebuffer_common.mb_type =
+ MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+ tag->framebuffer_common.mb_size =
+ sizeof (multiboot_tag_framebuffer_t);
+ tag->framebuffer_common.framebuffer_addr = fb.fb_addr;
+ tag->framebuffer_common.framebuffer_width = fb.fb_width;
+ tag->framebuffer_common.framebuffer_height =
+ fb.fb_height;
+ tag->framebuffer_common.framebuffer_bpp = bpp;
+ /*
+ * Pitch is stride * bytes per pixel.
+ * Stride is pixels per scanline.
+ */
+ tag->framebuffer_common.framebuffer_pitch =
+ fb.fb_stride * (bpp / 8);
+ tag->framebuffer_common.framebuffer_type =
+ MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+ tag->framebuffer_common.mb_reserved = 0;
+
+ /*
+ * The RGB or BGR color ordering.
+ */
+ if (fb.fb_mask_red & 0x000000ff) {
+ tag->u.fb2.framebuffer_red_field_position = 0;
+ tag->u.fb2.framebuffer_blue_field_position = 16;
+ } else {
+ tag->u.fb2.framebuffer_red_field_position = 16;
+ tag->u.fb2.framebuffer_blue_field_position = 0;
+ }
+ tag->u.fb2.framebuffer_red_mask_size = 8;
+ tag->u.fb2.framebuffer_green_field_position = 8;
+ tag->u.fb2.framebuffer_green_mask_size = 8;
+ tag->u.fb2.framebuffer_blue_mask_size = 8;
+ }
+ }
+
+ /* Leave EFI memmap last as we will also switch off the BS. */
+ {
+ multiboot_tag_efi_mmap_t *tag;
+ UINTN size, desc_size, key;
+ EFI_STATUS status;
+
+ tag = (multiboot_tag_efi_mmap_t *)
+ mb_malloc(sizeof (*tag));
+
+ size = 0;
+ status = BS->GetMemoryMap(&size,
+ (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key,
+ &desc_size, &tag->mb_descr_vers);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ error = EINVAL;
+ goto error;
+ }
+ status = BS->GetMemoryMap(&size,
+ (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap, &key,
+ &desc_size, &tag->mb_descr_vers);
+ if (EFI_ERROR(status)) {
+ error = EINVAL;
+ goto error;
+ }
+ tag->mb_type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
+ tag->mb_size = sizeof (*tag) + size;
+ tag->mb_descr_size = (uint32_t) desc_size;
+
+ /*
+ * Find relocater pages. We assume we have free pages
+ * below kernel load address.
+ * In this version we are using 5 pages:
+ * relocator data, trampoline, copy, memmove, stack.
+ */
+ for (i = 0, map = (EFI_MEMORY_DESCRIPTOR *)tag->mb_efi_mmap;
+ i < size / desc_size;
+ i++, map = NextMemoryDescriptor(map, desc_size)) {
+ if (map->PhysicalStart == 0)
+ continue;
+ if (map->Type != EfiConventionalMemory)
+ continue;
+ if (map->PhysicalStart < load_addr &&
+ map->NumberOfPages > 5)
+ break;
+ }
+ if (map->PhysicalStart == 0)
+ panic("Could not find memory for relocater\n");
+
+ if (keep_bs == 0) {
+ status = BS->ExitBootServices(IH, key);
+ if (EFI_ERROR(status)) {
+ printf("Call to ExitBootServices failed\n");
+ error = EINVAL;
+ goto error;
+ }
+ }
+
+ last_addr += size;
+ last_addr = roundup2(last_addr, MULTIBOOT_TAG_ALIGN);
+ }
+#endif
+
/*
* MB tag list end marker.
*/
@@ -936,13 +1169,66 @@ multiboot2_exec(struct preloaded_file *fp)
mbi->mbi_total_size = last_addr - (vm_offset_t)mbi;
mbi->mbi_reserved = 0;
+#if defined (EFI)
+ /* At this point we have load_addr pointing to kernel load
+ * address, module list in MBI having physical addresses,
+ * module list in fp having logical addresses and tmp pointing to
+ * physical address for MBI.
+ * Now we must move all pieces to place and start the kernel.
+ */
+ relocator = (struct relocator *)(uintptr_t)map->PhysicalStart;
+ head = &relocator->rel_chunk_head;
+ STAILQ_INIT(head);
+
+ i = 0;
+ chunk = &relocator->rel_chunklist[i++];
+ chunk->chunk_vaddr = fp->f_addr;
+ chunk->chunk_paddr = load_addr;
+ chunk->chunk_size = fp->f_size;
+
+ STAILQ_INSERT_TAIL(head, chunk, chunk_next);
+
+ for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) {
+ chunk = &relocator->rel_chunklist[i++];
+ chunk->chunk_vaddr = mfp->f_addr;
+ chunk->chunk_paddr = module->mb_mod_start;
+ chunk->chunk_size = mfp->f_size;
+ STAILQ_INSERT_TAIL(head, chunk, chunk_next);
+
+ module = (multiboot_tag_module_t *)
+ roundup2((uintptr_t)module + module->mb_size,
+ MULTIBOOT_TAG_ALIGN);
+ }
+ chunk = &relocator->rel_chunklist[i++];
+ chunk->chunk_vaddr = (EFI_VIRTUAL_ADDRESS)mbi;
+ chunk->chunk_paddr = tmp;
+ chunk->chunk_size = mbi->mbi_total_size;
+ STAILQ_INSERT_TAIL(head, chunk, chunk_next);
+
+ trampoline = (void *)(uintptr_t)relocator + EFI_PAGE_SIZE;
+ memmove(trampoline, multiboot_tramp, EFI_PAGE_SIZE);
+
+ relocator->rel_copy = (uintptr_t)trampoline + EFI_PAGE_SIZE;
+ memmove((void *)relocator->rel_copy, efi_copy_finish, EFI_PAGE_SIZE);
+
+ relocator->rel_memmove = (uintptr_t)relocator->rel_copy + EFI_PAGE_SIZE;
+ memmove((void *)relocator->rel_memmove, memmove, EFI_PAGE_SIZE);
+ relocator->rel_stack = relocator->rel_memmove + EFI_PAGE_SIZE - 8;
+
+ trampoline(MULTIBOOT2_BOOTLOADER_MAGIC, relocator, entry_addr);
+#else
dev_cleanup();
__exec((void *)VTOP(multiboot_tramp), MULTIBOOT2_BOOTLOADER_MAGIC,
(void *)entry_addr, (void *)VTOP(mbi));
+#endif
panic("exec returned");
error:
if (cmdline != NULL)
free(cmdline);
+#if defined (EFI)
+ if (mbi != NULL)
+ efi_free_loadaddr((uint64_t)mbi, EFI_SIZE_TO_PAGES(size));
+#endif
return (error);
}
diff --git a/usr/src/boot/sys/boot/efi/boot1/Makefile b/usr/src/boot/sys/boot/efi/boot1/Makefile
index 413e7115fc..ac32e257c4 100644
--- a/usr/src/boot/sys/boot/efi/boot1/Makefile
+++ b/usr/src/boot/sys/boot/efi/boot1/Makefile
@@ -40,7 +40,7 @@ CPPFLAGS += -I./../../../../include
CPPFLAGS += -I./../../../sys
CPPFLAGS += -I./../../..
CPPFLAGS += -I../../../../lib/libstand
-CPPFLAGS += -DEFI_UFS_BOOT
+CPPFLAGS += -DEFI_UFS_BOOT -DUFS1_ONLY
# CPPFLAGS += -DEFI_DEBUG
CPPFLAGS += -I./../../zfs/
diff --git a/usr/src/boot/sys/boot/efi/loader/Makefile b/usr/src/boot/sys/boot/efi/loader/Makefile
index 4823f7ccd8..4baf9a2413 100644
--- a/usr/src/boot/sys/boot/efi/loader/Makefile
+++ b/usr/src/boot/sys/boot/efi/loader/Makefile
@@ -27,9 +27,9 @@ MACHINE= $(MACH64)
# architecture-specific loader code
SRCS= autoload.c bootinfo.c conf.c copy.c devicename.c main.c self_reloc.c \
- smbios.c acpi.c vers.c
+ smbios.c acpi.c vers.c memmap.c multiboot2.c
OBJS= autoload.o bootinfo.o conf.o copy.o devicename.o main.o self_reloc.o \
- smbios.o acpi.o vers.o
+ smbios.o acpi.o vers.o memmap.o multiboot2.o
ASFLAGS=-m64 -fPIC
CFLAGS= -O2
@@ -83,6 +83,9 @@ LIBZFSBOOT= ../../zfs/${MACHINE}/libzfsboot.a
include ./Makefile.common
CPPFLAGS += -I../../common
+# For multiboot2.h, must be last, to avoid conflicts
+CPPFLAGS += -I$(SRC)/uts/common
+
FILES= loader.efi
FILEMODE= 0555
ROOT_BOOT= $(ROOT)/boot
diff --git a/usr/src/boot/sys/boot/efi/loader/arch/amd64/Makefile.inc b/usr/src/boot/sys/boot/efi/loader/arch/amd64/Makefile.inc
index 4f6be8a2e2..47dd322f91 100644
--- a/usr/src/boot/sys/boot/efi/loader/arch/amd64/Makefile.inc
+++ b/usr/src/boot/sys/boot/efi/loader/arch/amd64/Makefile.inc
@@ -1,15 +1,13 @@
-SRCS += amd64_tramp.S \
+SRCS += multiboot_tramp.S \
start.S \
framebuffer.c \
- elf64_freebsd.c \
trap.c \
exc.S
-OBJS += amd64_tramp.o \
+OBJS += multiboot_tramp.o \
start.o \
framebuffer.o \
- elf64_freebsd.o \
trap.o \
exc.o
diff --git a/usr/src/boot/sys/boot/efi/loader/arch/amd64/multiboot_tramp.S b/usr/src/boot/sys/boot/efi/loader/arch/amd64/multiboot_tramp.S
new file mode 100644
index 0000000000..dffa273563
--- /dev/null
+++ b/usr/src/boot/sys/boot/efi/loader/arch/amd64/multiboot_tramp.S
@@ -0,0 +1,119 @@
+/*
+ * 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>
+ */
+
+#include <x86/specialreg.h>
+
+ .file "multiboot_tramp.s"
+
+/*
+ * The current dboot in illumos kernel is running in 32bit mode
+ * and expecting following 32-bit multiboot execution environment:
+ *
+ * EAX: MB magic
+ * EBX: 32-bit physical address of MBI
+ * CS: 32-bit read/execute code segment with offset 0 and limit 0xFFFFFFFF
+ * DS: 32-bit read/write code segment with offset 0 and limit 0xFFFFFFFF
+ * ES: 32-bit read/write code segment with offset 0 and limit 0xFFFFFFFF
+ * FS: 32-bit read/write code segment with offset 0 and limit 0xFFFFFFFF
+ * GS: 32-bit read/write code segment with offset 0 and limit 0xFFFFFFFF
+ * SS: 32-bit read/write data segment with offset 0 and limit 0xFFFFFFFF
+ * A20 enabled
+ * CR0: PG cleared, PE set
+ * EFLAGS: VM cleared, IF cleared
+ * interrupts disabled
+ */
+
+ .set SEL_SCODE,0x8
+ .set SEL_SDATA,0x10
+
+ .text
+ .p2align 4
+ .globl multiboot_tramp
+ .type multiboot_tramp, STT_FUNC
+
+/*
+ * void multiboot_tramp(uint32_t magic, struct relocator *relocator,
+ * uint64_t entry)
+ */
+multiboot_tramp:
+ cli
+ movq (%rsi), %rax
+ movq %rax, %rsp /* Switch to temporary stack. */
+ movq 0x8(%rsi), %rax /* relocator->copy */
+ pushq %rdi /* save magic */
+ pushq %rdx /* save entry */
+ movq %rsi, %rdi
+ callq *%rax
+ movq %rax, %rbx /* MBI */
+ popq %rsi /* entry to rsi */
+ popq %rdi /* restore magic */
+ movq gdt@GOTPCREL(%rip), %rax
+ movq gdtaddr@GOTPCREL(%rip), %rdx
+ movq %rax, (%rdx)
+ movq gdtdesc@GOTPCREL(%rip), %rax
+ lgdt (%rax)
+
+ /* record the address */
+ movq multiboot_tramp_2@GOTPCREL(%rip), %rcx
+ movq %rsp, %rax
+ pushq $SEL_SDATA
+ pushq %rax
+ pushf
+ pushq $SEL_SCODE
+ movq multiboot_tramp_1@GOTPCREL(%rip), %rax
+ pushq %rax
+ iretq
+
+ .code32
+multiboot_tramp_1:
+ movl $SEL_SDATA, %eax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ movl %cr0, %eax /* disable paging */
+ btrl $31, %eax
+ movl %eax, %cr0
+ jmp *%ecx
+multiboot_tramp_2:
+ movl %cr4, %eax /* disable PAE, PGE, PSE */
+ andl $~(CR4_PGE | CR4_PAE | CR4_PSE), %eax
+ movl %eax, %cr4
+ movl $MSR_EFER, %ecx
+ rdmsr /* updates %edx:%eax */
+ btcl $8, %eax /* clear long mode */
+ wrmsr
+ movl %edi, %eax /* magic */
+ jmp *%esi /* jump to kernel */
+
+/* GDT record */
+ .p2align 4
+gdt:
+ .word 0x0, 0x0 /* NULL entry */
+ .byte 0x0, 0x0, 0x0, 0x0
+ .word 0xffff, 0x0 /* code segment */
+ .byte 0x0, 0x9a, 0xcf, 0x0
+ .word 0xffff, 0x0 /* data segment */
+ .byte 0x0, 0x92, 0xcf, 0x0
+gdt_end:
+
+ .p2align 4
+gdtdesc: .word gdt_end - gdt - 1 /* limit */
+gdtaddr: .long 0 /* base */
+ .long 0
+
+multiboot_tramp_end:
diff --git a/usr/src/boot/sys/boot/efi/loader/conf.c b/usr/src/boot/sys/boot/efi/loader/conf.c
index 1af649a022..7da0afc6ca 100644
--- a/usr/src/boot/sys/boot/efi/loader/conf.c
+++ b/usr/src/boot/sys/boot/efi/loader/conf.c
@@ -88,3 +88,14 @@ struct console *consoles[] = {
#endif
NULL
};
+
+#if defined(__amd64__) || defined(__i386__)
+extern struct file_format multiboot2;
+#endif
+
+struct file_format *file_formats[] = {
+#if defined(__amd64__) || defined(__i386__)
+ &multiboot2,
+#endif
+ NULL
+};
diff --git a/usr/src/boot/sys/boot/efi/loader/copy.c b/usr/src/boot/sys/boot/efi/loader/copy.c
index 128196e269..8b287b108a 100644
--- a/usr/src/boot/sys/boot/efi/loader/copy.c
+++ b/usr/src/boot/sys/boot/efi/loader/copy.c
@@ -27,91 +27,91 @@
*/
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/multiboot2.h>
#include <stand.h>
#include <bootstrap.h>
#include <efi.h>
#include <efilib.h>
+#include <assert.h>
#include "loader_efi.h"
-#ifndef EFI_STAGING_SIZE
-#define EFI_STAGING_SIZE 48
-#endif
+/*
+ * Allocate pages for data to be loaded. As we can not expect AllocateAddress
+ * to succeed, we allocate using AllocateMaxAddress from 4GB limit.
+ * 4GB limit is because reportedly some 64bit systems are reported to have
+ * issues with memory above 4GB. It should be quite enough anyhow.
+ * Note: AllocateMaxAddress will only make sure we are below the specified
+ * address, we can not make any assumptions about actual location or
+ * about the order of the allocated blocks.
+ */
+uint64_t
+efi_loadaddr(u_int type, void *data, uint64_t addr)
+{
+ EFI_PHYSICAL_ADDRESS paddr;
+ struct stat st;
+ int size;
+ uint64_t pages;
+ EFI_STATUS status;
+
+ if (addr == 0)
+ return (addr); /* nothing to do */
+
+ if (type == LOAD_ELF)
+ return (0); /* not supported */
+
+ if (type == LOAD_MEM)
+ size = *(int *)data;
+ else {
+ stat(data, &st);
+ size = st.st_size;
+ }
-#define STAGE_PAGES EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024)
+ pages = EFI_SIZE_TO_PAGES(size);
+ /* 4GB upper limit */
+ paddr = 0x0000000100000000;
-EFI_PHYSICAL_ADDRESS staging, staging_end;
-int stage_offset_set = 0;
-ssize_t stage_offset;
+ status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
+ pages, &paddr);
-int
-efi_copy_init(void)
-{
- EFI_STATUS status;
-
- status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
- STAGE_PAGES, &staging);
if (EFI_ERROR(status)) {
- printf("failed to allocate staging area: %lu\n",
- EFI_ERROR_CODE(status));
- return (status);
+ printf("failed to allocate %d bytes for staging area: %lu\n",
+ size, EFI_ERROR_CODE(status));
+ return (0);
}
- staging_end = staging + STAGE_PAGES * EFI_PAGE_SIZE;
-#if defined(__aarch64__) || defined(__arm__)
- /*
- * Round the kernel load address to a 2MiB value. This is needed
- * because the kernel builds a page table based on where it has
- * been loaded in physical address space. As the kernel will use
- * either a 1MiB or 2MiB page for this we need to make sure it
- * is correctly aligned for both cases.
- */
- staging = roundup2(staging, 2 * 1024 * 1024);
-#endif
+ return (paddr);
+}
- return (0);
+void
+efi_free_loadaddr(uint64_t addr, uint64_t pages)
+{
+ (void) BS->FreePages(addr, pages);
}
void *
efi_translate(vm_offset_t ptr)
{
-
- return ((void *)(ptr + stage_offset));
+ return ((void *)ptr);
}
ssize_t
efi_copyin(const void *src, vm_offset_t dest, const size_t len)
{
-
- if (!stage_offset_set) {
- stage_offset = (vm_offset_t)staging - dest;
- stage_offset_set = 1;
- }
-
- /* XXX: Callers do not check for failure. */
- if (dest + stage_offset + len > staging_end) {
- errno = ENOMEM;
- return (-1);
- }
- bcopy(src, (void *)(dest + stage_offset), len);
+ assert(dest < 0x100000000);
+ bcopy(src, (void *)(uintptr_t)dest, len);
return (len);
}
ssize_t
efi_copyout(const vm_offset_t src, void *dest, const size_t len)
{
-
- /* XXX: Callers do not check for failure. */
- if (src + stage_offset + len > staging_end) {
- errno = ENOMEM;
- return (-1);
- }
- bcopy((void *)(src + stage_offset), dest, len);
+ assert(src < 0x100000000);
+ bcopy((void *)(uintptr_t)src, dest, len);
return (len);
}
@@ -119,23 +119,86 @@ efi_copyout(const vm_offset_t src, void *dest, const size_t len)
ssize_t
efi_readin(const int fd, vm_offset_t dest, const size_t len)
{
-
- if (dest + stage_offset + len > staging_end) {
- errno = ENOMEM;
- return (-1);
- }
- return (read(fd, (void *)(dest + stage_offset), len));
+ return (read(fd, (void *)dest, len));
}
-void
-efi_copy_finish(void)
+/*
+ * Relocate chunks and return pointer to MBI.
+ * This function is relocated before being called and we only have
+ * memmove() available, as most likely moving chunks into the final
+ * destination will destroy the rest of the loader code.
+ *
+ * In safe area we have relocator data, multiboot_tramp, efi_copy_finish,
+ * memmove and stack.
+ */
+multiboot2_info_header_t *
+efi_copy_finish(struct relocator *relocator)
{
- uint64_t *src, *dst, *last;
+ multiboot2_info_header_t *mbi;
+ struct chunk *chunk, *c;
+ struct chunk_head *head;
+ UINT64 size;
+ int done = 0;
+ void (*move)(void *s1, const void *s2, size_t n);
+
+ move = (void *)relocator->rel_memmove;
+
+ /* MBI is the last chunk in the list. */
+ head = &relocator->rel_chunk_head;
+ chunk = STAILQ_LAST(head, chunk, chunk_next);
+ mbi = (multiboot2_info_header_t *)chunk->chunk_paddr;
- src = (uint64_t *)staging;
- dst = (uint64_t *)(staging - stage_offset);
- last = (uint64_t *)staging_end;
+ /*
+ * If chunk paddr == vaddr, the chunk is in place.
+ * If all chunks are in place, we are done.
+ */
+ chunk = NULL;
+ while (done == 0) {
+ /* First check if we have anything to do. */
+ if (chunk == NULL) {
+ done = 1;
+ STAILQ_FOREACH(chunk, head, chunk_next) {
+ if (chunk->chunk_paddr != chunk->chunk_vaddr) {
+ done = 0;
+ break;
+ }
+ }
+ }
+ if (done == 1)
+ break;
+
+ /*
+ * Make sure the destination is not conflicting
+ * with rest of the modules.
+ */
+ STAILQ_FOREACH(c, head, chunk_next) {
+ /* Moved already? */
+ if (c->chunk_vaddr == c->chunk_paddr)
+ continue;
+ /* Is it the chunk itself? */
+ if (c->chunk_vaddr == chunk->chunk_vaddr &&
+ c->chunk_size == chunk->chunk_size)
+ continue;
+ if ((c->chunk_vaddr >= chunk->chunk_paddr &&
+ c->chunk_vaddr <=
+ chunk->chunk_paddr + chunk->chunk_size) ||
+ (c->chunk_vaddr + c->chunk_size >=
+ chunk->chunk_paddr &&
+ c->chunk_vaddr + c->chunk_size <=
+ chunk->chunk_paddr + chunk->chunk_size))
+ break;
+ }
+ /* If there are no conflicts, move to place and restart. */
+ if (c == NULL) {
+ move((void *)chunk->chunk_paddr,
+ (void *)chunk->chunk_vaddr,
+ chunk->chunk_size);
+ chunk->chunk_vaddr = chunk->chunk_paddr;
+ chunk = NULL;
+ continue;
+ }
+ chunk = STAILQ_NEXT(chunk, chunk_next);
+ }
- while (src < last)
- *dst++ = *src++;
+ return (mbi);
}
diff --git a/usr/src/boot/sys/boot/efi/loader/loader_efi.h b/usr/src/boot/sys/boot/efi/loader/loader_efi.h
index ee7c4bb72e..6378a662d3 100644
--- a/usr/src/boot/sys/boot/efi/loader/loader_efi.h
+++ b/usr/src/boot/sys/boot/efi/loader/loader_efi.h
@@ -1,4 +1,4 @@
-/*-
+/*
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
@@ -24,28 +24,51 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
-#ifndef _LOADER_EFI_COPY_H_
-#define _LOADER_EFI_COPY_H_
+#ifndef _LOADER_EFI_H
+#define _LOADER_EFI_H
#include <stand.h>
+#include <efi.h>
+#include <efilib.h>
+#include <sys/multiboot2.h>
+#include <sys/queue.h>
+#include <bootstrap.h>
+
+struct chunk {
+ EFI_VIRTUAL_ADDRESS chunk_vaddr;
+ EFI_PHYSICAL_ADDRESS chunk_paddr;
+ UINT64 chunk_size;
+ STAILQ_ENTRY(chunk) chunk_next;
+};
+
+STAILQ_HEAD(chunk_head, chunk);
+
+struct relocator {
+ UINT64 rel_stack;
+ UINT64 rel_copy;
+ UINT64 rel_memmove;
+ struct chunk_head rel_chunk_head;
+ struct chunk rel_chunklist[];
+};
int efi_autoload(void);
-int efi_getdev(void **vdev, const char *devspec, const char **path);
-char *efi_fmtdev(void *vdev);
-int efi_setcurrdev(struct env_var *ev, int flags, const void *value);
+int efi_getdev(void **, const char *, const char **);
+char *efi_fmtdev(void *);
+int efi_setcurrdev(struct env_var *, int, const void *);
-int efi_copy_init(void);
+ssize_t efi_copyin(const void *, vm_offset_t, const size_t);
+ssize_t efi_copyout(const vm_offset_t, void *, const size_t);
+ssize_t efi_readin(const int, vm_offset_t, const size_t);
+uint64_t efi_loadaddr(u_int, void *, uint64_t);
+void efi_free_loadaddr(uint64_t, uint64_t);
+void * efi_translate(vm_offset_t);
-ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len);
-ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len);
-ssize_t efi_readin(const int fd, vm_offset_t dest, const size_t len);
-void * efi_translate(vm_offset_t ptr);
+multiboot2_info_header_t *efi_copy_finish(struct relocator *);
+void multiboot_tramp(uint32_t, struct relocator *, uint64_t);
-void efi_copy_finish(void);
+void efi_addsmapdata(struct preloaded_file *);
-#endif /* _LOADER_EFI_COPY_H_ */
+#endif /* _LOADER_EFI_H */
diff --git a/usr/src/boot/sys/boot/efi/loader/main.c b/usr/src/boot/sys/boot/efi/loader/main.c
index 269f7dfaeb..472ce86509 100644
--- a/usr/src/boot/sys/boot/efi/loader/main.c
+++ b/usr/src/boot/sys/boot/efi/loader/main.c
@@ -65,6 +65,7 @@ EFI_GUID serial_io = SERIAL_IO_PROTOCOL;
extern void acpi_detect(void);
void efi_serial_init(void);
+extern void efi_getsmap(void);
#ifdef EFI_ZFS_BOOT
static void efi_zfs_probe(void);
#endif
@@ -209,6 +210,8 @@ main(int argc, CHAR16 *argv[])
archsw.arch_copyin = efi_copyin;
archsw.arch_copyout = efi_copyout;
archsw.arch_readin = efi_readin;
+ archsw.arch_loadaddr = efi_loadaddr;
+ archsw.arch_free_loadaddr = efi_free_loadaddr;
#ifdef EFI_ZFS_BOOT
/* Note this needs to be set before ZFS init. */
archsw.arch_zfs_probe = efi_zfs_probe;
@@ -226,6 +229,7 @@ main(int argc, CHAR16 *argv[])
* printf() etc. once this is done.
*/
cons_probe();
+ efi_getsmap();
/*
* Initialise the block cache. Set the upper limit.
@@ -329,11 +333,6 @@ main(int argc, CHAR16 *argv[])
setenv("console", "ttya" , 1);
}
- if (efi_copy_init()) {
- printf("failed to allocate staging area\n");
- return (EFI_BUFFER_TOO_SMALL);
- }
-
/*
* March through the device switch probing for things.
*/
@@ -796,6 +795,104 @@ command_fdt(int argc, char *argv[])
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
#endif
+/*
+ * Chain load another efi loader.
+ */
+static int
+command_chain(int argc, char *argv[])
+{
+ EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+ EFI_HANDLE loaderhandle;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+ struct stat st;
+ struct devdesc *dev;
+ char *name, *path;
+ void *buf;
+ int fd;
+
+ if (argc < 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ name = argv[1];
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ command_errmsg = "no such file";
+ return (CMD_ERROR);
+ }
+
+ if (fstat(fd, &st) < -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "failed to allocate buffer";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ command_errmsg = "error while reading the file";
+ (void)BS->FreePool(buf);
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+ status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
+ (void)BS->FreePool(buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "LoadImage failed";
+ return (CMD_ERROR);
+ }
+ status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+ (void **)&loaded_image);
+
+ if (argc > 2) {
+ int i, len = 0;
+ CHAR16 *argp;
+
+ for (i = 2; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ len *= sizeof (*argp);
+ loaded_image->LoadOptions = argp = malloc (len);
+ if (loaded_image->LoadOptions == NULL) {
+ (void) BS->UnloadImage(loaded_image);
+ return (CMD_ERROR);
+ }
+ loaded_image->LoadOptionsSize = len;
+ for (i = 2; i < argc; i++) {
+ char *ptr = argv[i];
+ while (*ptr)
+ *(argp++) = *(ptr++);
+ *(argp++) = ' ';
+ }
+ *(--argv) = 0;
+ }
+
+ if (efi_getdev((void **)&dev, name, (const char **)&path) == 0)
+ loaded_image->DeviceHandle =
+ efi_find_handle(dev->d_dev, dev->d_unit);
+
+ dev_cleanup();
+ status = BS->StartImage(loaderhandle, NULL, NULL);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "StartImage failed";
+ free(loaded_image->LoadOptions);
+ loaded_image->LoadOptions = NULL;
+ status = BS->UnloadImage(loaded_image);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_ERROR); /* not reached */
+}
+
+COMMAND_SET(chain, "chain", "chain load file", command_chain);
+
#ifdef EFI_ZFS_BOOT
static void
efi_zfs_probe(void)
diff --git a/usr/src/boot/sys/boot/efi/loader/memmap.c b/usr/src/boot/sys/boot/efi/loader/memmap.c
new file mode 100644
index 0000000000..50834ab44e
--- /dev/null
+++ b/usr/src/boot/sys/boot/efi/loader/memmap.c
@@ -0,0 +1,180 @@
+/*
+ * 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>
+ */
+
+/*
+ * Build smap like memory map from efi memmap.
+ */
+
+#include <stand.h>
+#include <inttypes.h>
+#include <efi.h>
+#include <efilib.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/queue.h>
+#include <sys/stddef.h>
+#include <machine/metadata.h>
+#include <machine/pc/bios.h>
+#include "bootstrap.h"
+
+struct smap_buf {
+ struct bios_smap sb_smap;
+ STAILQ_ENTRY(smap_buf) sb_bufs;
+};
+
+static struct bios_smap *smapbase;
+static int smaplen;
+
+/*
+ * See ACPI 6.1 Table 15-330 UEFI Memory Types and mapping to ACPI address
+ * range types.
+ */
+static int
+smap_type(int type)
+{
+ switch (type) {
+ case EfiLoaderCode:
+ case EfiLoaderData:
+ case EfiBootServicesCode:
+ case EfiBootServicesData:
+ case EfiConventionalMemory:
+ return (SMAP_TYPE_MEMORY);
+ case EfiReservedMemoryType:
+ case EfiRuntimeServicesCode:
+ case EfiRuntimeServicesData:
+ case EfiMemoryMappedIO:
+ case EfiMemoryMappedIOPortSpace:
+ case EfiPalCode:
+ case EfiUnusableMemory:
+ return (SMAP_TYPE_RESERVED);
+ case EfiACPIReclaimMemory:
+ return (SMAP_TYPE_ACPI_RECLAIM);
+ case EfiACPIMemoryNVS:
+ return (SMAP_TYPE_ACPI_NVS);
+ }
+ return (SMAP_TYPE_RESERVED);
+}
+
+void
+efi_getsmap(void)
+{
+ UINTN size, desc_size, key;
+ EFI_MEMORY_DESCRIPTOR *efi_mmap, *p;
+ EFI_PHYSICAL_ADDRESS addr;
+ EFI_STATUS status;
+ STAILQ_HEAD(smap_head, smap_buf) head =
+ STAILQ_HEAD_INITIALIZER(head);
+ struct smap_buf *cur, *next;
+ int i, n, ndesc;
+ int type = -1;
+
+ size = 0;
+ status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
+ efi_mmap = malloc(size);
+ status = BS->GetMemoryMap(&size, efi_mmap, &key, &desc_size, NULL);
+ if (EFI_ERROR(status)) {
+ printf("GetMemoryMap: error %lu\n", EFI_ERROR_CODE(status));
+ free(efi_mmap);
+ return;
+ }
+
+ STAILQ_INIT(&head);
+ n = 0;
+ i = 0;
+ p = efi_mmap;
+ next = NULL;
+ ndesc = size / desc_size;
+ while (i < ndesc) {
+ if (next == NULL) {
+ next = malloc(sizeof(*next));
+ if (next == NULL)
+ break;
+
+ next->sb_smap.base = p->PhysicalStart;
+ next->sb_smap.length =
+ p->NumberOfPages << EFI_PAGE_SHIFT;
+ /*
+ * ACPI 6.1 tells the lower memory should be
+ * reported as normal memory, so we enforce
+ * page 0 type even as vmware maps it as
+ * acpi reclaimable.
+ */
+ if (next->sb_smap.base == 0)
+ type = SMAP_TYPE_MEMORY;
+ else
+ type = smap_type(p->Type);
+ next->sb_smap.type = type;
+
+ STAILQ_INSERT_TAIL(&head, next, sb_bufs);
+ n++;
+ p = NextMemoryDescriptor(p, desc_size);
+ i++;
+ continue;
+ }
+ addr = next->sb_smap.base + next->sb_smap.length;
+ if ((smap_type(p->Type) == type) &&
+ (p->PhysicalStart == addr)) {
+ next->sb_smap.length +=
+ (p->NumberOfPages << EFI_PAGE_SHIFT);
+ p = NextMemoryDescriptor(p, desc_size);
+ i++;
+ } else
+ next = NULL;
+ }
+ smaplen = n;
+ if (smaplen > 0) {
+ smapbase = malloc(smaplen * sizeof(*smapbase));
+ if (smapbase != NULL) {
+ n = 0;
+ STAILQ_FOREACH(cur, &head, sb_bufs)
+ smapbase[n++] = cur->sb_smap;
+ }
+ cur = STAILQ_FIRST(&head);
+ while (cur != NULL) {
+ next = STAILQ_NEXT(cur, sb_bufs);
+ free(cur);
+ cur = next;
+ }
+ }
+ free(efi_mmap);
+}
+
+void
+efi_addsmapdata(struct preloaded_file *kfp)
+{
+ size_t size;
+
+ if (smapbase == NULL || smaplen == 0)
+ return;
+ size = smaplen * sizeof(*smapbase);
+ file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase);
+}
+
+COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap);
+
+static int
+command_smap(int argc, char *argv[])
+{
+ u_int i;
+
+ if (smapbase == NULL || smaplen == 0)
+ return (CMD_ERROR);
+
+ for (i = 0; i < smaplen; i++)
+ printf("SMAP type=%02" PRIx32 " base=%016" PRIx64
+ " len=%016" PRIx64 "\n", smapbase[i].type,
+ smapbase[i].base, smapbase[i].length);
+ return (CMD_OK);
+}
diff --git a/usr/src/boot/sys/boot/i386/libi386/libi386.h b/usr/src/boot/sys/boot/i386/libi386/libi386.h
index ac614ae2eb..433e8f7563 100644
--- a/usr/src/boot/sys/boot/i386/libi386/libi386.h
+++ b/usr/src/boot/sys/boot/i386/libi386/libi386.h
@@ -25,6 +25,8 @@
*
*/
+#ifndef _LIBI386_H
+#define _LIBI386_H
/*
* i386 fully-qualified device descriptor.
@@ -153,4 +155,8 @@ int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep,
vm_offset_t *kernend, int add_smap);
int bi_checkcpu(void);
+int mb_kernel_cmdline(struct preloaded_file *, struct devdesc *, char **);
+void multiboot_tramp(uint32_t, vm_offset_t, vm_offset_t);
void pxe_enable(void *pxeinfo);
+
+#endif /* _LIBI386_H */
diff --git a/usr/src/boot/sys/boot/i386/libi386/multiboot.c b/usr/src/boot/sys/boot/i386/libi386/multiboot.c
index 32e4fe3b07..e6329240b6 100644
--- a/usr/src/boot/sys/boot/i386/libi386/multiboot.c
+++ b/usr/src/boot/sys/boot/i386/libi386/multiboot.c
@@ -50,11 +50,13 @@
#include "bootstrap.h"
#include "multiboot.h"
-#include "pxe.h"
#include "../zfs/libzfs.h"
#include "../i386/libi386/libi386.h"
#include "../i386/btx/lib/btxv86.h"
+#define SUPPORT_DHCP
+#include <bootp.h>
+
#define MULTIBOOT_SUPPORTED_FLAGS \
(MULTIBOOT_AOUT_KLUDGE|MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
#define METADATA_FIXED_SIZE (PAGE_SIZE*4)
@@ -67,16 +69,6 @@
static vm_offset_t last_addr;
extern char bootprog_info[];
-extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
- struct preloaded_file **result, int multiboot);
-extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
-extern int elf64_obj_loadfile(char *filename, u_int64_t dest,
- struct preloaded_file **result);
-extern int mb_kernel_cmdline(struct preloaded_file *, struct devdesc *,
- char **);
-
-extern void multiboot_tramp();
-
static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
static int multiboot_exec(struct preloaded_file *);
@@ -261,7 +253,6 @@ multiboot_exec(struct preloaded_file *fp)
multiboot_memory_map_t *mmap;
struct bios_smap *smap;
struct devdesc *rootdev;
- extern BOOTPLAYER bootplayer; /* dhcp info */
char *cmdline = NULL;
size_t len;
int error, num, i;
@@ -393,10 +384,11 @@ multiboot_exec(struct preloaded_file *fp)
mb_info->mmap_addr = VTOP(mmap);
mb_info->flags |= MULTIBOOT_INFO_MEM_MAP;
- if (strstr(getenv("loaddev"), "pxe") != NULL) {
- mb_info->drives_length = sizeof (BOOTPLAYER);
+ if (strstr(getenv("loaddev"), "pxe") != NULL &&
+ bootp_response != NULL) {
+ mb_info->drives_length = sizeof (*bootp_response);
mb_info->drives_addr = mb_malloc(mb_info->drives_length);
- i386_copyin(&bootplayer, mb_info->drives_addr,
+ i386_copyin(bootp_response, mb_info->drives_addr,
mb_info->drives_length);
mb_info->flags &= ~MULTIBOOT_INFO_DRIVE_INFO;
}
diff --git a/usr/src/boot/sys/boot/i386/libi386/pxe.c b/usr/src/boot/sys/boot/i386/libi386/pxe.c
index 4a7880f64c..2feb4d0f69 100644
--- a/usr/src/boot/sys/boot/i386/libi386/pxe.c
+++ b/usr/src/boot/sys/boot/i386/libi386/pxe.c
@@ -58,7 +58,7 @@ static char data_buffer[PXE_BUFFER_SIZE];
static pxenv_t *pxenv_p = NULL; /* PXENV+ */
static pxe_t *pxe_p = NULL; /* !PXE */
-BOOTPLAYER bootplayer = {0}; /* PXE Cached information. */
+static BOOTPLAYER bootplayer = {0}; /* PXE Cached information. */
static int pxe_debug = 0;
static int pxe_sock = -1;