Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161148097
D55559.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D55559.diff
View Options
diff --git a/stand/efi/include/efilib.h b/stand/efi/include/efilib.h
--- a/stand/efi/include/efilib.h
+++ b/stand/efi/include/efilib.h
@@ -31,6 +31,7 @@
#include <stand.h>
#include <stdbool.h>
#include <sys/queue.h>
+#include <efipartinfo.h>
#include <Protocol/BlockIo.h>
@@ -110,6 +111,9 @@
EFI_DEVICE_PATH *efi_name_to_devpath16(CHAR16 *path);
void efi_devpath_free(EFI_DEVICE_PATH *dp);
EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles);
+EFI_PARTITION_INFO_PROTOCOL *efi_devpath_partinfo(EFI_DEVICE_PATH *devpath);
+bool efi_devpath_havepartinfo(EFI_DEVICE_PATH *devpath);
+bool efi_devpath_havebootme(EFI_DEVICE_PATH *devpath);
int efi_status_to_errno(EFI_STATUS);
EFI_STATUS errno_to_efi_status(int errno);
@@ -132,6 +136,7 @@
/* CHAR16 utility functions. */
int wcscmp(CHAR16 *, CHAR16 *);
void cpy8to16(const char *, CHAR16 *, size_t);
+void xcpy8to16(const char *, CHAR16 *, size_t);
void cpy16to8(const CHAR16 *, char *, size_t);
/*
@@ -157,4 +162,12 @@
/* efipart.c */
int efipart_inithandles(void);
+/* efigpt.c */
+struct efigpt_ctx {
+ EFI_BLOCK_IO *blkio;
+ EFI_PARTITION_TABLE_HEADER hdr;
+ EFI_PARTITION_ENTRY table[128];
+};
+struct efigpt_ctx *efigpt_read(EFI_BLOCK_IO *blkio);
+
#endif /* _LOADER_EFILIB_H */
diff --git a/stand/efi/include/efipartinfo.h b/stand/efi/include/efipartinfo.h
new file mode 100644
--- /dev/null
+++ b/stand/efi/include/efipartinfo.h
@@ -0,0 +1,34 @@
+
+#ifndef _EFI_PARTINFO_H
+#define _EFI_PARTINFO_H
+
+#include "efipart.h"
+#include "efigpt.h"
+
+#define EFI_PARTITION_INFO_PROTOCOL_GUID \
+{ 0x8cf2f62c, 0xbc9b, 0x4821, {0x80, 0x8d, 0xec, 0x9e, 0xc4, 0x21, 0xa1, 0xa0} }
+
+#define EFI_PARTITION_INFO_PROTOCOL_REVISION 0x0001000
+#define PARTITION_TYPE_OTHER 0x00
+#define PARTITION_TYPE_MBR 0x01
+#define PARTITION_TYPE_GPT 0x02
+
+typedef struct {
+
+ UINT32 Revision;
+ UINT32 Type;
+ UINT8 System;
+ UINT8 Reserved[7];
+ union {
+ ///
+ /// MBR data
+ ///
+ MBR_PARTITION_RECORD Mbr;
+
+ ///
+ /// GPT data
+ ///
+ EFI_PARTITION_ENTRY Gpt;
+ } Info;
+} EFI_PARTITION_INFO_PROTOCOL;
+#endif
diff --git a/stand/efi/libefi/Makefile b/stand/efi/libefi/Makefile
--- a/stand/efi/libefi/Makefile
+++ b/stand/efi/libefi/Makefile
@@ -11,6 +11,7 @@
efichar.c \
eficom.c \
efienv.c \
+ efigpt.c \
efihttp.c \
efinet.c \
efipart.c \
diff --git a/stand/efi/libefi/devpath.c b/stand/efi/libefi/devpath.c
--- a/stand/efi/libefi/devpath.c
+++ b/stand/efi/libefi/devpath.c
@@ -23,9 +23,12 @@
* SUCH DAMAGE.
*/
+#include <sys/disk/gpt.h>
+
#include <efi.h>
#include <efilib.h>
#include <efichar.h>
+#include <efipartinfo.h>
#include <uuid.h>
#include <machine/_inttypes.h>
#include <Protocol/DevicePathToText.h>
@@ -35,6 +38,7 @@
EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+static EFI_GUID PartitionInfoGUID = EFI_PARTITION_INFO_PROTOCOL_GUID;
static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol;
static EFI_GUID DevicePathFromTextGUID =
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID;
@@ -766,3 +770,32 @@
}
return (NULL);
}
+
+EFI_PARTITION_INFO_PROTOCOL *
+efi_devpath_partinfo(EFI_DEVICE_PATH *devpath)
+{
+ EFI_PARTITION_INFO_PROTOCOL *pi;
+
+ if (OpenProtocolByHandle(efi_devpath_handle(devpath), &PartitionInfoGUID, (void **)&pi) != EFI_SUCCESS)
+ return (NULL);
+ return (pi);
+}
+
+bool
+efi_devpath_havepartinfo(EFI_DEVICE_PATH *devpath)
+{
+ return (efi_devpath_partinfo(devpath) != NULL);
+}
+
+bool
+efi_devpath_havebootme(EFI_DEVICE_PATH *devpath)
+{
+ EFI_PARTITION_INFO_PROTOCOL *pi;
+
+ pi = efi_devpath_partinfo(devpath);
+ if (pi == NULL)
+ return (false);
+ if (pi->Type != PARTITION_TYPE_GPT)
+ return (false);
+ return (pi->Info.Gpt.Attributes & GPT_ENT_ATTR_BOOTME);
+}
diff --git a/stand/efi/libefi/efigpt.c b/stand/efi/libefi/efigpt.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/libefi/efigpt.c
@@ -0,0 +1,253 @@
+/*-
+ * Copyright (c) 2025 Stormshield
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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.
+ */
+
+/* Heavily inspired by libsa/gpt.c */
+
+#include <sys/param.h>
+
+#include <efi.h>
+#include <efigpt.h>
+#include <efilib.h>
+
+#ifndef LITTLE_ENDIAN
+#error efigpt.c works only for little endian architectures
+#endif
+
+#define EFI_GPT_HDR_SIG "EFI PART"
+
+static uint64_t
+blksize(EFI_BLOCK_IO *blkio)
+{
+ return (blkio->Media->LastBlock);
+}
+
+static int
+blkread(EFI_BLOCK_IO *blkio, void *buf, daddr_t lba, unsigned nblk)
+{
+ EFI_STATUS status;
+ int size;
+
+ lba = lba / (blkio->Media->BlockSize / DEV_BSIZE);
+ size = nblk * DEV_BSIZE;
+ status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, lba,
+ size, buf);
+ return (EFI_ERROR(status) ? -1 : 0);
+}
+
+static UINT32
+crc32(const unsigned char *data, size_t data_size)
+{
+ EFI_STATUS status;
+ UINT32 crc;
+
+ status = BS->CalculateCrc32((void*)(uintptr_t)data, data_size, &crc);
+ return (EFI_ERROR(status) ? 0 : crc);
+}
+
+static int
+readhdr(const char *which, struct efigpt_ctx *gpt, EFI_LBA lba)
+{
+ uint32_t target_crc32, current_crc32;
+ char blk[DEV_BSIZE];
+ EFI_PARTITION_TABLE_HEADER *hdr;
+
+ if (blkread(gpt->blkio, blk, lba, 1)) {
+ printf("ERROR: unable to read %s GPT header\n", which);
+ return (-1);
+ }
+ hdr = (EFI_PARTITION_TABLE_HEADER *)blk;
+
+ /* Perform some sanity checks */
+ if (bcmp(&hdr->Header.Signature, EFI_GPT_HDR_SIG, sizeof(hdr->Header.Signature)) != 0) {
+ printf("ERROR: invalid %s GPT header; invalid signature\n", which);
+ return (-1);
+ }
+ if (hdr->MyLBA != lba) {
+ printf("ERROR: invalid %s GPT header; invalid LBA\n", which);
+ return (-1);
+ }
+ if (hdr->Header.Revision < 0x00010000) {
+ printf("ERROR: invalid %s GPT header; unexpected revision\n", which);
+ return (-1);
+ }
+ if (hdr->SizeOfPartitionEntry != sizeof(EFI_PARTITION_ENTRY)) {
+ printf("ERROR: invalid %s GPT header; unexpected partition entry size\n", which);
+ return (-1);
+ }
+ if (hdr->NumberOfPartitionEntries > sizeof(gpt->table)) {
+ printf("ERROR: invalid %s GPT header; too many entries\n", which);
+ return (-1);
+ }
+ if (DEV_BSIZE % hdr->SizeOfPartitionEntry != 0) {
+ printf("ERROR: invalid %s GPT header; block size vs partition entry size inconsistency\n", which);
+ return (-1);
+ }
+
+ /* Verify integrity */
+ target_crc32 = hdr->Header.CRC32;
+ hdr->Header.CRC32 = 0;
+ current_crc32 = crc32((const unsigned char *)hdr, hdr->Header.HeaderSize);
+ if (current_crc32 != target_crc32) {
+ printf("ERROR: %s GPT header checksum mismatch, got %#x, want %#x\n",
+ which, current_crc32, target_crc32);
+ return (-1);
+ }
+ hdr->Header.CRC32 = target_crc32;
+
+ bcopy(hdr, &gpt->hdr, sizeof(gpt->hdr));
+
+ return (0);
+}
+
+static int
+readtable(const char *which, struct efigpt_ctx *gpt)
+{
+ uint32_t ent_idx;
+ EFI_LBA ent_lba;
+ EFI_PARTITION_TABLE_HEADER *hdr;
+ EFI_PARTITION_ENTRY *table;
+ int entries_per_block, valid_count;
+ char blk[DEV_BSIZE];
+ UINT32 target_crc32, current_crc32;
+
+ hdr = &gpt->hdr;
+ table = gpt->table;
+
+ if (hdr->NumberOfPartitionEntries == 0) {
+ printf("WARNING: %s GPT partition table is empty\n",
+ which);
+ return (0);
+ }
+
+ if (DEV_BSIZE < hdr->SizeOfPartitionEntry) {
+ printf("ERROR: block size is smaller than GPT partition entry size, %u < %u\n",
+ DEV_BSIZE, hdr->SizeOfPartitionEntry);
+ return (-1);
+ }
+ entries_per_block = DEV_BSIZE / hdr->SizeOfPartitionEntry;
+
+ for (ent_lba = hdr->PartitionEntryLBA, ent_idx = 0, valid_count = 0;
+ ent_idx < hdr->NumberOfPartitionEntries;
+ ent_lba++) {
+ EFI_PARTITION_ENTRY *ent;
+
+ if (blkread(gpt->blkio, blk, ent_lba, 1)) {
+ printf("ERROR: unable to read block at %lu (%s GPT table chunk)\n",
+ ent_lba, which);
+ return (-1);
+ }
+ ent = (EFI_PARTITION_ENTRY *)blk;
+
+ for (int e = 0; e < entries_per_block && ent_idx < hdr->NumberOfPartitionEntries; e++, ent++, ent_idx++) {
+ bool valid = true;
+
+ bcopy(ent, &table[ent_idx], sizeof(table[ent_idx]));
+
+ /* Identify invalid partitions */
+ if (ent->PartitionTypeGUID.Data1 == 0 && ent->PartitionTypeGUID.Data2 == 0 && ent->PartitionTypeGUID.Data3 == 0) {
+#ifdef EFI_DEBUG
+ printf("WARNING: invalid partition type GUID for partition %u\n", ent_idx);
+#endif
+ valid = false;
+ }
+ if (ent->StartingLBA < hdr->FirstUsableLBA || hdr->LastUsableLBA < ent->StartingLBA) {
+#ifdef EFI_DEBUG
+ printf("WARNING: starting LBA out of media range (%lu not in %lu..%lu) for partition %u\n",
+ ent->StartingLBA, hdr->FirstUsableLBA, hdr->LastUsableLBA, ent_idx);
+#endif
+ valid = false;
+ }
+ if (ent->EndingLBA < hdr->FirstUsableLBA || hdr->LastUsableLBA < ent->EndingLBA) {
+#ifdef EFI_DEBUG
+ printf("WARNING: ending LBA out of media range (%lu not in %lu..%lu) for partition %u\n",
+ ent->EndingLBA, hdr->FirstUsableLBA, hdr->LastUsableLBA, ent_idx);
+#endif
+ valid = false;
+ }
+ if (valid)
+ valid_count++;
+ }
+ }
+
+ /* Verify table integrity */
+ target_crc32 = hdr->PartitionEntryArrayCRC32;
+ current_crc32 = crc32((const unsigned char *)table, hdr->NumberOfPartitionEntries * hdr->SizeOfPartitionEntry);
+ if (current_crc32 != target_crc32) {
+ printf("ERROR: %s GPT table checksum mismatch, got %#x, want %#x\n",
+ which, current_crc32, target_crc32);
+ return (-1);
+ }
+
+#ifdef EFI_DEBUG
+ printf("Found %u/%u valid partition entries in %s GPT\n",
+ valid_count, hdr->NumberOfPartitionEntries, which);
+#endif
+ return (0);
+}
+
+struct efigpt_ctx *
+efigpt_read(EFI_BLOCK_IO *blkio)
+{
+ struct efigpt_ctx *gpt = NULL;
+ EFI_STATUS status;
+ uint64_t altlba;
+
+ status = BS->AllocatePool(EfiLoaderData, sizeof(struct efigpt_ctx), (void **)&gpt);
+ if (EFI_ERROR(status))
+ goto fail;
+ gpt->blkio = blkio;
+
+ /* Try to use the primary header. */
+ if (readhdr("primary", gpt, PRIMARY_PART_HEADER_LBA) == 0 &&
+ readtable("primary", gpt) == 0) {
+#ifdef EFI_DEBUG
+ printf("Using primary GPT\n");
+#endif
+ return (gpt);
+ }
+
+ /* Try to locate and use the secondary header. */
+ altlba = blksize(gpt->blkio);
+ if (altlba > 0)
+ altlba--;
+ if (altlba == 0) {
+ printf("ERROR: unable to locate secondary GPT\n");
+ goto fail;
+ }
+ if (readhdr("secondary", gpt, altlba) == 0 &&
+ readtable("secondary", gpt) == 0) {
+#ifdef EFI_DEBUG
+ printf("Using secondary GPT\n");
+#endif
+ return (gpt);
+ }
+
+ fail:
+ if (gpt)
+ BS->FreePool(gpt);
+ return (NULL);
+}
diff --git a/stand/efi/libefi/wchar.c b/stand/efi/libefi/wchar.c
--- a/stand/efi/libefi/wchar.c
+++ b/stand/efi/libefi/wchar.c
@@ -58,6 +58,22 @@
*dst++ = (CHAR16)0;
}
+/*
+ * xcpy8to16 copies a traditional NUL-terminated C string into a CHAR16 string and
+ * always 0 terminates it. nitems is the number of CHAR16 items available in dst.
+ */
+void
+xcpy8to16(const char *src, CHAR16 *dst, size_t nitems)
+{
+ while (nitems > 0 && *src) {
+ *dst++ = *src++;
+ nitems--;
+ }
+ if (nitems == 0) /* overwrite the last item */
+ dst--;
+ *dst = (CHAR16)0;
+}
+
void
cpy16to8(const CHAR16 *src, char *dst, size_t len)
{
diff --git a/stand/efi/loader/Makefile b/stand/efi/loader/Makefile
--- a/stand/efi/loader/Makefile
+++ b/stand/efi/loader/Makefile
@@ -99,6 +99,13 @@
# Always add MI sources
.include "${BOOTSRC}/loader.mk"
+MK_LOADER_PARTMENU?= no
+.if ${MK_LOADER_PARTMENU} != "no"
+SRCS+= partmenu.c
+CFLAGS+= -DEFI_LOADER_PARTMENU
+# CFLAGS+= -DEFI_DEBUG
+.endif
+
CLEANFILES+= 8x16.c
8x16.c: ${SRCTOP}/contrib/terminus/ter-u16b.bdf
diff --git a/stand/efi/loader/main.c b/stand/efi/loader/main.c
--- a/stand/efi/loader/main.c
+++ b/stand/efi/loader/main.c
@@ -79,6 +79,9 @@
#include <acpi_detect.h>
#include "loader_efi.h"
+#ifdef EFI_LOADER_PARTMENU
+#include "partmenu.h"
+#endif
struct arch_switch archsw = { /* MI/MD interface boundary */
.arch_autoload = efi_autoload,
@@ -153,6 +156,7 @@
[RELAXED] = "relaxed",
};
+
static bool
has_keyboard(void)
{
@@ -646,6 +650,13 @@
if (dp->pd_parent != NULL) {
pdinfo_t *espdp = dp;
dp = dp->pd_parent;
+#ifdef EFI_LOADER_PARTMENU
+ pp = partmenu(dp, espdp);
+ if (pp) {
+ try_as_currdev(dp, pp);
+ return (0);
+ }
+#endif
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
/* Already tried the ESP */
if (espdp == pp)
diff --git a/stand/efi/loader/partmenu.h b/stand/efi/loader/partmenu.h
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/partmenu.h
@@ -0,0 +1,9 @@
+#ifndef EFI_LOADER_PARTMENU_H
+#define EFI_LOADER_PARTMENU_H
+
+#include <efi.h>
+#include <efilib.h>
+
+pdinfo_t *partmenu(pdinfo_t *dp, pdinfo_t *espdp);
+
+#endif /* EFI_LOADER_PARTMENU_H */
diff --git a/stand/efi/loader/partmenu.c b/stand/efi/loader/partmenu.c
new file mode 100644
--- /dev/null
+++ b/stand/efi/loader/partmenu.c
@@ -0,0 +1,338 @@
+#include <sys/param.h>
+#include <sys/disk/gpt.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include <efipartinfo.h>
+
+void delay(int usecs);
+
+#define PARTMENU_BACKKEY 'q'
+#define PARTMENU_INFOKEY 'i'
+#define PARTMENU_MAXENTRIES 10
+#define PARTMENU_NAMESIZE 36 /* Keep consistent with sizeof(EFI_PARTITION_ENTRY.PartitionName). */
+#define PARTMENU_BRIGHT "\033[1m"
+#define PARTMENU_NORMAL "\033[0m"
+static int partmenu_units[PARTMENU_MAXENTRIES];
+static CHAR16 partmenu_names[PARTMENU_MAXENTRIES][PARTMENU_NAMESIZE];
+
+static EFI_PARTITION_ENTRY *
+partmenu_getpartentry(pdinfo_t *pp, pdinfo_t *dp)
+{
+ EFI_PARTITION_INFO_PROTOCOL *pi;
+ static struct efigpt_ctx *gpt = NULL;
+
+ pi = efi_devpath_partinfo(pp->pd_devpath);
+ if (pi)
+ return (&pi->Info.Gpt);
+ else if (gpt || (gpt = efigpt_read(dp->pd_blkio)))
+ return (&gpt->table[pp->pd_unit - 1]);
+ else
+ return (NULL);
+}
+
+/* Load partmenu_units with the filtered partition units, if any. */
+static void
+partmenu_loadunits(void)
+{
+ int count;
+ char *s;
+
+ s = getenv("partmenu_units");
+ if (s == NULL) {
+#ifdef EFI_DEBUG
+ printf(" Unit Filter: none\n");
+#endif
+ partmenu_units[0] = 0;
+ return;
+ }
+
+ count = 0;
+ for (char *unitstr = strtok(s, ",");
+ unitstr && count < PARTMENU_MAXENTRIES;
+ unitstr = strtok(NULL, ","), count++) {
+ unsigned long n = strtoul(unitstr, NULL, 0);
+ if (n >= PARTMENU_MAXENTRIES)
+ break;
+ partmenu_units[count] = (int)n;
+ }
+
+#ifdef EFI_DEBUG
+ printf(" Unit Filter:");
+ if (partmenu_units[0] == 0)
+ printf(" empty or invalid");
+ else {
+ for (int u = 0; partmenu_units[u] != 0 && u < PARTMENU_MAXENTRIES; u++)
+ printf(" %d", partmenu_units[u]);
+ }
+ printf("\n");
+#endif
+}
+
+/* Load partmenu_names with the filtered partition names, if any. */
+static void
+partmenu_loadnames(void)
+{
+ char *s, *namestr;
+
+ s = getenv("partmenu_names");
+ if (s)
+ s = strdup(s); /* Silently ignore any allocation error */
+ if (s == NULL) {
+#ifdef EFI_DEBUG
+ printf(" Name Filter: none\n");
+#endif
+ partmenu_names[0][0] = 0;
+ return;
+ }
+
+ for (int count = 0; (namestr = strsep(&s, ",")) != NULL && count < PARTMENU_MAXENTRIES; count++)
+ xcpy8to16(namestr, (CHAR16 *)&partmenu_names[count], nitems(partmenu_names[count]));
+
+#ifdef EFI_DEBUG
+ printf(" Name Filter:");
+ if (partmenu_names[0][0] == 0)
+ printf(" empty or invalid");
+ else {
+ for (int n = 0; partmenu_names[n][0] != 0 && n < PARTMENU_MAXENTRIES; n++)
+ printf(" \"%S\"", partmenu_names[n]);
+ }
+ printf("\n");
+#endif
+
+ free(s);
+}
+
+/* Tell if the given partition should be part of the menu or not.
+ * If non-empty, rely on partmenu_units, else use the BOOTME attribute.
+ */
+static bool
+partmenu_take(pdinfo_t *pp, pdinfo_t *dp)
+{
+ if (partmenu_units[0] == 0 && partmenu_names[0][0] == 0)
+ return (efi_devpath_havebootme(pp->pd_devpath)); /* No filter, use BOOTME */
+ for (int u = 0; partmenu_units[u] != 0 && u < PARTMENU_MAXENTRIES; u++) {
+ if (partmenu_units[u] == pp->pd_unit)
+ return (true);
+ }
+ for (int n = 0; partmenu_names[n][0] != 0 && n < PARTMENU_MAXENTRIES; n++) {
+ EFI_PARTITION_ENTRY *pe = partmenu_getpartentry(pp, dp);
+ if (!pe) {
+ printf("WARNING: missing EFI partition entry for partition %u", pp->pd_unit);
+ continue;
+ }
+ if (!wcscmp(partmenu_names[n], pe->PartitionName))
+ return (true);
+ }
+ return (false);
+}
+
+/* Print partition's name, unit and attributes. Return true if the printed
+ * partition have the BOOTME attribute.
+ */
+static bool
+partmenu_printpartnameunit(pdinfo_t *pp, pdinfo_t *dp)
+{
+ EFI_PARTITION_ENTRY *pe;
+ bool bootme = false;
+
+ pe = partmenu_getpartentry(pp, dp);
+ printf("%s", PARTMENU_BRIGHT);
+ if (pe) {
+ if (pe->PartitionName[0] != 0)
+ printf("%S", pe->PartitionName);
+ else
+ printf("partition %d", pp->pd_unit);
+ bootme = (pe->Attributes & GPT_ENT_ATTR_BOOTME);
+ } else
+ printf("partition %d", pp->pd_unit);
+ printf("%s", PARTMENU_NORMAL);
+ return (bootme);
+}
+
+void
+partmenu_info(pdinfo_t *dp, pdinfo_t *espdp)
+{
+ pdinfo_t *pp;
+ CHAR16 *text;
+
+ printf("\n");
+ printf(">>> Information\n");
+ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+ EFI_PARTITION_ENTRY *pe;
+
+ printf(" Partition %u", pp->pd_unit);
+ if (espdp == pp)
+ printf(", EFI System Partition");
+ printf("\n");
+
+ printf(" Partition Info: ");
+ if (efi_devpath_havepartinfo(pp->pd_devpath))
+ printf("available");
+ else
+ printf("unavailable");
+ printf("\n");
+
+ text = efi_devpath_name(pp->pd_devpath);
+ if (text != NULL) {
+ printf(" Path: %S\n", text);
+ efi_free_devpath_name(text);
+ }
+
+ pe = partmenu_getpartentry(pp, dp);
+ if (pe) {
+ if (pe->PartitionName[0] != 0)
+ printf(" Name: %S\n", pe->PartitionName);
+
+ if (pe->Attributes != 0) {
+ printf(" Attributes: %#lx", pe->Attributes);
+ if (pe->Attributes & GPT_ENT_ATTR_PLATFORM)
+ printf(" PLATFORM");
+ if (pe->Attributes & GPT_ENT_ATTR_BOOTME)
+ printf(" BOOTME");
+ if (pe->Attributes & GPT_ENT_ATTR_BOOTONCE)
+ printf(" BOOTONCE");
+ if (pe->Attributes & GPT_ENT_ATTR_BOOTFAILED)
+ printf(" BOOTFAILED");
+ printf(" \n");
+ }
+ }
+ }
+
+ printf("[%c] Back\n", PARTMENU_BACKKEY);
+ while (true) {
+ EFI_INPUT_KEY key;
+
+ if (ST->ConIn->ReadKeyStroke(ST->ConIn, &key) != EFI_SUCCESS) {
+ delay(1000000); /* Wait one second. */
+ continue;
+ }
+ if (key.UnicodeChar == PARTMENU_BACKKEY)
+ return;
+ }
+}
+
+/* Print a menu to let the user pick a partition. */
+pdinfo_t *
+partmenu(pdinfo_t *dp, pdinfo_t *espdp)
+{
+ int maxdelay, orig_maxdelay, partinfo_unavail = 0, nentries;
+ pdinfo_t *pp, *firstbootmepp = 0, *parts[10] = {0};
+ time_t keyhit_timeout;
+ char k, firstkey = '0', *s;
+ CHAR16 *text;
+
+ partmenu_loadunits();
+ partmenu_loadnames();
+
+ s = getenv("partmenu_firstkey");
+ if (s && '0' <= *s && *s <= '9')
+ firstkey = *s;
+ s = getenv("partmenu_maxdelay");
+ if (s == NULL)
+ maxdelay = orig_maxdelay = 10; /* seconds */
+ else {
+ unsigned long ul = strtoul(s, NULL, 0);
+ if (ul >= 30)
+ ul = 30;
+ maxdelay = orig_maxdelay = (int)ul;
+ }
+
+ /* Print the menu. */
+ print_partmenu:
+ k = firstkey;
+ nentries = 0;
+ printf("\n");
+ printf(">>> Partitions\n");
+ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+ bool bootme;
+
+ if (espdp == pp)
+ continue;
+ if (!partmenu_take(pp, dp))
+ continue;
+
+ printf("[%c] Boot ", k);
+ bootme = partmenu_printpartnameunit(pp, dp);
+ printf("\n");
+
+ if (bootme && !firstbootmepp)
+ firstbootmepp = pp;
+ parts[k - firstkey] = pp;
+ k++;
+ nentries++;
+ if (k > '9') {
+#ifdef EFI_DEBUG
+ printf("WARNING: ignoring remaining partitions\n");
+#endif
+ break;
+ }
+ }
+ printf("[%c] Display Information\n", PARTMENU_INFOKEY);
+
+ /* Explain default behaviour */
+ pp = NULL;
+ s = getenv("partmenu_default");
+ if (s && !strcmp(s, "firstbootme"))
+ pp = firstbootmepp;
+ printf("Default: ");
+ if (pp == NULL)
+ printf("skip");
+ else {
+ printf("firstbootme, boot ");
+ partmenu_printpartnameunit(pp, dp);
+ }
+ printf("\n");
+
+ printf("Choose: ");
+ if (nentries < 2)
+ goto done;
+
+ /* Wait for a key to be pressed. */
+ for (; maxdelay > 0; maxdelay--) {
+ EFI_INPUT_KEY key;
+ pdinfo_t *p;
+
+ if (ST->ConIn->ReadKeyStroke(ST->ConIn, &key) != EFI_SUCCESS) {
+ delay(1000000); /* Wait one second. */
+ continue;
+ }
+#ifdef EFI_DEBUG
+ printf("key %#x<%c> ", key.UnicodeChar, key.UnicodeChar);
+#endif
+ if (key.UnicodeChar == PARTMENU_INFOKEY) {
+ printf("display information\n");
+ partmenu_info(dp, espdp);
+ maxdelay = orig_maxdelay; /* Reset the autoboot timer */
+ goto print_partmenu;
+ }
+ if (key.UnicodeChar < firstkey || key.UnicodeChar > '9')
+ break; /* Invalid key pressed */
+
+ p = parts[key.UnicodeChar - firstkey];
+
+ /* Valid key but not assigned to a partition, e.g., pressing 7 while
+ * only 3 partitions were found, i.e., key should be in 0..2.
+ */
+ if (p == NULL)
+ break;
+ pp = p;
+ break; /* Partition selected by user */
+ }
+ done:
+ if (pp == NULL) {
+ printf("skip\n"); /* Invalid key pressed or timeout reached */
+ return (NULL);
+ } else {
+ printf("boot ");
+ partmenu_printpartnameunit(pp, dp);
+ printf("\n");
+ }
+
+ text = efi_devpath_name(efi_devpath_last_node(pp->pd_devpath));
+ if (text != NULL) {
+ printf("Trying: %S\n", text);
+ efi_free_devpath_name(text);
+ }
+ return (pp);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jul 1, 11:58 PM (7 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34581210
Default Alt Text
D55559.diff (22 KB)
Attached To
Mode
D55559: Let the EFI loader build a partition menu
Attached
Detach File
Event Timeline
Log In to Comment