Page MenuHomeFreeBSD

D55559.diff
No OneTemporary

D55559.diff

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

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)

Event Timeline