From de91fba78b8d8bcc8dff5a5d15d763bd883bbda6 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:01 +0000 Subject: [PATCH 01/52] Restore grub-mkdevicemap This is kind of a mess, requiring lots of OS-specific code to iterate over all possible devices. However, we use it in a number of scripts to discover devices and reimplementing those in terms of something else would be very complicated. Author: Dimitri John Ledkov Forwarded: no Last-Update: 2018-03-16 Patch-Name: restore_mkdevicemap.patch --- Makefile.util.def | 17 + docs/man/grub-mkdevicemap.h2m | 4 + include/grub/util/deviceiter.h | 14 + util/deviceiter.c | 1021 ++++++++++++++++++++++++++++++++ util/devicemap.c | 13 + util/grub-mkdevicemap.c | 181 ++++++ 6 files changed, 1250 insertions(+) create mode 100644 docs/man/grub-mkdevicemap.h2m create mode 100644 include/grub/util/deviceiter.h create mode 100644 util/deviceiter.c create mode 100644 util/devicemap.c create mode 100644 util/grub-mkdevicemap.c diff --git a/Makefile.util.def b/Makefile.util.def index f9caccb97..c939deb92 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -314,6 +314,23 @@ program = { condition = COND_GRUB_MKFONT; }; +program = { + name = grub-mkdevicemap; + installdir = sbin; + mansection = 8; + + common = util/grub-mkdevicemap.c; + common = util/deviceiter.c; + common = util/devicemap.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + program = { name = grub-probe; installdir = sbin; diff --git a/docs/man/grub-mkdevicemap.h2m b/docs/man/grub-mkdevicemap.h2m new file mode 100644 index 000000000..96cd6ee72 --- /dev/null +++ b/docs/man/grub-mkdevicemap.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkdevicemap \- make a device map file automatically +[SEE ALSO] +.BR grub-probe (8) diff --git a/include/grub/util/deviceiter.h b/include/grub/util/deviceiter.h new file mode 100644 index 000000000..85374978c --- /dev/null +++ b/include/grub/util/deviceiter.h @@ -0,0 +1,14 @@ +#ifndef GRUB_DEVICEITER_MACHINE_UTIL_HEADER +#define GRUB_DEVICEITER_MACHINE_UTIL_HEADER 1 + +#include + +typedef int (*grub_util_iterate_devices_hook_t) (const char *name, + int is_floppy, void *data); + +void grub_util_iterate_devices (grub_util_iterate_devices_hook_t hook, + void *hook_data, int floppy_disks); +void grub_util_emit_devicemap_entry (FILE *fp, char *name, int is_floppy, + int *num_fd, int *num_hd); + +#endif /* ! GRUB_DEVICEITER_MACHINE_UTIL_HEADER */ diff --git a/util/deviceiter.c b/util/deviceiter.c new file mode 100644 index 000000000..a4971ef42 --- /dev/null +++ b/util/deviceiter.c @@ -0,0 +1,1021 @@ +/* deviceiter.c - iterate over system devices */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __linux__ +# if !defined(__GLIBC__) || \ + ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))) +/* Maybe libc doesn't have large file support. */ +# include /* _llseek */ +# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */ +# include /* ioctl */ +# ifndef HDIO_GETGEO +# define HDIO_GETGEO 0x0301 /* get device geometry */ +/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is + defined. */ +struct hd_geometry +{ + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; +# endif /* ! HDIO_GETGEO */ +# ifndef FLOPPY_MAJOR +# define FLOPPY_MAJOR 2 /* the major number for floppy */ +# endif /* ! FLOPPY_MAJOR */ +# ifndef MAJOR +# define MAJOR(dev) \ + ({ \ + unsigned long long __dev = (dev); \ + (unsigned) ((__dev >> 8) & 0xfff) \ + | ((unsigned int) (__dev >> 32) & ~0xfff); \ + }) +# endif /* ! MAJOR */ +# ifndef MINOR +# define MINOR(dev) \ + ({ \ + unsigned long long __dev = (dev); \ + (unsigned) (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff); \ + }) +# endif /* ! MINOR */ +# ifndef CDROM_GET_CAPABILITY +# define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */ +# endif /* ! CDROM_GET_CAPABILITY */ +# ifndef BLKGETSIZE +# define BLKGETSIZE _IO(0x12,96) /* return device size */ +# endif /* ! BLKGETSIZE */ + +#ifdef HAVE_DEVICE_MAPPER +# include +# pragma GCC diagnostic ignored "-Wcast-align" +#endif +#endif /* __linux__ */ + +/* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with + kFreeBSD-based non-FreeBSD systems (e.g. GNU/kFreeBSD) */ +#if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__) +# define __FreeBSD_kernel__ +#endif +#ifdef __FreeBSD_kernel__ + /* Obtain version of kFreeBSD headers */ +# include +# ifndef __FreeBSD_kernel_version +# define __FreeBSD_kernel_version __FreeBSD_version +# endif + + /* Runtime detection of kernel */ +# include +static int +get_kfreebsd_version (void) +{ + struct utsname uts; + int major; + int minor; + int v[2]; + + uname (&uts); + sscanf (uts.release, "%d.%d", &major, &minor); + + if (major >= 9) + major = 9; + if (major >= 5) + { + v[0] = minor/10; v[1] = minor%10; + } + else + { + v[0] = minor%10; v[1] = minor/10; + } + return major*100000+v[0]*10000+v[1]*1000; +} +#endif /* __FreeBSD_kernel__ */ + +#if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include /* ioctl */ +# include +# include /* CDIOCCLRDEBUG */ +# if defined(__FreeBSD_kernel__) +# include +# if __FreeBSD_kernel_version >= 500040 +# include +# endif +# endif /* __FreeBSD_kernel__ */ +#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ + +#ifdef HAVE_OPENDISK +# include +#endif /* HAVE_OPENDISK */ + +#ifdef __linux__ +/* Check if we have devfs support. */ +static int +have_devfs (void) +{ + struct stat st; + return stat ("/dev/.devfsd", &st) == 0; +} +#endif /* __linux__ */ + +/* These three functions are quite different among OSes. */ +static void +get_floppy_disk_name (char *name, int unit) +{ +#if defined(__linux__) + /* GNU/Linux */ + if (have_devfs ()) + sprintf (name, "/dev/floppy/%d", unit); + else + sprintf (name, "/dev/fd%d", unit); +#elif defined(__GNU__) + /* GNU/Hurd */ + sprintf (name, "/dev/fd%d", unit); +#elif defined(__FreeBSD_kernel__) + /* kFreeBSD */ + if (get_kfreebsd_version () >= 400000) + sprintf (name, "/dev/fd%d", unit); + else + sprintf (name, "/dev/rfd%d", unit); +#elif defined(__NetBSD__) + /* NetBSD */ + /* opendisk() doesn't work for floppies. */ + sprintf (name, "/dev/rfd%da", unit); +#elif defined(__OpenBSD__) + /* OpenBSD */ + sprintf (name, "/dev/rfd%dc", unit); +#elif defined(__QNXNTO__) + /* QNX RTP */ + sprintf (name, "/dev/fd%d", unit); +#elif defined(__CYGWIN__) + /* Cygwin */ + sprintf (name, "/dev/fd%d", unit); +#elif defined(__MINGW32__) + (void) unit; + *name = 0; +#else +# warning "BIOS floppy drives cannot be guessed in your operating system." + /* Set NAME to a bogus string. */ + *name = 0; +#endif +} + +static void +get_ide_disk_name (char *name, int unit) +{ +#if defined(__linux__) + /* GNU/Linux */ + sprintf (name, "/dev/hd%c", unit + 'a'); +#elif defined(__GNU__) + /* GNU/Hurd */ + sprintf (name, "/dev/hd%d", unit); +#elif defined(__FreeBSD_kernel__) + /* kFreeBSD */ + if (get_kfreebsd_version () >= 400000) + sprintf (name, "/dev/ad%d", unit); + else + sprintf (name, "/dev/rwd%d", unit); +#elif defined(__NetBSD__) && defined(HAVE_OPENDISK) + /* NetBSD */ + char shortname[16]; + int fd; + + sprintf (shortname, "wd%d", unit); + fd = opendisk (shortname, O_RDONLY, name, + 16, /* length of NAME */ + 0 /* char device */ + ); + close (fd); +#elif defined(__OpenBSD__) + /* OpenBSD */ + sprintf (name, "/dev/rwd%dc", unit); +#elif defined(__QNXNTO__) + /* QNX RTP */ + /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could + contain SCSI disks. */ + sprintf (name, "/dev/hd%d", unit); +#elif defined(__CYGWIN__) + /* Cygwin emulates all disks as /dev/sdX. */ + (void) unit; + *name = 0; +#elif defined(__MINGW32__) + sprintf (name, "//./PHYSICALDRIVE%d", unit); +#else +# warning "BIOS IDE drives cannot be guessed in your operating system." + /* Set NAME to a bogus string. */ + *name = 0; +#endif +} + +static void +get_scsi_disk_name (char *name, int unit) +{ +#if defined(__linux__) + /* GNU/Linux */ + sprintf (name, "/dev/sd%c", unit + 'a'); +#elif defined(__GNU__) + /* GNU/Hurd */ + sprintf (name, "/dev/sd%d", unit); +#elif defined(__FreeBSD_kernel__) + /* kFreeBSD */ + if (get_kfreebsd_version () >= 400000) + sprintf (name, "/dev/da%d", unit); + else + sprintf (name, "/dev/rda%d", unit); +#elif defined(__NetBSD__) && defined(HAVE_OPENDISK) + /* NetBSD */ + char shortname[16]; + int fd; + + sprintf (shortname, "sd%d", unit); + fd = opendisk (shortname, O_RDONLY, name, + 16, /* length of NAME */ + 0 /* char device */ + ); + close (fd); +#elif defined(__OpenBSD__) + /* OpenBSD */ + sprintf (name, "/dev/rsd%dc", unit); +#elif defined(__QNXNTO__) + /* QNX RTP */ + /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to + disable the detection of SCSI disks here. */ + *name = 0; +#elif defined(__CYGWIN__) + /* Cygwin emulates all disks as /dev/sdX. */ + sprintf (name, "/dev/sd%c", unit + 'a'); +#elif defined(__MINGW32__) + (void) unit; + *name = 0; +#else +# warning "BIOS SCSI drives cannot be guessed in your operating system." + /* Set NAME to a bogus string. */ + *name = 0; +#endif +} + +#ifdef __FreeBSD_kernel__ +static void +get_ada_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/ada%d", unit); +} + +static void +get_ataraid_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/ar%d", unit); +} + +static void +get_mfi_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/mfid%d", unit); +} + +static void +get_virtio_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/vtbd%d", unit); +} + +static void +get_xvd_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/xbd%d", unit); +} +#endif + +#ifdef __linux__ +static void +get_virtio_disk_name (char *name, int unit) +{ +#ifdef __sparc__ + sprintf (name, "/dev/vdisk%c", unit + 'a'); +#else + sprintf (name, "/dev/vd%c", unit + 'a'); +#endif +} + +static void +get_dac960_disk_name (char *name, int controller, int drive) +{ + sprintf (name, "/dev/rd/c%dd%d", controller, drive); +} + +static void +get_acceleraid_disk_name (char *name, int controller, int drive) +{ + sprintf (name, "/dev/rs/c%dd%d", controller, drive); +} + +static void +get_ataraid_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/ataraid/d%c", unit + '0'); +} + +static void +get_i2o_disk_name (char *name, char unit) +{ + sprintf (name, "/dev/i2o/hd%c", unit); +} + +static void +get_cciss_disk_name (char *name, int controller, int drive) +{ + sprintf (name, "/dev/cciss/c%dd%d", controller, drive); +} + +static void +get_ida_disk_name (char *name, int controller, int drive) +{ + sprintf (name, "/dev/ida/c%dd%d", controller, drive); +} + +static void +get_mmc_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/mmcblk%d", unit); +} + +static void +get_xvd_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/xvd%c", unit + 'a'); +} + +static void +get_nvme_disk_name (char *name, int controller, int namespace) +{ + sprintf (name, "/dev/nvme%dn%d", controller, namespace); +} +#endif + +static struct seen_device +{ + struct seen_device *next; + struct seen_device **prev; + const char *name; +} *seen; + +/* Check if DEVICE can be read. Skip any DEVICE that we have already seen. + If an error occurs, return zero, otherwise return non-zero. */ +static int +check_device_readable_unique (const char *device) +{ + char *real_device; + char buf[512]; + FILE *fp; + struct seen_device *seen_elt; + + /* If DEVICE is empty, just return error. */ + if (*device == 0) + return 0; + + /* Have we seen this device already? */ + real_device = canonicalize_file_name (device); + if (! real_device) + return 0; + if (grub_named_list_find (GRUB_AS_NAMED_LIST (seen), real_device)) + { + grub_dprintf ("deviceiter", "Already seen %s (%s)\n", + device, real_device); + goto fail; + } + + fp = fopen (device, "r"); + if (! fp) + { + switch (errno) + { +#ifdef ENOMEDIUM + case ENOMEDIUM: +# if 0 + /* At the moment, this finds only CDROMs, which can't be + read anyway, so leave it out. Code should be + reactivated if `removable disks' and CDROMs are + supported. */ + /* Accept it, it may be inserted. */ + return 1; +# endif + break; +#endif /* ENOMEDIUM */ + default: + /* Break case and leave. */ + break; + } + /* Error opening the device. */ + goto fail; + } + + /* Make sure CD-ROMs don't get assigned a BIOS disk number + before SCSI disks! */ +#ifdef __linux__ +# ifdef CDROM_GET_CAPABILITY + if (ioctl (fileno (fp), CDROM_GET_CAPABILITY, 0) >= 0) + goto fail; +# else /* ! CDROM_GET_CAPABILITY */ + /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */ + { + struct hd_geometry hdg; + struct stat st; + + if (fstat (fileno (fp), &st)) + goto fail; + + /* If it is a block device and isn't a floppy, check if HDIO_GETGEO + succeeds. */ + if (S_ISBLK (st.st_mode) + && MAJOR (st.st_rdev) != FLOPPY_MAJOR + && ioctl (fileno (fp), HDIO_GETGEO, &hdg)) + goto fail; + } +# endif /* ! CDROM_GET_CAPABILITY */ +#endif /* __linux__ */ + +#if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) +# ifdef CDIOCCLRDEBUG + if (ioctl (fileno (fp), CDIOCCLRDEBUG, 0) >= 0) + goto fail; +# endif /* CDIOCCLRDEBUG */ +#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */ + + /* Attempt to read the first sector. */ + if (fread (buf, 1, 512, fp) != 512) + { + fclose (fp); + goto fail; + } + + /* Remember that we've seen this device. */ + seen_elt = xmalloc (sizeof (*seen_elt)); + seen_elt->name = real_device; /* steal memory */ + grub_list_push (GRUB_AS_LIST_P (&seen), GRUB_AS_LIST (seen_elt)); + + fclose (fp); + return 1; + +fail: + free (real_device); + return 0; +} + +static void +clear_seen_devices (void) +{ + while (seen) + { + struct seen_device *seen_elt = seen; + seen = seen->next; + free (seen_elt); + } + seen = NULL; +} + +#ifdef __linux__ +struct device +{ + char *stable; + char *kernel; +}; + +/* Sort by the kernel name for preference since that most closely matches + older device.map files, but sort by stable by-id names as a fallback. + This is because /dev/disk/by-id/ usually has a few alternative + identifications of devices (e.g. ATA vs. SATA). + check_device_readable_unique will ensure that we only get one for any + given disk, but sort the list so that the choice of which one we get is + stable. */ +static int +compare_devices (const void *a, const void *b) +{ + const struct device *left = (const struct device *) a; + const struct device *right = (const struct device *) b; + + if (left->kernel && right->kernel) + { + int ret = strcmp (left->kernel, right->kernel); + if (ret) + return ret; + } + + return strcmp (left->stable, right->stable); +} +#endif /* __linux__ */ + +void +grub_util_iterate_devices (int (*hook) (const char *, int, void *), void *hook_data, + int floppy_disks) +{ + int i; + + clear_seen_devices (); + + /* Floppies. */ + for (i = 0; i < floppy_disks; i++) + { + char name[32]; + struct stat st; + + get_floppy_disk_name (name, i); + if (stat (name, &st) < 0) + break; + /* In floppies, write the map, whether check_device_readable_unique + succeeds or not, because the user just may not insert floppies. */ + if (hook (name, 1, hook_data)) + goto out; + } + +#ifdef __linux__ + { + DIR *dir = opendir ("/dev/disk/by-id"); + + if (dir) + { + struct dirent *entry; + struct device *devs; + size_t devs_len = 0, devs_max = 1024, dev; + + devs = xmalloc (devs_max * sizeof (*devs)); + + /* Dump all the directory entries into names, resizing if + necessary. */ + for (entry = readdir (dir); entry; entry = readdir (dir)) + { + /* Skip current and parent directory entries. */ + if (strcmp (entry->d_name, ".") == 0 || + strcmp (entry->d_name, "..") == 0) + continue; + /* Skip partition entries. */ + if (strstr (entry->d_name, "-part")) + continue; + /* Skip device-mapper entries; we'll handle the ones we want + later. */ + if (strncmp (entry->d_name, "dm-", sizeof ("dm-") - 1) == 0) + continue; + /* Skip RAID entries; they are handled by upper layers. */ + if (strncmp (entry->d_name, "md-", sizeof ("md-") - 1) == 0) + continue; + if (devs_len >= devs_max) + { + devs_max *= 2; + devs = xrealloc (devs, devs_max * sizeof (*devs)); + } + devs[devs_len].stable = + xasprintf ("/dev/disk/by-id/%s", entry->d_name); + devs[devs_len].kernel = + canonicalize_file_name (devs[devs_len].stable); + devs_len++; + } + + qsort (devs, devs_len, sizeof (*devs), &compare_devices); + + closedir (dir); + + /* Now add all the devices in sorted order. */ + for (dev = 0; dev < devs_len; ++dev) + { + if (check_device_readable_unique (devs[dev].stable)) + { + if (hook (devs[dev].stable, 0, hook_data)) + goto out; + } + free (devs[dev].stable); + free (devs[dev].kernel); + } + free (devs); + } + } + + if (have_devfs ()) + { + i = 0; + while (1) + { + char discn[32]; + char name[PATH_MAX]; + struct stat st; + + /* Linux creates symlinks "/dev/discs/discN" for convenience. + The way to number disks is the same as GRUB's. */ + sprintf (discn, "/dev/discs/disc%d", i++); + if (stat (discn, &st) < 0) + break; + + if (realpath (discn, name)) + { + strcat (name, "/disc"); + if (hook (name, 0, hook_data)) + goto out; + } + } + goto out; + } +#endif /* __linux__ */ + + /* IDE disks. */ + for (i = 0; i < 96; i++) + { + char name[16]; + + get_ide_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + +#ifdef __FreeBSD_kernel__ + /* IDE disks using ATA Direct Access driver. */ + if (get_kfreebsd_version () >= 800000) + for (i = 0; i < 96; i++) + { + char name[16]; + + get_ada_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* ATARAID disks. */ + for (i = 0; i < 8; i++) + { + char name[20]; + + get_ataraid_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* LSI MegaRAID SAS. */ + for (i = 0; i < 32; i++) + { + char name[20]; + + get_mfi_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* Virtio disks. */ + for (i = 0; i < 96; i++) + { + char name[16]; + + get_virtio_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* Xen virtual block devices. */ + for (i = 0; i < 96; i++) + { + char name[16]; + + get_xvd_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } +#endif + +#ifdef __linux__ + /* Virtio disks. */ + for (i = 0; i < 26; i++) + { + char name[16]; + + get_virtio_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* ATARAID disks. */ + for (i = 0; i < 8; i++) + { + char name[20]; + + get_ataraid_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* Xen virtual block devices. */ + for (i = 0; i < 26; i++) + { + char name[16]; + + get_xvd_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } +#endif /* __linux__ */ + + /* The rest is SCSI disks. */ + for (i = 0; i < 48; i++) + { + char name[16]; + + get_scsi_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + +#ifdef __linux__ + /* This is for DAC960 - we have + /dev/rd/cdp. + + DAC960 driver currently supports up to 8 controllers, 32 logical + drives, and 7 partitions. */ + { + int controller, drive; + + for (controller = 0; controller < 8; controller++) + { + for (drive = 0; drive < 15; drive++) + { + char name[24]; + + get_dac960_disk_name (name, controller, drive); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + } + + /* This is for Mylex Acceleraid - we have + /dev/rd/cdp. */ + { + int controller, drive; + + for (controller = 0; controller < 8; controller++) + { + for (drive = 0; drive < 15; drive++) + { + char name[24]; + + get_acceleraid_disk_name (name, controller, drive); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + } + + /* This is for CCISS - we have + /dev/cciss/cdp. */ + { + int controller, drive; + + for (controller = 0; controller < 3; controller++) + { + for (drive = 0; drive < 16; drive++) + { + char name[24]; + + get_cciss_disk_name (name, controller, drive); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + } + + /* This is for Compaq Intelligent Drive Array - we have + /dev/ida/cdp. */ + { + int controller, drive; + + for (controller = 0; controller < 3; controller++) + { + for (drive = 0; drive < 16; drive++) + { + char name[24]; + + get_ida_disk_name (name, controller, drive); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + } + + /* This is for I2O - we have /dev/i2o/hd */ + { + char unit; + + for (unit = 'a'; unit < 'f'; unit++) + { + char name[24]; + + get_i2o_disk_name (name, unit); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + + /* MultiMediaCard (MMC). */ + for (i = 0; i < 10; i++) + { + char name[16]; + + get_mmc_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + + /* This is for standard NVMe controllers + /dev/nvmenp. No idea about + actual limits of how many controllers a system can have and/or + how many namespace that would be, 10 for now. */ + { + int controller, namespace; + + for (controller = 0; controller < 10; controller++) + { + for (namespace = 0; namespace < 10; namespace++) + { + char name[16]; + + get_nvme_disk_name (name, controller, namespace); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + } + } + +# ifdef HAVE_DEVICE_MAPPER +# define dmraid_check(cond, ...) \ + if (! (cond)) \ + { \ + grub_dprintf ("deviceiter", __VA_ARGS__); \ + goto dmraid_end; \ + } + + /* DM-RAID. */ + if (grub_device_mapper_supported ()) + { + struct dm_tree *tree = NULL; + struct dm_task *task = NULL; + struct dm_names *names = NULL; + unsigned int next = 0; + void *top_handle, *second_handle; + struct dm_tree_node *root, *top, *second; + + /* Build DM tree for all devices. */ + tree = dm_tree_create (); + dmraid_check (tree, "dm_tree_create failed\n"); + task = dm_task_create (DM_DEVICE_LIST); + dmraid_check (task, "dm_task_create failed\n"); + dmraid_check (dm_task_run (task), "dm_task_run failed\n"); + names = dm_task_get_names (task); + dmraid_check (names, "dm_task_get_names failed\n"); + dmraid_check (names->dev, "No DM devices found\n"); + do + { + names = (struct dm_names *) ((char *) names + next); + dmraid_check (dm_tree_add_dev (tree, MAJOR (names->dev), + MINOR (names->dev)), + "dm_tree_add_dev (%s) failed\n", names->name); + next = names->next; + } + while (next); + + /* Walk the second-level children of the inverted tree; that is, devices + which are directly composed of non-DM devices such as hard disks. + This class includes all DM-RAID disks and excludes all DM-RAID + partitions. */ + root = dm_tree_find_node (tree, 0, 0); + top_handle = NULL; + top = dm_tree_next_child (&top_handle, root, 1); + while (top) + { + second_handle = NULL; + second = dm_tree_next_child (&second_handle, top, 1); + while (second) + { + const char *node_name, *node_uuid; + char *name; + + node_name = dm_tree_node_get_name (second); + dmraid_check (node_name, "dm_tree_node_get_name failed\n"); + node_uuid = dm_tree_node_get_uuid (second); + dmraid_check (node_uuid, "dm_tree_node_get_uuid failed\n"); + if (strstr (node_uuid, "DMRAID-") == 0) + { + grub_dprintf ("deviceiter", "%s is not DM-RAID\n", node_name); + goto dmraid_next_child; + } + + name = xasprintf ("/dev/mapper/%s", node_name); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + { + free (name); + if (task) + dm_task_destroy (task); + if (tree) + dm_tree_free (tree); + goto out; + } + } + free (name); + +dmraid_next_child: + second = dm_tree_next_child (&second_handle, top, 1); + } + top = dm_tree_next_child (&top_handle, root, 1); + } + +dmraid_end: + if (task) + dm_task_destroy (task); + if (tree) + dm_tree_free (tree); + } +# endif /* HAVE_DEVICE_MAPPER */ +#endif /* __linux__ */ + +out: + clear_seen_devices (); +} diff --git a/util/devicemap.c b/util/devicemap.c new file mode 100644 index 000000000..c61864420 --- /dev/null +++ b/util/devicemap.c @@ -0,0 +1,13 @@ +#include + +#include + +void +grub_util_emit_devicemap_entry (FILE *fp, char *name, int is_floppy, + int *num_fd, int *num_hd) +{ + if (is_floppy) + fprintf (fp, "(fd%d)\t%s\n", (*num_fd)++, name); + else + fprintf (fp, "(hd%d)\t%s\n", (*num_hd)++, name); +} diff --git a/util/grub-mkdevicemap.c b/util/grub-mkdevicemap.c new file mode 100644 index 000000000..c4bbdbf69 --- /dev/null +++ b/util/grub-mkdevicemap.c @@ -0,0 +1,181 @@ +/* grub-mkdevicemap.c - make a device map file automatically */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE 1 +#include + +#include "progname.h" + +/* Context for make_device_map. */ +struct make_device_map_ctx +{ + FILE *fp; + int num_fd; + int num_hd; +}; + +/* Helper for make_device_map. */ +static int +process_device (const char *name, int is_floppy, void *data) +{ + struct make_device_map_ctx *ctx = data; + + grub_util_emit_devicemap_entry (ctx->fp, (char *) name, + is_floppy, &ctx->num_fd, &ctx->num_hd); + return 0; +} + +static void +make_device_map (const char *device_map, int floppy_disks) +{ + struct make_device_map_ctx ctx = { + .num_fd = 0, + .num_hd = 0 + }; + + if (strcmp (device_map, "-") == 0) + ctx.fp = stdout; + else + ctx.fp = fopen (device_map, "w"); + + if (! ctx.fp) + grub_util_error (_("cannot open %s"), device_map); + + grub_util_iterate_devices (process_device, &ctx, floppy_disks); + + if (ctx.fp != stdout) + fclose (ctx.fp); +} + +static struct option options[] = + { + {"device-map", required_argument, 0, 'm'}, + {"probe-second-floppy", no_argument, 0, 's'}, + {"no-floppy", no_argument, 0, 'n'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + +static void +usage (int status) +{ + if (status) + fprintf (stderr, + _("Try `%s --help' for more information.\n"), program_name); + else + printf (_("\ +Usage: %s [OPTION]...\n\ +\n\ +Generate a device map file automatically.\n\ +\n\ + -n, --no-floppy do not probe any floppy drive\n\ + -s, --probe-second-floppy probe the second floppy drive\n\ + -m, --device-map=FILE use FILE as the device map [default=%s]\n\ + -h, --help display this message and exit\n\ + -V, --version print version information and exit\n\ + -v, --verbose print verbose messages\n\ +\n\ +Report bugs to <%s>.\n\ +"), program_name, + DEFAULT_DEVICE_MAP, PACKAGE_BUGREPORT); + + exit (status); +} + +int +main (int argc, char *argv[]) +{ + char *dev_map = 0; + int floppy_disks = 1; + + grub_util_host_init (&argc, &argv); + + /* Check for options. */ + while (1) + { + int c = getopt_long (argc, argv, "snm:r:hVv", options, 0); + + if (c == -1) + break; + else + switch (c) + { + case 'm': + if (dev_map) + free (dev_map); + + dev_map = xstrdup (optarg); + break; + + case 'n': + floppy_disks = 0; + break; + + case 's': + floppy_disks = 2; + break; + + case 'h': + usage (0); + break; + + case 'V': + printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION); + return 0; + + case 'v': + verbosity++; + break; + + default: + usage (1); + break; + } + } + + if (verbosity > 1) + grub_env_set ("debug", "all"); + + make_device_map (dev_map ? : DEFAULT_DEVICE_MAP, floppy_disks); + + free (dev_map); + + return 0; +} From d6b00c624ad11c65fa670be90fa10ab391a1122b Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:02 +0000 Subject: [PATCH 02/52] Silence error messages when translations are unavailable Bug: https://savannah.gnu.org/bugs/?35880 Forwarded: https://savannah.gnu.org/bugs/?35880 Last-Update: 2013-11-14 Patch-Name: gettext_quiet.patch --- grub-core/gettext/gettext.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 4880cefe3..3098e8918 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -427,6 +427,11 @@ grub_gettext_init_ext (struct grub_gettext_context *ctx, if (locale[0] == 'e' && locale[1] == 'n' && (locale[2] == '\0' || locale[2] == '_')) grub_errno = err = GRUB_ERR_NONE; + + /* If no translations are available, fall back to untranslated text. */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + grub_errno = err = GRUB_ERR_NONE; + return err; } From bc5248a3a6a60361c925393aa6e3de064d374bf1 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:03 +0000 Subject: [PATCH 03/52] Bail out if trying to run grub-mkconfig during upgrade to 2.00 Since files in /etc/grub.d/ are conffiles, they are not put in place until grub-common is configured, meaning that they may be out of sync with the parts of grub-mkconfig that reside in /usr/. In GRUB 1.99, /etc/grub.d/00_header contained a reference to ${GRUB_PREFIX}/video.lst. This and other code from 1.99 breaks with 2.00's grub-mkconfig. Deferring this to when grub-PLATFORM.postinst eventually runs is safe and avoids this problem. Forwarded: no Last-Update: 2013-12-25 Patch-Name: mkconfig_mid_upgrade.patch --- util/grub-mkconfig.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 54fbe5288..99ba7acba 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -102,6 +102,13 @@ do esac done +if fgrep -qs '${GRUB_PREFIX}/video.lst' "${grub_mkconfig_dir}/00_header"; then + echo "GRUB >= 2.00 has been unpacked but not yet configured." >&2 + echo "grub-mkconfig will not work until the upgrade is complete." >&2 + echo "It should run later as part of configuring the new GRUB packages." >&2 + exit 0 +fi + if [ "x$EUID" = "x" ] ; then EUID=`id -u` fi From bf7aea0b065eb3ef2833bc4cf955505804d2c563 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:05 +0000 Subject: [PATCH 04/52] Fall back to i386-pc if booted using EFI but -efi is missing It may be possible, particularly in recovery situations, to be booted using EFI on x86 when only the i386-pc target is installed. There's nothing actually stopping us installing i386-pc from an EFI environment, and it's better than returning a confusing error. Forwarded: no Last-Update: 2013-12-20 Patch-Name: install_efi_fallback.patch --- grub-core/osdep/linux/platform.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/grub-core/osdep/linux/platform.c b/grub-core/osdep/linux/platform.c index 775b6c031..35f1bcc0e 100644 --- a/grub-core/osdep/linux/platform.c +++ b/grub-core/osdep/linux/platform.c @@ -19,10 +19,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -113,11 +115,24 @@ grub_install_get_default_x86_platform (void) grub_util_info ("Looking for /sys/firmware/efi .."); if (is_not_empty_directory ("/sys/firmware/efi")) { + const char *pkglibdir = grub_util_get_pkglibdir (); + const char *platform; + char *pd; + int found; + grub_util_info ("...found"); if (read_platform_size() == 64) - return "x86_64-efi"; + platform = "x86_64-efi"; else - return "i386-efi"; + platform = "i386-efi"; + + pd = grub_util_path_concat (2, pkglibdir, platform); + found = grub_util_is_directory (pd); + free (pd); + if (found) + return platform; + else + grub_util_info ("... but %s platform not available", platform); } grub_util_info ("... not found. Looking for /proc/device-tree .."); From c8164bdd843b7f1c002f0c0419decadec8c74a94 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:06 +0000 Subject: [PATCH 05/52] "single" -> "recovery" when friendly-recovery is installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If configured with --enable-ubuntu-recovery, also set nomodeset for recovery mode, and disable 'set gfxpayload=keep' even if the system normally supports it. See https://launchpad.net/ubuntu/+spec/desktop-o-xorg-tools-and-processes. Author: Stéphane Graber Forwarded: no Last-Update: 2013-12-25 Patch-Name: mkconfig_ubuntu_recovery.patch --- configure.ac | 11 +++++++++++ util/grub.d/10_linux.in | 16 ++++++++++++++-- util/grub.d/30_os-prober.in | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index edd184154..d4978ad5d 100644 --- a/configure.ac +++ b/configure.ac @@ -1819,6 +1819,17 @@ fi AC_SUBST([LIBZFS]) AC_SUBST([LIBNVPAIR]) +AC_ARG_ENABLE([ubuntu-recovery], + [AS_HELP_STRING([--enable-ubuntu-recovery], + [adjust boot options for the Ubuntu recovery mode (default=no)])], + [], [enable_ubuntu_recovery=no]) +if test x"$enable_ubuntu_recovery" = xyes ; then + UBUNTU_RECOVERY=1 +else + UBUNTU_RECOVERY=0 +fi +AC_SUBST([UBUNTU_RECOVERY]) + LIBS="" AC_SUBST([FONT_SOURCE]) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 5ff76f59a..37b5180f3 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -20,6 +20,7 @@ set -e prefix="@prefix@" exec_prefix="@exec_prefix@" datarootdir="@datarootdir@" +ubuntu_recovery="@UBUNTU_RECOVERY@" . "$pkgdatadir/grub-mkconfig_lib" @@ -74,6 +75,15 @@ esac title_correction_code= +if [ -x /lib/recovery-mode/recovery-menu ]; then + GRUB_CMDLINE_LINUX_RECOVERY=recovery +else + GRUB_CMDLINE_LINUX_RECOVERY=single +fi +if [ "$ubuntu_recovery" = 1 ]; then + GRUB_CMDLINE_LINUX_RECOVERY="$GRUB_CMDLINE_LINUX_RECOVERY nomodeset" +fi + linux_entry () { os="$1" @@ -113,7 +123,9 @@ linux_entry () if [ "x$GRUB_GFXPAYLOAD_LINUX" != xtext ]; then echo " load_video" | sed "s/^/$submenu_indentation/" fi - echo " set gfxpayload=$GRUB_GFXPAYLOAD_LINUX" | sed "s/^/$submenu_indentation/" + if [ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]; then + echo " set gfxpayload=$GRUB_GFXPAYLOAD_LINUX" | sed "s/^/$submenu_indentation/" + fi fi echo " insmod gzio" | sed "s/^/$submenu_indentation/" @@ -243,7 +255,7 @@ while [ "x$list" != "x" ] ; do "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then linux_entry "${OS}" "${version}" recovery \ - "single ${GRUB_CMDLINE_LINUX}" + "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}" fi list=`echo $list | tr ' ' '\n' | fgrep -vx "$linux" | tr '\n' ' '` diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 515a68c7a..775ceb2e0 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -220,7 +220,7 @@ EOF fi onstr="$(gettext_printf "(on %s)" "${DEVICE}")" - recovery_params="$(echo "${LPARAMS}" | grep single)" || true + recovery_params="$(echo "${LPARAMS}" | grep 'single\|recovery')" || true counter=1 while echo "$used_osprober_linux_ids" | grep 'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id' > /dev/null; do counter=$((counter+1)); From 03ca727beff305cee6e925af0ec9d37f8f5046f8 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:07 +0000 Subject: [PATCH 06/52] Prefer translations from Ubuntu language packs if available Bug-Ubuntu: https://bugs.launchpad.net/bugs/537998 Forwarded: not-needed Last-Update: 2013-12-25 Patch-Name: install_locale_langpack.patch --- util/grub-install-common.c | 40 +++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 452b230da..c9d5e3d0f 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -595,17 +595,25 @@ get_localedir (void) } static void -copy_locales (const char *dstd) +copy_locales (const char *dstd, int langpack) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; const char *locale_dir = get_localedir (); + char *dir; - d = grub_util_fd_opendir (locale_dir); + if (langpack) + dir = xasprintf ("%s-langpack", locale_dir); + else + dir = xstrdup (locale_dir); + + d = grub_util_fd_opendir (dir); if (!d) { - grub_util_warn (_("cannot open directory `%s': %s"), - locale_dir, grub_util_fd_strerror ()); + if (!langpack) + grub_util_warn (_("cannot open directory `%s': %s"), + dir, grub_util_fd_strerror ()); + free (dir); return; } @@ -622,14 +630,14 @@ copy_locales (const char *dstd) if (ext && (grub_strcmp (ext, ".mo") == 0 || grub_strcmp (ext, ".gmo") == 0)) { - srcf = grub_util_path_concat (2, locale_dir, de->d_name); + srcf = grub_util_path_concat (2, dir, de->d_name); dstf = grub_util_path_concat (2, dstd, de->d_name); ext = grub_strrchr (dstf, '.'); grub_strcpy (ext, ".mo"); } else { - srcf = grub_util_path_concat_ext (4, locale_dir, de->d_name, + srcf = grub_util_path_concat_ext (4, dir, de->d_name, "LC_MESSAGES", PACKAGE, ".mo"); dstf = grub_util_path_concat_ext (2, dstd, de->d_name, ".mo"); } @@ -638,6 +646,7 @@ copy_locales (const char *dstd) free (dstf); } grub_util_fd_closedir (d); + free (dir); } static struct @@ -793,12 +802,14 @@ grub_install_copy_files (const char *src, { char *srcd = grub_util_path_concat (2, src, "po"); copy_by_ext (srcd, dst_locale, ".mo", 0); - copy_locales (dst_locale); + copy_locales (dst_locale, 0); + copy_locales (dst_locale, 1); free (srcd); } else { const char *locale_dir = get_localedir (); + char *locale_langpack_dir = xasprintf ("%s-langpack", locale_dir); for (i = 0; i < install_locales.n_entries; i++) { @@ -816,6 +827,19 @@ grub_install_copy_files (const char *src, continue; } free (srcf); + srcf = grub_util_path_concat_ext (4, + locale_langpack_dir, + install_locales.entries[i], + "LC_MESSAGES", + PACKAGE, + ".mo"); + if (grub_install_compress_file (srcf, dstf, 0)) + { + free (srcf); + free (dstf); + continue; + } + free (srcf); srcf = grub_util_path_concat_ext (4, locale_dir, install_locales.entries[i], @@ -831,6 +855,8 @@ grub_install_copy_files (const char *src, grub_util_error (_("cannot find locale `%s'"), install_locales.entries[i]); } + + free (locale_langpack_dir); } if (install_themes.is_default) From 7ef88db58a928e4abc6588b8812bce044e2af7a6 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:08 +0000 Subject: [PATCH 07/52] Avoid getting confused by inaccessible loop device backing paths Bug-Ubuntu: https://bugs.launchpad.net/bugs/938724 Forwarded: no Last-Update: 2013-12-20 Patch-Name: mkconfig_nonexistent_loopback.patch --- util/grub-mkconfig_lib.in | 2 +- util/grub.d/30_os-prober.in | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 9ee60d5fa..2c9743052 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -143,7 +143,7 @@ prepare_grub_to_access_device () *) loop_device="$1" shift - set -- `"${grub_probe}" --target=device "${loop_file}"` "$@" + set -- `"${grub_probe}" --target=device "${loop_file}"` "$@" || return 0 ;; esac ;; diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 775ceb2e0..b7e1147c4 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -219,6 +219,11 @@ EOF LINITRD="${LINITRD#/boot}" fi + if [ -z "${prepare_boot_cache}" ]; then + prepare_boot_cache="$(prepare_grub_to_access_device ${LBOOT} | grub_add_tab)" + [ "${prepare_boot_cache}" ] || continue + fi + onstr="$(gettext_printf "(on %s)" "${DEVICE}")" recovery_params="$(echo "${LPARAMS}" | grep 'single\|recovery')" || true counter=1 @@ -230,10 +235,6 @@ EOF fi used_osprober_linux_ids="$used_osprober_linux_ids 'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id'" - if [ -z "${prepare_boot_cache}" ]; then - prepare_boot_cache="$(prepare_grub_to_access_device ${LBOOT} | grub_add_tab)" - fi - if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xy ]; then cat << EOF menuentry '$(echo "$OS $onstr" | grub_quote)' $CLASS --class gnu-linux --class gnu --class os \$menuentry_id_option 'osprober-gnulinux-simple-$boot_device_id' { From 62f86bcd111ed41ffa53d948147f4098033a5e71 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 13 Jan 2014 12:13:09 +0000 Subject: [PATCH 08/52] Don't permit loading modules on UEFI secure boot Author: Colin Watson Origin: vendor, http://pkgs.fedoraproject.org/cgit/grub2.git/tree/grub-2.00-no-insmod-on-sb.patch Forwarded: no Last-Update: 2013-12-25 Patch-Name: no_insmod_on_sb.patch --- grub-core/kern/dl.c | 13 +++++++++++++ grub-core/kern/efi/efi.c | 28 ++++++++++++++++++++++++++++ include/grub/efi/efi.h | 1 + 3 files changed, 42 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index e394cd96f..b6535155b 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -38,6 +38,10 @@ #define GRUB_MODULES_MACHINE_READONLY #endif +#ifdef GRUB_MACHINE_EFI +#include +#endif + #pragma GCC diagnostic ignored "-Wcast-align" @@ -686,6 +690,15 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; +#ifdef GRUB_MACHINE_EFI + if (grub_efi_secure_boot ()) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + "Secure Boot forbids loading module from %s", filename); + return 0; + } +#endif + grub_boot_time ("Loading module %s", filename); file = grub_file_open (filename); diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index d467785fc..b677bd280 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -264,6 +264,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return NULL; } +grub_efi_boolean_t +grub_efi_secure_boot (void) +{ + grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_size_t datasize; + char *secure_boot = NULL; + char *setup_mode = NULL; + grub_efi_boolean_t ret = 0; + + secure_boot = grub_efi_get_variable ("SecureBoot", &efi_var_guid, &datasize); + + if (datasize != 1 || !secure_boot) + goto out; + + setup_mode = grub_efi_get_variable ("SetupMode", &efi_var_guid, &datasize); + + if (datasize != 1 || !setup_mode) + goto out; + + if (*secure_boot && !*setup_mode) + ret = 1; + + out: + grub_free (secure_boot); + grub_free (setup_mode); + return ret; +} + #pragma GCC diagnostic ignored "-Wcast-align" /* Search the mods section from the PE32/PE32+ image. This code uses diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index e9c601f34..5e87950fd 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -73,6 +73,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var, const grub_efi_guid_t *guid, void *data, grub_size_t datasize); +grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); From 0c9b39ffe82f8946dce58ff0e45fcf122ed64fe3 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:10 +0000 Subject: [PATCH 09/52] Read /etc/default/grub.d/*.cfg after /etc/default/grub Bug-Ubuntu: https://bugs.launchpad.net/bugs/901600 Forwarded: no Last-Update: 2014-01-28 Patch-Name: default_grub_d.patch --- grub-core/osdep/unix/config.c | 114 +++++++++++++++++++++++++++------- util/grub-mkconfig.in | 5 ++ 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c index 65effa9f3..5478030fd 100644 --- a/grub-core/osdep/unix/config.c +++ b/grub-core/osdep/unix/config.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -61,13 +63,27 @@ grub_util_get_localedir (void) return LOCALEDIR; } +struct cfglist +{ + struct cfglist *next; + struct cfglist *prev; + char *path; +}; + void grub_util_load_config (struct grub_util_config *cfg) { pid_t pid; const char *argv[4]; - char *script, *ptr; + char *script = NULL, *ptr; const char *cfgfile, *iptr; + char *cfgdir; + grub_util_fd_dir_t d; + struct cfglist *cfgpaths = NULL, *cfgpath, *next_cfgpath; + int num_cfgpaths = 0; + size_t len_cfgpaths = 0; + char **sorted_cfgpaths = NULL; + int i; FILE *f = NULL; int fd; const char *v; @@ -83,29 +99,75 @@ grub_util_load_config (struct grub_util_config *cfg) cfg->grub_distributor = xstrdup (v); cfgfile = grub_util_get_config_filename (); - if (!grub_util_is_regular (cfgfile)) - return; + if (grub_util_is_regular (cfgfile)) + { + ++num_cfgpaths; + len_cfgpaths += strlen (cfgfile) * 4 + sizeof (". ''; ") - 1; + } + + cfgdir = xasprintf ("%s.d", cfgfile); + d = grub_util_fd_opendir (cfgdir); + if (d) + { + grub_util_fd_dirent_t de; + + while ((de = grub_util_fd_readdir (d))) + { + const char *ext = strrchr (de->d_name, '.'); + + if (!ext || strcmp (ext, ".cfg") != 0) + continue; + + cfgpath = xmalloc (sizeof (*cfgpath)); + cfgpath->path = grub_util_path_concat (2, cfgdir, de->d_name); + grub_list_push (GRUB_AS_LIST_P (&cfgpaths), GRUB_AS_LIST (cfgpath)); + ++num_cfgpaths; + len_cfgpaths += strlen (cfgpath->path) * 4 + sizeof (". ''; ") - 1; + } + grub_util_fd_closedir (d); + } + + if (num_cfgpaths == 0) + goto out; + + sorted_cfgpaths = xmalloc (num_cfgpaths * sizeof (*sorted_cfgpaths)); + i = 0; + if (grub_util_is_regular (cfgfile)) + sorted_cfgpaths[i++] = xstrdup (cfgfile); + FOR_LIST_ELEMENTS_SAFE (cfgpath, next_cfgpath, cfgpaths) + { + sorted_cfgpaths[i++] = cfgpath->path; + free (cfgpath); + } + assert (i == num_cfgpaths); + qsort (sorted_cfgpaths + 1, num_cfgpaths - 1, sizeof (*sorted_cfgpaths), + (int (*) (const void *, const void *)) strcmp); argv[0] = "sh"; argv[1] = "-c"; - script = xmalloc (4 * strlen (cfgfile) + 300); + script = xmalloc (len_cfgpaths + 300); ptr = script; - memcpy (ptr, ". '", 3); - ptr += 3; - for (iptr = cfgfile; *iptr; iptr++) + for (i = 0; i < num_cfgpaths; i++) { - if (*iptr == '\\') + memcpy (ptr, ". '", 3); + ptr += 3; + for (iptr = sorted_cfgpaths[i]; *iptr; iptr++) { - memcpy (ptr, "'\\''", 4); - ptr += 4; - continue; + if (*iptr == '\\') + { + memcpy (ptr, "'\\''", 4); + ptr += 4; + continue; + } + *ptr++ = *iptr; } - *ptr++ = *iptr; + memcpy (ptr, "'; ", 3); + ptr += 3; } - strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " + strcpy (ptr, "printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\""); argv[2] = script; @@ -125,15 +187,25 @@ grub_util_load_config (struct grub_util_config *cfg) waitpid (pid, NULL, 0); } if (f) - return; + goto out; - f = grub_util_fopen (cfgfile, "r"); - if (f) + for (i = 0; i < num_cfgpaths; i++) { - grub_util_parse_config (f, cfg, 0); - fclose (f); + f = grub_util_fopen (sorted_cfgpaths[i], "r"); + if (f) + { + grub_util_parse_config (f, cfg, 0); + fclose (f); + } + else + grub_util_warn (_("cannot open configuration file `%s': %s"), + cfgfile, strerror (errno)); } - else - grub_util_warn (_("cannot open configuration file `%s': %s"), - cfgfile, strerror (errno)); + +out: + free (script); + for (i = 0; i < num_cfgpaths; i++) + free (sorted_cfgpaths[i]); + free (sorted_cfgpaths); + free (cfgdir); } diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 99ba7acba..b4f0011b5 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -157,6 +157,11 @@ fi if test -f ${sysconfdir}/default/grub ; then . ${sysconfdir}/default/grub fi +for x in ${sysconfdir}/default/grub.d/*.cfg ; do + if [ -e "${x}" ]; then + . "${x}" + fi +done # XXX: should this be deprecated at some point? if [ "x${GRUB_TERMINAL}" != "x" ] ; then From 6c496f2a48b653c659bc54bab1a6548282750d1e Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:11 +0000 Subject: [PATCH 10/52] Blacklist 1440x900x32 from VBE preferred mode handling Bug-Ubuntu: https://bugs.launchpad.net/bugs/701111 Forwarded: no Last-Update: 2013-11-14 Patch-Name: blacklist_1440x900x32.patch --- grub-core/video/i386/pc/vbe.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/grub-core/video/i386/pc/vbe.c b/grub-core/video/i386/pc/vbe.c index b7f911926..4b1bd7d5e 100644 --- a/grub-core/video/i386/pc/vbe.c +++ b/grub-core/video/i386/pc/vbe.c @@ -1054,6 +1054,15 @@ grub_video_vbe_setup (unsigned int width, unsigned int height, || vbe_mode_info.y_resolution > height) /* Resolution exceeds that of preferred mode. */ continue; + + /* Blacklist 1440x900x32 from preferred mode handling until a + better solution is available. This mode causes problems on + many Thinkpads. See: + https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/701111 */ + if (vbe_mode_info.x_resolution == 1440 && + vbe_mode_info.y_resolution == 900 && + vbe_mode_info.bits_per_pixel == 32) + continue; } else { From 78b220997f3f16b5d3bd351e5573fc2779cc2cfd Mon Sep 17 00:00:00 2001 From: Steve Langasek Date: Mon, 13 Jan 2014 12:13:12 +0000 Subject: [PATCH 11/52] Output a menu entry for firmware setup on UEFI FastBoot systems Forwarded: no Last-Update: 2015-09-04 Patch-Name: uefi_firmware_setup.patch --- Makefile.util.def | 6 +++++ util/grub.d/30_uefi-firmware.in | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 util/grub.d/30_uefi-firmware.in diff --git a/Makefile.util.def b/Makefile.util.def index c939deb92..168acbe59 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -509,6 +509,12 @@ script = { installdir = grubconf; }; +script = { + name = '30_uefi-firmware'; + common = util/grub.d/30_uefi-firmware.in; + installdir = grubconf; +}; + script = { name = '40_custom'; common = util/grub.d/40_custom.in; diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in new file mode 100644 index 000000000..3c9f533d8 --- /dev/null +++ b/util/grub.d/30_uefi-firmware.in @@ -0,0 +1,46 @@ +#! /bin/sh +set -e + +# grub-mkconfig helper script. +# Copyright (C) 2012 Free Software Foundation, Inc. +# +# GRUB is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GRUB is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GRUB. If not, see . + +prefix="@prefix@" +exec_prefix="@exec_prefix@" +datarootdir="@datarootdir@" + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "@datadir@/@PACKAGE@/grub-mkconfig_lib" + +efi_vars_dir=/sys/firmware/efi/vars +EFI_GLOBAL_VARIABLE=8be4df61-93ca-11d2-aa0d-00e098032b8c +OsIndications="$efi_vars_dir/OsIndicationsSupported-$EFI_GLOBAL_VARIABLE/data" + +if [ -e "$OsIndications" ] && \ + [ "$(( $(printf 0x%x \'"$(cat $OsIndications | cut -b1)") & 1 ))" = 1 ]; then + LABEL="System setup" + + gettext_printf "Adding boot menu entry for EFI firmware configuration\n" >&2 + + onstr="$(gettext_printf "(on %s)" "${DEVICE}")" + + cat << EOF +menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { + fwsetup +} +EOF +fi From 00e15161a27958f7f0728c3fb36c0604a5b0aeb9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 13 Jan 2014 12:13:14 +0000 Subject: [PATCH 12/52] Remove GNU/Linux from default distributor string for Ubuntu Ubuntu is called "Ubuntu", not "Ubuntu GNU/Linux". Author: Colin Watson Author: Harald Sitter Forwarded: not-needed Last-Update: 2013-12-25 Patch-Name: mkconfig_ubuntu_distributor.patch --- util/grub.d/10_linux.in | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 37b5180f3..37c248c54 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -32,7 +32,14 @@ CLASS="--class gnu-linux --class gnu --class os" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS=GNU/Linux else - OS="${GRUB_DISTRIBUTOR} GNU/Linux" + case ${GRUB_DISTRIBUTOR} in + Ubuntu|Kubuntu) + OS="${GRUB_DISTRIBUTOR}" + ;; + *) + OS="${GRUB_DISTRIBUTOR} GNU/Linux" + ;; + esac CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" fi From 72da0de7449b8eada6a23f9a5e4b48f07955a368 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 13 Jan 2014 12:13:15 +0000 Subject: [PATCH 13/52] Add "linuxefi" loader which avoids ExitBootServices Origin: vendor, http://pkgs.fedoraproject.org/cgit/grub2.git/tree/grub2-linuxefi.patch Forwarded: no Last-Update: 2016-09-19 Patch-Name: linuxefi.patch --- grub-core/Makefile.core.def | 8 + grub-core/kern/efi/mm.c | 32 +++ grub-core/loader/i386/efi/linux.c | 371 ++++++++++++++++++++++++++++++ include/grub/efi/efi.h | 3 + include/grub/i386/linux.h | 1 + 5 files changed, 415 insertions(+) create mode 100644 grub-core/loader/i386/efi/linux.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 2dfa22a92..68e47a727 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1733,6 +1733,14 @@ module = { enable = x86_64_efi; }; +module = { + name = linuxefi; + efi = loader/i386/efi/linux.c; + efi = lib/cmdline.c; + enable = i386_efi; + enable = x86_64_efi; +}; + module = { name = chain; efi = loader/efi/chainloader.c; diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 20a47aaf5..efb15cc1b 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -49,6 +49,38 @@ static grub_efi_uintn_t finish_desc_size; static grub_efi_uint32_t finish_desc_version; int grub_efi_is_finished = 0; +/* Allocate pages below a specified address */ +void * +grub_efi_allocate_pages_max (grub_efi_physical_address_t max, + grub_efi_uintn_t pages) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address = max; + + if (max > 0xffffffff) + return 0; + + b = grub_efi_system_table->boot_services; + status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = max; + status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + return 0; + } + + return (void *) ((grub_addr_t) address); +} + /* Allocate pages. Return the pointer to the first of allocated pages. */ void * grub_efi_allocate_pages (grub_efi_physical_address_t address, diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c new file mode 100644 index 000000000..b622b3b71 --- /dev/null +++ b/grub-core/loader/i386/efi/linux.c @@ -0,0 +1,371 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; +static int loaded; +static void *kernel_mem; +static grub_uint64_t kernel_size; +static grub_uint8_t *initrd_mem; +static grub_uint32_t handover_offset; +struct linux_kernel_params *params; +static char *linux_cmdline; + +#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + +#define SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } + +struct grub_efi_shim_lock +{ + grub_efi_status_t (*verify) (void *buffer, grub_uint32_t size); +}; +typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; + +static grub_efi_boolean_t +grub_linuxefi_secure_validate (void *data, grub_uint32_t size) +{ + grub_efi_guid_t guid = SHIM_LOCK_GUID; + grub_efi_shim_lock_t *shim_lock; + + shim_lock = grub_efi_locate_protocol(&guid, NULL); + + if (!shim_lock) + return 1; + + if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) + return 1; + + return 0; +} + +typedef void(*handover_func)(void *, grub_efi_system_table_t *, struct linux_kernel_params *); + +static grub_err_t +grub_linuxefi_boot (void) +{ + handover_func hf; + int offset = 0; + +#ifdef __x86_64__ + offset = 512; +#endif + + hf = (handover_func)((char *)kernel_mem + handover_offset + offset); + + asm volatile ("cli"); + + hf (grub_efi_image_handle, grub_efi_system_table, params); + + /* Not reached */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_linuxefi_unload (void) +{ + grub_dl_unref (my_mod); + loaded = 0; + if (initrd_mem) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(params->ramdisk_size)); + if (linux_cmdline) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(params->cmdline_size + 1)); + if (kernel_mem) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); + if (params) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t *files = 0; + int i, nfiles = 0; + grub_size_t size = 0; + grub_uint8_t *ptr; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!loaded) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + goto fail; + } + + files = grub_zalloc (argc * sizeof (files[0])); + if (!files) + goto fail; + + for (i = 0; i < argc; i++) + { + grub_file_filter_disable_compression (); + files[i] = grub_file_open (argv[i]); + if (! files[i]) + goto fail; + nfiles++; + size += ALIGN_UP (grub_file_size (files[i]), 4); + } + + initrd_mem = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(size)); + + if (!initrd_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate initrd")); + goto fail; + } + + params->ramdisk_size = size; + params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; + + ptr = initrd_mem; + + for (i = 0; i < nfiles; i++) + { + grub_ssize_t cursize = grub_file_size (files[i]); + if (grub_file_read (files[i], ptr, cursize) != cursize) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + argv[i]); + goto fail; + } + ptr += cursize; + grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); + ptr += ALIGN_UP_OVERHEAD (cursize, 4); + } + + params->ramdisk_size = size; + + fail: + for (i = 0; i < nfiles; i++) + grub_file_close (files[i]); + grub_free (files); + + if (initrd_mem && grub_errno) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)initrd_mem, BYTES_TO_PAGES(size)); + + return grub_errno; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_kernel_header lh; + grub_ssize_t len, start, filelen; + void *kernel; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + file = grub_file_open (argv[0]); + if (! file) + goto fail; + + filelen = grub_file_size (file); + + kernel = grub_malloc(filelen); + + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, filelen) != filelen) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]); + goto fail; + } + + if (! grub_linuxefi_secure_validate (kernel, filelen)) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); + grub_free (kernel); + goto fail; + } + + grub_file_seek (file, 0); + + grub_free(kernel); + + params = grub_efi_allocate_pages_max (0x3fffffff, BYTES_TO_PAGES(16384)); + + if (! params) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate kernel parameters"); + goto fail; + } + + grub_memset (params, 0, 16384); + + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + if (lh.boot_flag != grub_cpu_to_le16 (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + goto fail; + } + + if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + { + grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); + goto fail; + } + + if (lh.version < grub_cpu_to_le16 (0x020b)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); + goto fail; + } + + if (!lh.handover_offset) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); + goto fail; + } + + linux_cmdline = grub_efi_allocate_pages_max(0x3fffffff, + BYTES_TO_PAGES(lh.cmdline_size + 1)); + + if (!linux_cmdline) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate cmdline")); + goto fail; + } + + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + linux_cmdline + sizeof (LINUX_IMAGE) - 1, + lh.cmdline_size - (sizeof (LINUX_IMAGE) - 1)); + + lh.cmd_line_ptr = (grub_uint32_t)(grub_addr_t)linux_cmdline; + + handover_offset = lh.handover_offset; + + start = (lh.setup_sects + 1) * 512; + len = grub_file_size(file) - start; + + kernel_mem = grub_efi_allocate_pages(lh.pref_address, + BYTES_TO_PAGES(lh.init_size)); + + if (!kernel_mem) + kernel_mem = grub_efi_allocate_pages_max(0x3fffffff, + BYTES_TO_PAGES(lh.init_size)); + + if (!kernel_mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("can't allocate kernel")); + goto fail; + } + + if (grub_file_seek (file, start) == (grub_off_t) -1) + { + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + if (grub_file_read (file, kernel_mem, len) != len && !grub_errno) + { + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + } + + if (grub_errno == GRUB_ERR_NONE) + { + grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0); + loaded = 1; + lh.code32_start = (grub_uint32_t)(grub_addr_t) kernel_mem; + } + + grub_memcpy (params, &lh, 2 * 512); + + params->type_of_loader = 0x21; + + fail: + + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_dl_unref (my_mod); + loaded = 0; + } + + if (linux_cmdline && !loaded) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)linux_cmdline, BYTES_TO_PAGES(lh.cmdline_size + 1)); + + if (kernel_mem && !loaded) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)kernel_mem, BYTES_TO_PAGES(kernel_size)); + + if (params && !loaded) + grub_efi_free_pages((grub_efi_physical_address_t)(grub_addr_t)params, BYTES_TO_PAGES(16384)); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd; + +GRUB_MOD_INIT(linuxefi) +{ + cmd_linux = + grub_register_command ("linuxefi", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = + grub_register_command ("initrdefi", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linuxefi) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); +} diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 5e87950fd..62a3d9726 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -40,6 +40,9 @@ void EXPORT_FUNC(grub_efi_stall) (grub_efi_uintn_t microseconds); void * EXPORT_FUNC(grub_efi_allocate_pages) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); +void * +EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, + grub_efi_uintn_t pages); void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); int diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index da0ca3b83..fc36bdaf3 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -139,6 +139,7 @@ struct linux_kernel_header grub_uint64_t setup_data; grub_uint64_t pref_address; grub_uint32_t init_size; + grub_uint32_t handover_offset; } GRUB_PACKED; /* Boot parameters for Linux based on 2.6.12. This is used by the setup From a2958c004fe653de6a5defba0a7efd799810b5ae Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:18 +0000 Subject: [PATCH 14/52] Add more debugging to linuxefi Forwarded: no Last-Update: 2016-09-19 Patch-Name: linuxefi_debug.patch --- grub-core/loader/i386/efi/linux.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index b622b3b71..1995a5089 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -54,15 +55,27 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size) { grub_efi_guid_t guid = SHIM_LOCK_GUID; grub_efi_shim_lock_t *shim_lock; + grub_efi_status_t status; + grub_dprintf ("linuxefi", "Locating shim protocol\n"); shim_lock = grub_efi_locate_protocol(&guid, NULL); if (!shim_lock) - return 1; + { + grub_dprintf ("linuxefi", "shim not available\n"); + return 1; + } - if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) - return 1; + grub_dprintf ("linuxefi", "Asking shim to verify kernel signature\n"); + status = shim_lock->verify(data, size); + if (status == GRUB_EFI_SUCCESS) + { + grub_dprintf ("linuxefi", "Kernel signature verification passed\n"); + return 1; + } + grub_dprintf ("linuxefi", "Kernel signature verification failed (0x%lx)\n", + (unsigned long) status); return 0; } @@ -147,6 +160,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linuxefi", "initrd_mem = %lx\n", (unsigned long) initrd_mem); + params->ramdisk_size = size; params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem; @@ -236,6 +251,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linuxefi", "params = %lx\n", (unsigned long) params); + grub_memset (params, 0, 16384); if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) @@ -279,6 +296,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linuxefi", "linux_cmdline = %lx\n", + (unsigned long) linux_cmdline); + grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); grub_create_loader_cmdline (argc, argv, linux_cmdline + sizeof (LINUX_IMAGE) - 1, @@ -304,6 +324,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_dprintf ("linuxefi", "kernel_mem = %lx\n", (unsigned long) kernel_mem); + if (grub_file_seek (file, start) == (grub_off_t) -1) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), From 90b80a94103bece12cd80e99e71ff7ab75a66e0b Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:19 +0000 Subject: [PATCH 15/52] Make linuxefi refuse to boot without shim This is only intended as a temporary measure. Forwarded: not-needed Last-Update: 2013-01-29 Patch-Name: linuxefi_require_shim.patch --- grub-core/loader/i386/efi/linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index 1995a5089..ff293166c 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -63,7 +63,7 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size) if (!shim_lock) { grub_dprintf ("linuxefi", "shim not available\n"); - return 1; + return 0; } grub_dprintf ("linuxefi", "Asking shim to verify kernel signature\n"); From 6da8e840ec0878e1bbdf549ac09afe016409fcb8 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:20 +0000 Subject: [PATCH 16/52] If running under UEFI secure boot, attempt to use linuxefi loader Author: Steve Langasek Forwarded: no Last-Update: 2013-12-20 Patch-Name: linuxefi_non_sb_fallback.patch --- grub-core/loader/i386/efi/linux.c | 2 +- grub-core/loader/i386/linux.c | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index ff293166c..de4471bc8 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -234,7 +234,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! grub_linuxefi_secure_validate (kernel, filelen)) { - grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]); + grub_error (GRUB_ERR_ACCESS_DENIED, N_("%s has invalid signature"), argv[0]); grub_free (kernel); goto fail; } diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 083f9417c..4dcd213b6 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -75,6 +75,8 @@ static grub_size_t maximal_cmdline_size; static struct linux_kernel_params linux_params; static char *linux_cmdline; #ifdef GRUB_MACHINE_EFI +static int using_linuxefi; +static grub_command_t initrdefi_cmd; static grub_efi_uintn_t efi_mmap_size; #else static const grub_size_t efi_mmap_size = 0; @@ -689,6 +691,41 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); +#ifdef GRUB_MACHINE_EFI + using_linuxefi = 0; + if (grub_efi_secure_boot ()) + { + /* Try linuxefi first, which will require a successful signature check + and then hand over to the kernel without calling ExitBootServices. + If that fails, however, fall back to calling ExitBootServices + ourselves and then booting an unsigned kernel. */ + grub_dl_t mod; + grub_command_t linuxefi_cmd; + + grub_dprintf ("linux", "Secure Boot enabled: trying linuxefi\n"); + + mod = grub_dl_load ("linuxefi"); + if (mod) + { + grub_dl_ref (mod); + linuxefi_cmd = grub_command_find ("linuxefi"); + initrdefi_cmd = grub_command_find ("initrdefi"); + if (linuxefi_cmd && initrdefi_cmd) + { + (linuxefi_cmd->func) (linuxefi_cmd, argc, argv); + if (grub_errno == GRUB_ERR_NONE) + { + grub_dprintf ("linux", "Handing off to linuxefi\n"); + using_linuxefi = 1; + return GRUB_ERR_NONE; + } + grub_dprintf ("linux", "linuxefi failed (%d)\n", grub_errno); + grub_errno = GRUB_ERR_NONE; + } + } + } +#endif + if (argc == 0) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -1054,6 +1091,12 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), grub_err_t err; struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; +#ifdef GRUB_MACHINE_EFI + /* If we're using linuxefi, just forward to initrdefi. */ + if (using_linuxefi && initrdefi_cmd) + return (initrdefi_cmd->func) (initrdefi_cmd, argc, argv); +#endif + if (argc == 0) { grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); From 0874e7cf1520ce34cf51e5e3aad7d2707db5c6d3 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:21 +0000 Subject: [PATCH 17/52] Generate configuration for signed UEFI kernels if available Forwarded: no Last-Update: 2013-12-25 Patch-Name: mkconfig_signed_kernel.patch --- util/grub.d/10_linux.in | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 37c248c54..60467460b 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -151,8 +151,16 @@ linux_entry () message="$(gettext_printf "Loading Linux %s ..." ${version})" sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' +EOF + if test -d /sys/firmware/efi && test -e "${linux}.efi.signed"; then + sed "s/^/$submenu_indentation/" << EOF + linux ${rel_dirname}/${basename}.efi.signed root=${linux_root_device_thisversion} ro ${args} +EOF + else + sed "s/^/$submenu_indentation/" << EOF linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args} EOF + fi if test -n "${initrd}" ; then # TRANSLATORS: ramdisk isn't identifier. Should be translated. message="$(gettext_printf "Loading initial ramdisk ...")" @@ -200,6 +208,13 @@ submenu_indentation="" is_top_level=true while [ "x$list" != "x" ] ; do linux=`version_find_latest $list` + case $linux in + *.efi.signed) + # We handle these in linux_entry. + list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '` + continue + ;; + esac gettext_printf "Found linux image: %s\n" "$linux" >&2 basename=`basename $linux` dirname=`dirname $linux` From 1917ef9042066380b9839501f7ccc598617847ae Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:22 +0000 Subject: [PATCH 18/52] Install signed images if UEFI Secure Boot is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Author: Stéphane Graber Author: Steve Langasek Author: Linn Crosetto Author: Mathieu Trudel-Lapierre Forwarded: no Last-Update: 2016-11-01 Patch-Name: install_signed.patch --- util/grub-install.c | 192 +++++++++++++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 47 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 0e1303ee5..260dabcfb 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -80,6 +80,7 @@ static char *label_color; static char *label_bgcolor; static char *product_version; static int add_rs_codes = 1; +static int uefi_secure_boot = 1; enum { @@ -110,7 +111,9 @@ enum OPTION_LABEL_FONT, OPTION_LABEL_COLOR, OPTION_LABEL_BGCOLOR, - OPTION_PRODUCT_VERSION + OPTION_PRODUCT_VERSION, + OPTION_UEFI_SECURE_BOOT, + OPTION_NO_UEFI_SECURE_BOOT }; static int fs_probe = 1; @@ -234,6 +237,14 @@ argp_parser (int key, char *arg, struct argp_state *state) bootloader_id = xstrdup (arg); return 0; + case OPTION_UEFI_SECURE_BOOT: + uefi_secure_boot = 1; + return 0; + + case OPTION_NO_UEFI_SECURE_BOOT: + uefi_secure_boot = 0; + return 0; + case ARGP_KEY_ARG: if (install_device) grub_util_error ("%s", _("More than one install device?")); @@ -303,6 +314,14 @@ static struct argp_option options[] = { {"label-color", OPTION_LABEL_COLOR, N_("COLOR"), 0, N_("use COLOR for label"), 2}, {"label-bgcolor", OPTION_LABEL_BGCOLOR, N_("COLOR"), 0, N_("use COLOR for label background"), 2}, {"product-version", OPTION_PRODUCT_VERSION, N_("STRING"), 0, N_("use STRING as product version"), 2}, + {"uefi-secure-boot", OPTION_UEFI_SECURE_BOOT, 0, 0, + N_("install an image usable with UEFI Secure Boot. " + "This option is only available on EFI and if the grub-efi-amd64-signed " + "package is installed."), 2}, + {"no-uefi-secure-boot", OPTION_NO_UEFI_SECURE_BOOT, 0, 0, + N_("do not install an image usable with UEFI Secure Boot, even if the " + "system was currently started using it. " + "This option is only available on EFI."), 2}, {0, 0, 0, 0, 0, 0} }; @@ -821,7 +840,8 @@ main (int argc, char *argv[]) { int is_efi = 0; const char *efi_distributor = NULL; - const char *efi_file = NULL; + const char *efi_suffix = NULL, *efi_suffix_upper = NULL; + char *efi_file = NULL; char **grub_devices; grub_fs_t grub_fs; grub_device_t grub_dev = NULL; @@ -1081,6 +1101,31 @@ main (int argc, char *argv[]) */ char *t; efi_distributor = bootloader_id; + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_EFI: + efi_suffix = "ia32"; + efi_suffix_upper = "IA32"; + break; + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + efi_suffix = "x64"; + efi_suffix_upper = "X64"; + break; + case GRUB_INSTALL_PLATFORM_IA64_EFI: + efi_suffix = "ia64"; + efi_suffix_upper = "IA64"; + break; + case GRUB_INSTALL_PLATFORM_ARM_EFI: + efi_suffix = "arm"; + efi_suffix_upper = "ARM"; + break; + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + efi_suffix = "aa64"; + efi_suffix_upper = "AA64"; + break; + default: + break; + } if (removable) { /* The specification makes stricter requirements of removable @@ -1089,54 +1134,16 @@ main (int argc, char *argv[]) must have a specific file name depending on the architecture. */ efi_distributor = "BOOT"; - switch (platform) - { - case GRUB_INSTALL_PLATFORM_I386_EFI: - efi_file = "BOOTIA32.EFI"; - break; - case GRUB_INSTALL_PLATFORM_X86_64_EFI: - efi_file = "BOOTX64.EFI"; - break; - case GRUB_INSTALL_PLATFORM_IA64_EFI: - efi_file = "BOOTIA64.EFI"; - break; - case GRUB_INSTALL_PLATFORM_ARM_EFI: - efi_file = "BOOTARM.EFI"; - break; - case GRUB_INSTALL_PLATFORM_ARM64_EFI: - efi_file = "BOOTAA64.EFI"; - break; - default: - grub_util_error ("%s", _("You've found a bug")); - break; - } + if (!efi_suffix) + grub_util_error ("%s", _("You've found a bug")); + efi_file = xasprintf ("BOOT%s.EFI", efi_suffix_upper); } else { /* It is convenient for each architecture to have a different efi_file, so that different versions can be installed in parallel. */ - switch (platform) - { - case GRUB_INSTALL_PLATFORM_I386_EFI: - efi_file = "grubia32.efi"; - break; - case GRUB_INSTALL_PLATFORM_X86_64_EFI: - efi_file = "grubx64.efi"; - break; - case GRUB_INSTALL_PLATFORM_IA64_EFI: - efi_file = "grubia64.efi"; - break; - case GRUB_INSTALL_PLATFORM_ARM_EFI: - efi_file = "grubarm.efi"; - break; - case GRUB_INSTALL_PLATFORM_ARM64_EFI: - efi_file = "grubaa64.efi"; - break; - default: - efi_file = "grub.efi"; - break; - } + efi_file = xasprintf ("grub%s.efi", efi_suffix); } t = grub_util_path_concat (3, efidir, "EFI", efi_distributor); free (efidir); @@ -1342,14 +1349,41 @@ main (int argc, char *argv[]) } } - if (!have_abstractions) + char *efi_signed = NULL; + switch (platform) + { + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: + case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_IA64_EFI: + { + char *dir = xasprintf ("%s-signed", grub_install_source_directory); + char *signed_image; + if (removable) + signed_image = xasprintf ("gcd%s.efi.signed", efi_suffix); + else + signed_image = xasprintf ("grub%s.efi.signed", efi_suffix); + efi_signed = grub_util_path_concat (2, dir, signed_image); + break; + } + + default: + break; + } + + if (!efi_signed || !grub_util_is_regular (efi_signed)) + uefi_secure_boot = 0; + + if (!have_abstractions || uefi_secure_boot) { if ((disk_module && grub_strcmp (disk_module, "biosdisk") != 0) || grub_drives[1] || (!install_drive && platform != GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275) || (install_drive && !is_same_disk (grub_drives[0], install_drive)) - || !have_bootdev (platform)) + || !have_bootdev (platform) + || uefi_secure_boot) { char *uuid = NULL; /* generic method (used on coreboot and ata mod). */ @@ -1871,7 +1905,71 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_IA64_EFI: { char *dst = grub_util_path_concat (2, efidir, efi_file); - grub_install_copy_file (imgfile, dst, 1); + if (uefi_secure_boot) + { + char *shim_signed = NULL; + char *mok_signed = NULL, *mok_file = NULL; + char *fb_signed = NULL, *fb_file = NULL; + char *config_dst; + FILE *config_dst_f; + + shim_signed = xasprintf ("/usr/lib/shim/shim%s.efi.signed", efi_suffix); + mok_signed = xasprintf ("mm%s.efi.signed", efi_suffix); + mok_file = xasprintf ("mm%s.efi", efi_suffix); + fb_signed = xasprintf ("fb%s.efi.signed", efi_suffix); + fb_file = xasprintf ("fb%s.efi", efi_suffix); + + if (grub_util_is_regular (shim_signed)) + { + char *chained_base, *chained_dst; + char *mok_src, *mok_dst, *fb_src, *fb_dst; + if (!removable) + { + free (efi_file); + efi_file = xasprintf ("shim%s.efi", efi_suffix); + free (dst); + dst = grub_util_path_concat (2, efidir, efi_file); + } + grub_install_copy_file (shim_signed, dst, 1); + chained_base = xasprintf ("grub%s.efi", efi_suffix); + chained_dst = grub_util_path_concat (2, efidir, chained_base); + grub_install_copy_file (efi_signed, chained_dst, 1); + free (chained_dst); + free (chained_base); + + /* Not critical, so not an error if they are not present (as it + won't be for older releases); but if we have them, make + sure they are installed. */ + mok_src = grub_util_path_concat (2, "/usr/lib/shim/", + mok_signed); + mok_dst = grub_util_path_concat (2, efidir, + mok_file); + grub_install_copy_file (mok_src, + mok_dst, 0); + free (mok_src); + free (mok_dst); + + fb_src = grub_util_path_concat (2, "/usr/lib/shim/", + fb_signed); + fb_dst = grub_util_path_concat (2, efidir, + fb_file); + grub_install_copy_file (fb_src, + fb_dst, 0); + free (fb_src); + free (fb_dst); + } + else + grub_install_copy_file (efi_signed, dst, 1); + + config_dst = grub_util_path_concat (2, efidir, "grub.cfg"); + grub_install_copy_file (load_cfg, config_dst, 1); + config_dst_f = grub_util_fopen (config_dst, "ab"); + fprintf (config_dst_f, "configfile $prefix/grub.cfg\n"); + fclose (config_dst_f); + free (config_dst); + } + else + grub_install_copy_file (imgfile, dst, 1); free (dst); } if (!removable && update_nvram) From 4b5de05feb2b89ba5d3acd13826b1465c5111587 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:23 +0000 Subject: [PATCH 19/52] Allow Shift to interrupt 'sleep --interruptible' Upstream would like to consider this at more length. See http://lists.gnu.org/archive/html/grub-devel/2009-08/msg00718.html, and the rest of the thread for context. Forwarded: http://lists.gnu.org/archive/html/grub-devel/2009-08/msg00694.html Last-Update: 2013-12-04 Patch-Name: sleep_shift.patch --- grub-core/commands/sleep.c | 27 ++++++++++++++++++++++++++- grub-core/normal/menu.c | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/sleep.c b/grub-core/commands/sleep.c index e77e7900f..3906b1410 100644 --- a/grub-core/commands/sleep.c +++ b/grub-core/commands/sleep.c @@ -46,6 +46,31 @@ do_print (int n) grub_refresh (); } +static int +grub_check_keyboard (void) +{ + int mods = 0; + grub_term_input_t term; + + if (grub_term_poll_usb) + grub_term_poll_usb (0); + + FOR_ACTIVE_TERM_INPUTS(term) + { + if (term->getkeystatus) + mods |= term->getkeystatus (term); + } + + if (mods >= 0 && + (mods & (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT)) != 0) + return 1; + + if (grub_getkey_noblock () == GRUB_TERM_ESC) + return 1; + + return 0; +} + /* Based on grub_millisleep() from kern/generic/millisleep.c. */ static int grub_interruptible_millisleep (grub_uint32_t ms) @@ -55,7 +80,7 @@ grub_interruptible_millisleep (grub_uint32_t ms) start = grub_get_time_ms (); while (grub_get_time_ms () - start < ms) - if (grub_getkey_noblock () == GRUB_TERM_ESC) + if (grub_check_keyboard ()) return 1; return 0; diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 719e2fb1c..9d0ad4c95 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -615,8 +615,27 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) saved_time = grub_get_time_ms (); while (1) { + int mods = 0; + grub_term_input_t term; int key; + if (grub_term_poll_usb) + grub_term_poll_usb (0); + + FOR_ACTIVE_TERM_INPUTS(term) + { + if (term->getkeystatus) + mods |= term->getkeystatus (term); + } + + if (mods >= 0 && + (mods & (GRUB_TERM_STATUS_LSHIFT + | GRUB_TERM_STATUS_RSHIFT)) != 0) + { + timeout = -1; + break; + } + key = grub_getkey_noblock (); if (key != GRUB_TERM_NO_KEY) { From 55ed7346aeaa113b1378e3ac060ca66913d1161d Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:24 +0000 Subject: [PATCH 20/52] Skip Windows os-prober entries on Wubi systems Since we're already being booted from the Windows boot loader, including entries that take us back to it mostly just causes confusion, and stops us from being able to hide the menu if there are no other OSes installed. https://blueprints.launchpad.net/ubuntu/+spec/foundations-o-wubi Forwarded: not-needed Last-Update: 2013-11-26 Patch-Name: wubi_no_windows.patch --- util/grub.d/30_os-prober.in | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index b7e1147c4..271044f59 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -110,6 +110,8 @@ EOF used_osprober_linux_ids= +wubi= + for OS in ${OSPROBED} ; do DEVICE="`echo ${OS} | cut -d ':' -f 1`" LONGNAME="`echo ${OS} | cut -d ':' -f 2 | tr '^' ' '`" @@ -146,6 +148,23 @@ for OS in ${OSPROBED} ; do case ${BOOT} in chain) + case ${LONGNAME} in + Windows*) + if [ -z "$wubi" ]; then + if [ -x /usr/share/lupin-support/grub-mkimage ] && \ + /usr/share/lupin-support/grub-mkimage --test; then + wubi=yes + else + wubi=no + fi + fi + if [ "$wubi" = yes ]; then + echo "Skipping ${LONGNAME} on Wubi system" >&2 + continue + fi + ;; + esac + onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' $CLASS --class os \$menuentry_id_option 'osprober-chain-$(grub_get_device_id "${DEVICE}")' { From aa30b8a134b8cf10901fd133363f021c50c47631 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:26 +0000 Subject: [PATCH 21/52] Add configure option to reduce visual clutter at boot time If this option is enabled, then do all of the following: Don't display introductory message about line editing unless we're actually offering a shell prompt. (This is believed to be a workaround for a different bug. We'll go with this for now, but will drop this in favour of a better fix upstream if somebody figures out what that is.) Don't clear the screen just before booting if we never drew the menu in the first place. Remove verbose messages printed before reading configuration. In some ways this is awkward because it makes debugging harder, but it's a requirement for a smooth-looking boot process; we may be able to do better in future. Upstream doesn't want this, though. Disable the cursor as well, for similar reasons of tidiness. Suppress kernel/initrd progress messages, except in recovery mode. Suppress "GRUB loading" message unless Shift is held down. Upstream doesn't want this, as it makes debugging harder. Ubuntu wants it to provide a cleaner boot experience. Bug-Ubuntu: https://bugs.launchpad.net/bugs/386922 Bug-Ubuntu: https://bugs.launchpad.net/bugs/861048 Forwarded: (partial) http://lists.gnu.org/archive/html/grub-devel/2009-09/msg00056.html Last-Update: 2014-01-03 Patch-Name: maybe_quiet.patch --- config.h.in | 2 ++ configure.ac | 16 ++++++++++++++++ grub-core/boot/i386/pc/boot.S | 11 +++++++++++ grub-core/boot/i386/pc/diskboot.S | 26 ++++++++++++++++++++++++++ grub-core/kern/main.c | 17 +++++++++++++++++ grub-core/kern/rescue_reader.c | 2 ++ grub-core/normal/main.c | 11 +++++++++++ grub-core/normal/menu.c | 17 +++++++++++++++-- util/grub.d/10_linux.in | 15 +++++++++++---- 9 files changed, 111 insertions(+), 6 deletions(-) diff --git a/config.h.in b/config.h.in index 9e8f9911b..d2c4ce8e5 100644 --- a/config.h.in +++ b/config.h.in @@ -12,6 +12,8 @@ /* Define to 1 to enable disk cache statistics. */ #define DISK_CACHE_STATS @DISK_CACHE_STATS@ #define BOOT_TIME_STATS @BOOT_TIME_STATS@ +/* Define to 1 to make GRUB quieter at boot time. */ +#define QUIET_BOOT @QUIET_BOOT@ /* We don't need those. */ #define MINILZO_CFG_SKIP_LZO_PTR 1 diff --git a/configure.ac b/configure.ac index d4978ad5d..dd2fbd01c 100644 --- a/configure.ac +++ b/configure.ac @@ -1830,6 +1830,17 @@ else fi AC_SUBST([UBUNTU_RECOVERY]) +AC_ARG_ENABLE([quiet-boot], + [AS_HELP_STRING([--enable-quiet-boot], + [emit fewer messages at boot time (default=no)])], + [], [enable_quiet_boot=no]) +if test x"$enable_quiet_boot" = xyes ; then + QUIET_BOOT=1 +else + QUIET_BOOT=0 +fi +AC_SUBST([QUIET_BOOT]) + LIBS="" AC_SUBST([FONT_SOURCE]) @@ -2081,5 +2092,10 @@ echo "Without liblzma (no support for XZ-compressed mips images) ($liblzma_excus else echo "With liblzma from $LIBLZMA (support for XZ-compressed mips images)" fi +if [ x"$enable_quiet_boot" = xyes ]; then +echo With quiet boot: Yes +else +echo With quiet boot: No +fi echo "*******************************************************" ] diff --git a/grub-core/boot/i386/pc/boot.S b/grub-core/boot/i386/pc/boot.S index 2bd0b2d28..451c8bea5 100644 --- a/grub-core/boot/i386/pc/boot.S +++ b/grub-core/boot/i386/pc/boot.S @@ -19,6 +19,9 @@ #include #include +#if defined(QUIET_BOOT) && !defined(HYBRID_BOOT) +#include +#endif /* * defines for the code go here @@ -249,9 +252,17 @@ real_start: /* save drive reference first thing! */ pushw %dx +#if defined(QUIET_BOOT) && !defined(HYBRID_BOOT) + /* is either shift key held down? */ + movw $(GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR + 0x17), %bx + testb $3, (%bx) + jz 2f +#endif + /* print a notification message on the screen */ MSG(notification_string) +2: /* set %si to the disk address packet */ movw $disk_address_packet, %si diff --git a/grub-core/boot/i386/pc/diskboot.S b/grub-core/boot/i386/pc/diskboot.S index 1ee4cf5b2..31096ce5a 100644 --- a/grub-core/boot/i386/pc/diskboot.S +++ b/grub-core/boot/i386/pc/diskboot.S @@ -18,6 +18,9 @@ #include #include +#ifdef QUIET_BOOT +#include +#endif /* * defines for the code go here @@ -25,6 +28,12 @@ #define MSG(x) movw $x, %si; call LOCAL(message) +#ifdef QUIET_BOOT +#define SILENT(x) call LOCAL(check_silent); jz LOCAL(x) +#else +#define SILENT(x) +#endif + .file "diskboot.S" .text @@ -50,11 +59,14 @@ _start: /* save drive reference first thing! */ pushw %dx + SILENT(after_notification_string) + /* print a notification message on the screen */ pushw %si MSG(notification_string) popw %si +LOCAL(after_notification_string): /* this sets up for the first run through "bootloop" */ movw $LOCAL(firstlist), %di @@ -279,7 +291,10 @@ LOCAL(copy_buffer): /* restore addressing regs and print a dot with correct DS (MSG modifies SI, which is saved, and unused AX and BX) */ popw %ds + SILENT(after_notification_step) MSG(notification_step) + +LOCAL(after_notification_step): popa /* check if finished with this dataset */ @@ -295,8 +310,11 @@ LOCAL(copy_buffer): /* END OF MAIN LOOP */ LOCAL(bootit): + SILENT(after_notification_done) /* print a newline */ MSG(notification_done) + +LOCAL(after_notification_done): popw %dx /* this makes sure %dl is our "boot" drive */ ljmp $0, $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200) @@ -320,6 +338,14 @@ LOCAL(general_error): /* go here when you need to stop the machine hard after an error condition */ LOCAL(stop): jmp LOCAL(stop) +#ifdef QUIET_BOOT +LOCAL(check_silent): + /* is either shift key held down? */ + movw $(GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR + 0x17), %bx + testb $3, (%bx) + ret +#endif + notification_string: .asciz "loading" notification_step: .asciz "." diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 9cad0c448..5613cd553 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -264,15 +264,25 @@ reclaim_module_space (void) void __attribute__ ((noreturn)) grub_main (void) { +#ifdef QUIET_BOOT + struct grub_term_output *term; +#endif + /* First of all, initialize the machine. */ grub_machine_init (); grub_boot_time ("After machine init."); +#ifdef QUIET_BOOT + /* Disable the cursor until we need it. */ + FOR_ACTIVE_TERM_OUTPUTS(term) + grub_term_setcursor (term, 0); +#else /* Hello. */ grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); grub_printf ("Welcome to GRUB!\n\n"); grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); +#endif grub_load_config (); @@ -308,5 +318,12 @@ grub_main (void) grub_boot_time ("After execution of embedded config. Attempt to go to normal mode"); grub_load_normal_mode (); + +#ifdef QUIET_BOOT + /* If we have to enter rescue mode, enable the cursor again. */ + FOR_ACTIVE_TERM_OUTPUTS(term) + grub_term_setcursor (term, 1); +#endif + grub_rescue_run (); } diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c index dcd7d4439..03826cf63 100644 --- a/grub-core/kern/rescue_reader.c +++ b/grub-core/kern/rescue_reader.c @@ -78,7 +78,9 @@ grub_rescue_read_line (char **line, int cont, void __attribute__ ((noreturn)) grub_rescue_run (void) { +#ifdef QUIET_BOOT grub_printf ("Entering rescue mode...\n"); +#endif while (1) { diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 78a70a8bf..e924f6448 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -389,6 +389,15 @@ static grub_err_t grub_normal_read_line_real (char **line, int cont, int nested) { const char *prompt; +#ifdef QUIET_BOOT + static int displayed_intro; + + if (! displayed_intro) + { + grub_normal_reader_init (nested); + displayed_intro = 1; + } +#endif if (cont) /* TRANSLATORS: it's command line prompt. */ @@ -441,7 +450,9 @@ grub_cmdline_run (int nested, int force_auth) return; } +#ifndef QUIET_BOOT grub_normal_reader_init (nested); +#endif while (1) { diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 9d0ad4c95..1f3447ad3 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -826,12 +826,18 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) /* Callback invoked immediately before a menu entry is executed. */ static void -notify_booting (grub_menu_entry_t entry, +notify_booting (grub_menu_entry_t entry +#ifdef QUIET_BOOT + __attribute__((unused)) +#endif + , void *userdata __attribute__((unused))) { +#ifndef QUIET_BOOT grub_printf (" "); grub_printf_ (N_("Booting `%s'"), entry->title); grub_printf ("\n\n"); +#endif } /* Callback invoked when a default menu entry executed because of a timeout @@ -879,6 +885,9 @@ show_menu (grub_menu_t menu, int nested, int autobooted) int boot_entry; grub_menu_entry_t e; int auto_boot; +#ifdef QUIET_BOOT + int initial_timeout = grub_menu_get_timeout (); +#endif boot_entry = run_menu (menu, nested, &auto_boot); if (boot_entry < 0) @@ -888,7 +897,11 @@ show_menu (grub_menu_t menu, int nested, int autobooted) if (! e) continue; /* Menu is empty. */ - grub_cls (); +#ifdef QUIET_BOOT + /* Only clear the screen if we drew the menu in the first place. */ + if (initial_timeout != 0) +#endif + grub_cls (); if (auto_boot) grub_menu_execute_with_fallback (menu, e, autobooted, diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 60467460b..67d57ecfd 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -21,6 +21,7 @@ prefix="@prefix@" exec_prefix="@exec_prefix@" datarootdir="@datarootdir@" ubuntu_recovery="@UBUNTU_RECOVERY@" +quiet_boot="@QUIET_BOOT@" . "$pkgdatadir/grub-mkconfig_lib" @@ -148,10 +149,12 @@ linux_entry () fi printf '%s\n' "${prepare_boot_cache}" | sed "s/^/$submenu_indentation/" fi - message="$(gettext_printf "Loading Linux %s ..." ${version})" - sed "s/^/$submenu_indentation/" << EOF + if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then + message="$(gettext_printf "Loading Linux %s ..." ${version})" + sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' EOF + fi if test -d /sys/firmware/efi && test -e "${linux}.efi.signed"; then sed "s/^/$submenu_indentation/" << EOF linux ${rel_dirname}/${basename}.efi.signed root=${linux_root_device_thisversion} ro ${args} @@ -163,9 +166,13 @@ EOF fi if test -n "${initrd}" ; then # TRANSLATORS: ramdisk isn't identifier. Should be translated. - message="$(gettext_printf "Loading initial ramdisk ...")" - sed "s/^/$submenu_indentation/" << EOF + if [ x"$quiet_boot" = x0 ] || [ x"$type" != xsimple ]; then + message="$(gettext_printf "Loading initial ramdisk ...")" + sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' +EOF + fi + sed "s/^/$submenu_indentation/" << EOF initrd ${rel_dirname}/${initrd} EOF fi From f3a7735a3d1aa451203a4698f5137ee7565f82ff Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:27 +0000 Subject: [PATCH 22/52] Cope with Kubuntu setting GRUB_DISTRIBUTOR This is not a very good approach, and certainly not sanely upstreamable; we probably need to split GRUB_DISTRIBUTOR into a couple of different variables. Bug-Ubuntu: https://bugs.launchpad.net/bugs/1242417 Forwarded: not-needed Last-Update: 2013-12-25 Patch-Name: install_efi_ubuntu_flavours.patch --- util/grub-install.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/grub-install.c b/util/grub-install.c index 260dabcfb..a73d12e87 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1101,6 +1101,8 @@ main (int argc, char *argv[]) */ char *t; efi_distributor = bootloader_id; + if (strcmp (efi_distributor, "kubuntu") == 0) + efi_distributor = "ubuntu"; switch (platform) { case GRUB_INSTALL_PLATFORM_I386_EFI: From 82480176b8b1404acb7aec08d108cb7517d4fb23 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:28 +0000 Subject: [PATCH 23/52] Add configure option to bypass boot menu if possible If other operating systems are installed, then automatically unhide the menu. Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus if available to check whether Shift is pressed. If it is, show the menu, otherwise boot immediately. If keystatus is not available, then fall back to a short delay interruptible with Escape. This may or may not remain Ubuntu-specific, although it's not obviously wanted upstream. It implements a requirement of https://wiki.ubuntu.com/DesktopExperienceTeam/KarmicBootExperienceDesignSpec#Bootloader. If the previous boot failed (defined as failing to get to the end of one of the normal runlevels), then show the boot menu regardless. Author: Richard Laager Author: Robie Basak Forwarded: no Last-Update: 2015-09-04 Patch-Name: quick_boot.patch --- configure.ac | 11 ++++++ docs/grub.texi | 14 +++++++ grub-core/normal/menu.c | 24 ++++++++++++ util/grub-mkconfig.in | 3 +- util/grub.d/00_header.in | 77 +++++++++++++++++++++++++++++++------ util/grub.d/10_linux.in | 4 ++ util/grub.d/30_os-prober.in | 21 ++++++++++ 7 files changed, 141 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index dd2fbd01c..e508f9c43 100644 --- a/configure.ac +++ b/configure.ac @@ -1841,6 +1841,17 @@ else fi AC_SUBST([QUIET_BOOT]) +AC_ARG_ENABLE([quick-boot], + [AS_HELP_STRING([--enable-quick-boot], + [bypass boot menu if possible (default=no)])], + [], [enable_quick_boot=no]) +if test x"$enable_quick_boot" = xyes ; then + QUICK_BOOT=1 +else + QUICK_BOOT=0 +fi +AC_SUBST([QUICK_BOOT]) + LIBS="" AC_SUBST([FONT_SOURCE]) diff --git a/docs/grub.texi b/docs/grub.texi index e935af33e..5b2a7bbb2 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1490,6 +1490,20 @@ This option may be set to a list of GRUB module names separated by spaces. Each module will be loaded as early as possible, at the start of @file{grub.cfg}. +@item GRUB_RECORDFAIL_TIMEOUT +If this option is set, it overrides the default recordfail setting. A +setting of -1 causes GRUB to wait for user input indefinitely. However, a +false positive in the recordfail mechanism may occur if power is lost during +boot before boot success is recorded in userspace. The default setting is +30, which causes GRUB to wait for user input for thirty seconds before +continuing. This default allows interactive users the opportunity to switch +to a different, working kernel, while avoiding a false positive causing the +boot to block indefinitely on headless and appliance systems where access to +a console is restricted or limited. + +This option is only effective when GRUB was configured with the +@option{--enable-quick-boot} option. + @end table The following options are still accepted for compatibility with existing diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 1f3447ad3..906a480a2 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -604,6 +604,30 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot) static struct grub_term_coordinate *pos; int entry = -1; + if (timeout == 0) + { + /* If modifier key statuses can't be detected without a delay, + then a hidden timeout of zero cannot be interrupted in any way, + which is not very helpful. Bump it to three seconds in this + case to give the user a fighting chance. */ + grub_term_input_t term; + int nterms = 0; + int mods_detectable = 1; + + FOR_ACTIVE_TERM_INPUTS(term) + { + if (!term->getkeystatus) + { + mods_detectable = 0; + break; + } + else + nterms++; + } + if (!mods_detectable || !nterms) + timeout = 3; + } + if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout) { pos = grub_term_save_pos (); diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index b4f0011b5..fac560464 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -239,7 +239,8 @@ export GRUB_DEFAULT \ GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ - GRUB_DISABLE_SUBMENU + GRUB_DISABLE_SUBMENU \ + GRUB_RECORDFAIL_TIMEOUT if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 93a90233e..674a76140 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -21,6 +21,8 @@ prefix="@prefix@" exec_prefix="@exec_prefix@" datarootdir="@datarootdir@" grub_lang=`echo $LANG | cut -d . -f 1` +grubdir="`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`" +quick_boot="@QUICK_BOOT@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" @@ -44,6 +46,7 @@ if [ "x${GRUB_TIMEOUT_BUTTON}" = "x" ] ; then GRUB_TIMEOUT_BUTTON="$GRUB_TIMEOUT cat << EOF if [ -s \$prefix/grubenv ]; then + set have_grubenv=true load_env fi EOF @@ -96,7 +99,50 @@ function savedefault { save_env saved_entry fi } +EOF +if [ "$quick_boot" = 1 ]; then + cat < Date: Mon, 13 Jan 2014 12:13:29 +0000 Subject: [PATCH 24/52] Add configure option to enable gfxpayload=keep dynamically Set GRUB_GFXPAYLOAD_LINUX=keep unless it's known to be unsupported on the current hardware. See https://blueprints.launchpad.net/ubuntu/+spec/packageselection-foundations-n-grub2-boot-framebuffer. Author: Colin Watson Forwarded: no Last-Update: 2013-12-25 Patch-Name: gfxpayload_dynamic.patch --- configure.ac | 11 ++ grub-core/Makefile.core.def | 9 ++ grub-core/commands/i386/pc/hwmatch.c | 146 +++++++++++++++++++++++++++ util/grub.d/10_linux.in | 37 ++++++- 4 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 grub-core/commands/i386/pc/hwmatch.c diff --git a/configure.ac b/configure.ac index e508f9c43..7f1431352 100644 --- a/configure.ac +++ b/configure.ac @@ -1852,6 +1852,17 @@ else fi AC_SUBST([QUICK_BOOT]) +AC_ARG_ENABLE([gfxpayload-dynamic], + [AS_HELP_STRING([--enable-gfxpayload-dynamic], + [use GRUB_GFXPAYLOAD_LINUX=keep unless explicitly unsupported on current hardware (default=no)])], + [], [enable_gfxpayload_dynamic=no]) +if test x"$enable_gfxpayload_dynamic" = xyes ; then + GFXPAYLOAD_DYNAMIC=1 +else + GFXPAYLOAD_DYNAMIC=0 +fi +AC_SUBST([GFXPAYLOAD_DYNAMIC]) + LIBS="" AC_SUBST([FONT_SOURCE]) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 68e47a727..3b389277c 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -889,6 +889,15 @@ module = { common = lib/hexdump.c; }; +module = { + name = hwmatch; + i386_pc = commands/i386/pc/hwmatch.c; + enable = i386_pc; + common = gnulib/regex.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; +}; + module = { name = keystatus; common = commands/keystatus.c; diff --git a/grub-core/commands/i386/pc/hwmatch.c b/grub-core/commands/i386/pc/hwmatch.c new file mode 100644 index 000000000..c9f7c9f19 --- /dev/null +++ b/grub-core/commands/i386/pc/hwmatch.c @@ -0,0 +1,146 @@ +/* hwmatch.c - Match hardware against a whitelist/blacklist. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Context for grub_cmd_hwmatch. */ +struct hwmatch_ctx +{ + grub_file_t matches_file; + int class_match; + int match; +}; + +/* Helper for grub_cmd_hwmatch. */ +static int +hwmatch_iter (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + struct hwmatch_ctx *ctx = data; + grub_pci_address_t addr; + grub_uint32_t class, baseclass, vendor, device; + grub_pci_id_t subpciid; + grub_uint32_t subvendor, subdevice, subclass; + char *id, *line; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + baseclass = class >> 24; + + if (ctx->class_match != baseclass) + return 0; + + vendor = pciid & 0xffff; + device = pciid >> 16; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_SUBVENDOR); + subpciid = grub_pci_read (addr); + + subclass = (class >> 16) & 0xff; + subvendor = subpciid & 0xffff; + subdevice = subpciid >> 16; + + id = grub_xasprintf ("v%04xd%04xsv%04xsd%04xbc%02xsc%02x", + vendor, device, subvendor, subdevice, + baseclass, subclass); + + grub_file_seek (ctx->matches_file, 0); + while ((line = grub_file_getline (ctx->matches_file)) != NULL) + { + char *anchored_line; + regex_t regex; + int ret; + + if (! *line || *line == '#') + { + grub_free (line); + continue; + } + + anchored_line = grub_xasprintf ("^%s$", line); + ret = regcomp (®ex, anchored_line, REG_EXTENDED | REG_NOSUB); + grub_free (anchored_line); + if (ret) + { + grub_free (line); + continue; + } + + ret = regexec (®ex, id, 0, NULL, 0); + regfree (®ex); + grub_free (line); + if (! ret) + { + ctx->match = 1; + return 1; + } + } + + return 0; +} + +static grub_err_t +grub_cmd_hwmatch (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct hwmatch_ctx ctx = { .match = 0 }; + char *match_str; + + if (argc < 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "list file and class required"); + + ctx.matches_file = grub_file_open (args[0]); + if (! ctx.matches_file) + return grub_errno; + + ctx.class_match = grub_strtol (args[1], 0, 10); + + grub_pci_iterate (hwmatch_iter, &ctx); + + match_str = grub_xasprintf ("%d", ctx.match); + grub_env_set ("match", match_str); + grub_free (match_str); + + grub_file_close (ctx.matches_file); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(hwmatch) +{ + cmd = grub_register_command ("hwmatch", grub_cmd_hwmatch, + N_("MATCHES-FILE CLASS"), + N_("Match PCI devices.")); +} + +GRUB_MOD_FINI(hwmatch) +{ + grub_unregister_command (cmd); +} diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 0f8a16837..2983b515f 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -23,6 +23,7 @@ datarootdir="@datarootdir@" ubuntu_recovery="@UBUNTU_RECOVERY@" quiet_boot="@QUIET_BOOT@" quick_boot="@QUICK_BOOT@" +gfxpayload_dynamic="@GFXPAYLOAD_DYNAMIC@" . "$pkgdatadir/grub-mkconfig_lib" @@ -135,9 +136,10 @@ linux_entry () if [ "x$GRUB_GFXPAYLOAD_LINUX" != xtext ]; then echo " load_video" | sed "s/^/$submenu_indentation/" fi - if [ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]; then - echo " set gfxpayload=$GRUB_GFXPAYLOAD_LINUX" | sed "s/^/$submenu_indentation/" - fi + fi + if ([ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]) && \ + ([ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 1 ]); then + echo " set gfxpayload=\$linux_gfx_mode" | sed "s/^/$submenu_indentation/" fi echo " insmod gzio" | sed "s/^/$submenu_indentation/" @@ -212,6 +214,35 @@ prepare_root_cache= boot_device_id= title_correction_code= +# Use ELILO's generic "efifb" when it's known to be available. +# FIXME: We need an interface to select vesafb in case efifb can't be used. +if [ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 0 ]; then + echo "set linux_gfx_mode=$GRUB_GFXPAYLOAD_LINUX" +else + cat << EOF +if [ "\${recordfail}" != 1 ]; then + if [ -e \${prefix}/gfxblacklist.txt ]; then + if hwmatch \${prefix}/gfxblacklist.txt 3; then + if [ \${match} = 0 ]; then + set linux_gfx_mode=keep + else + set linux_gfx_mode=text + fi + else + set linux_gfx_mode=text + fi + else + set linux_gfx_mode=keep + fi +else + set linux_gfx_mode=text +fi +EOF +fi +cat << EOF +export linux_gfx_mode +EOF + # Extra indentation to add to menu entries in a submenu. We're not in a submenu # yet, so it's empty. In a submenu it will be equal to '\t' (one tab). submenu_indentation="" From 94c17f6606a35ed6e4e5ffe900ef4044e97547d4 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:30 +0000 Subject: [PATCH 25/52] Add configure option to use vt.handoff=7 This is used for non-recovery Linux entries only; it enables flicker-free booting if gfxpayload=keep is in use and a suitable kernel is present. Author: Andy Whitcroft Forwarded: not-needed Last-Update: 2013-12-25 Patch-Name: vt_handoff.patch --- configure.ac | 11 +++++++++++ util/grub.d/10_linux.in | 28 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7f1431352..cd1f49837 100644 --- a/configure.ac +++ b/configure.ac @@ -1863,6 +1863,17 @@ else fi AC_SUBST([GFXPAYLOAD_DYNAMIC]) +AC_ARG_ENABLE([vt-handoff], + [AS_HELP_STRING([--enable-vt-handoff], + [use Linux vt.handoff option for flicker-free booting (default=no)])], + [], [enable_vt_handoff=no]) +if test x"$enable_vt_handoff" = xyes ; then + VT_HANDOFF=1 +else + VT_HANDOFF=0 +fi +AC_SUBST([VT_HANDOFF]) + LIBS="" AC_SUBST([FONT_SOURCE]) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 2983b515f..18c859214 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -24,6 +24,7 @@ ubuntu_recovery="@UBUNTU_RECOVERY@" quiet_boot="@QUIET_BOOT@" quick_boot="@QUICK_BOOT@" gfxpayload_dynamic="@GFXPAYLOAD_DYNAMIC@" +vt_handoff="@VT_HANDOFF@" . "$pkgdatadir/grub-mkconfig_lib" @@ -94,6 +95,14 @@ if [ "$ubuntu_recovery" = 1 ]; then GRUB_CMDLINE_LINUX_RECOVERY="$GRUB_CMDLINE_LINUX_RECOVERY nomodeset" fi +if [ "$vt_handoff" = 1 ]; then + for word in $GRUB_CMDLINE_LINUX_DEFAULT; do + if [ "$word" = splash ]; then + GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT \$vt_handoff" + fi + done +fi + linux_entry () { os="$1" @@ -139,7 +148,7 @@ linux_entry () fi if ([ "$ubuntu_recovery" = 0 ] || [ x$type != xrecovery ]) && \ ([ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 1 ]); then - echo " set gfxpayload=\$linux_gfx_mode" | sed "s/^/$submenu_indentation/" + echo " gfxmode \$linux_gfx_mode" | sed "s/^/$submenu_indentation/" fi echo " insmod gzio" | sed "s/^/$submenu_indentation/" @@ -214,6 +223,23 @@ prepare_root_cache= boot_device_id= title_correction_code= +cat << 'EOF' +function gfxmode { + set gfxpayload="${1}" +EOF +if [ "$vt_handoff" = 1 ]; then + cat << 'EOF' + if [ "${1}" = "keep" ]; then + set vt_handoff=vt.handoff=7 + else + set vt_handoff= + fi +EOF +fi +cat << EOF +} +EOF + # Use ELILO's generic "efifb" when it's known to be available. # FIXME: We need an interface to select vesafb in case efifb can't be used. if [ "x$GRUB_GFXPAYLOAD_LINUX" != x ] || [ "$gfxpayload_dynamic" = 0 ]; then From 5b9981361cc915381282119b22a982ca59256588 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:31 +0000 Subject: [PATCH 26/52] Probe FusionIO devices Bug-Ubuntu: https://bugs.launchpad.net/bugs/1237519 Forwarded: no Last-Update: 2016-09-18 Patch-Name: probe_fusionio.patch --- grub-core/osdep/linux/getroot.c | 13 +++++++++++++ util/deviceiter.c | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 90d92d3ad..7adc0f30e 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -950,6 +950,19 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st, *pp = '\0'; return path; } + + /* If this is a FusionIO disk. */ + if ((strncmp ("fio", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z') + { + char *pp = p + 3; + while (*pp >= 'a' && *pp <= 'z') + pp++; + if (*pp) + *is_part = 1; + /* /dev/fio[a-z]+[0-9]* */ + *pp = '\0'; + return path; + } } return path; diff --git a/util/deviceiter.c b/util/deviceiter.c index a4971ef42..dddc50da7 100644 --- a/util/deviceiter.c +++ b/util/deviceiter.c @@ -383,6 +383,12 @@ get_nvme_disk_name (char *name, int controller, int namespace) { sprintf (name, "/dev/nvme%dn%d", controller, namespace); } + +static void +get_fio_disk_name (char *name, int unit) +{ + sprintf (name, "/dev/fio%c", unit + 'a'); +} #endif static struct seen_device @@ -923,6 +929,19 @@ grub_util_iterate_devices (int (*hook) (const char *, int, void *), void *hook_d } } + /* FusionIO. */ + for (i = 0; i < 26; i++) + { + char name[16]; + + get_fio_disk_name (name, i); + if (check_device_readable_unique (name)) + { + if (hook (name, 0, hook_data)) + goto out; + } + } + # ifdef HAVE_DEVICE_MAPPER # define dmraid_check(cond, ...) \ if (! (cond)) \ From 3f8dc0a537d2052585561f6fd4708a095edbc4c4 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:32 +0000 Subject: [PATCH 27/52] Ignore functional test failures for now as they are broken See: https://lists.gnu.org/archive/html/grub-devel/2013-11/msg00242.html Forwarded: not-needed Last-Update: 2013-11-19 Patch-Name: ignore_grub_func_test_failures.patch --- tests/grub_func_test.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/grub_func_test.in b/tests/grub_func_test.in index c8cc26376..f458f741e 100644 --- a/tests/grub_func_test.in +++ b/tests/grub_func_test.in @@ -16,6 +16,8 @@ out=`echo all_functional_test | @builddir@/grub-shell --timeout=3600 --files="/b if [ "$(echo "$out" | tail -n 1)" != "ALL TESTS PASSED" ]; then echo "Functional test failure: $out" - exit 1 + # Disabled temporarily due to unrecognised video checksum failures. + #exit 1 + exit 0 fi From 0adc9d7db3b4291b6f533ede619ec28cc8fa4226 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:33 +0000 Subject: [PATCH 28/52] Add GRUB_RECOVERY_TITLE option This allows the controversial "recovery mode" text to be customised. Bug-Ubuntu: https://bugs.launchpad.net/bugs/1240360 Forwarded: no Last-Update: 2013-12-25 Patch-Name: mkconfig_recovery_title.patch --- docs/grub.texi | 5 +++++ util/grub-mkconfig.in | 7 ++++++- util/grub.d/10_hurd.in | 4 ++-- util/grub.d/10_kfreebsd.in | 2 +- util/grub.d/10_linux.in | 2 +- util/grub.d/10_netbsd.in | 2 +- util/grub.d/20_linux_xen.in | 2 +- 7 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index 5b2a7bbb2..f1216d19d 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1504,6 +1504,11 @@ a console is restricted or limited. This option is only effective when GRUB was configured with the @option{--enable-quick-boot} option. +@item GRUB_RECOVERY_TITLE +This option sets the English text of the string that will be displayed in +parentheses to indicate that a boot option is provided to help users recover +a broken system. The default is "recovery mode". + @end table The following options are still accepted for compatibility with existing diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index fac560464..ad716384b 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -189,6 +189,10 @@ GRUB_ACTUAL_DEFAULT="$GRUB_DEFAULT" if [ "x${GRUB_ACTUAL_DEFAULT}" = "xsaved" ] ; then GRUB_ACTUAL_DEFAULT="`"${grub_editenv}" - list | sed -n '/^saved_entry=/ s,^saved_entry=,,p'`" ; fi +if [ "x${GRUB_RECOVERY_TITLE}" = "x" ]; then + GRUB_RECOVERY_TITLE="recovery mode" +fi + # These are defined in this script, export them here so that user can # override them. @@ -240,7 +244,8 @@ export GRUB_DEFAULT \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ GRUB_DISABLE_SUBMENU \ - GRUB_RECORDFAIL_TIMEOUT + GRUB_RECORDFAIL_TIMEOUT \ + GRUB_RECOVERY_TITLE if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" diff --git a/util/grub.d/10_hurd.in b/util/grub.d/10_hurd.in index 59a9a48a2..7fa3a3fbd 100644 --- a/util/grub.d/10_hurd.in +++ b/util/grub.d/10_hurd.in @@ -88,8 +88,8 @@ hurd_entry () { if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then - title="$(gettext_printf "%s, with Hurd %s (recovery mode)" "${OS}" "${kernel_base}")" - oldtitle="$OS using $kernel_base (recovery mode)" + title="$(gettext_printf "%s, with Hurd %s (%s)" "${OS}" "${kernel_base}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" + oldtitle="$OS using $kernel_base ($GRUB_RECOVERY_TITLE)" else title="$(gettext_printf "%s, with Hurd %s" "${OS}" "${kernel_base}")" oldtitle="$OS using $kernel_base" diff --git a/util/grub.d/10_kfreebsd.in b/util/grub.d/10_kfreebsd.in index 9d8e8fd85..8301d361a 100644 --- a/util/grub.d/10_kfreebsd.in +++ b/util/grub.d/10_kfreebsd.in @@ -76,7 +76,7 @@ kfreebsd_entry () fi if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then - title="$(gettext_printf "%s, with kFreeBSD %s (recovery mode)" "${os}" "${version}")" + title="$(gettext_printf "%s, with kFreeBSD %s (%s)" "${os}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" else title="$(gettext_printf "%s, with kFreeBSD %s" "${os}" "${version}")" fi diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 18c859214..704e834d5 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -116,7 +116,7 @@ linux_entry () if [ x$type != xsimple ] ; then case $type in recovery) - title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;; + title="$(gettext_printf "%s, with Linux %s (%s)" "${os}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" ;; *) title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;; esac diff --git a/util/grub.d/10_netbsd.in b/util/grub.d/10_netbsd.in index 874f59969..bb29cc046 100644 --- a/util/grub.d/10_netbsd.in +++ b/util/grub.d/10_netbsd.in @@ -102,7 +102,7 @@ netbsd_entry () if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then - title="$(gettext_printf "%s, with kernel %s (via %s, recovery mode)" "${OS}" "$(echo ${kernel} | sed -e 's,^.*/,,')" "${loader}")" + title="$(gettext_printf "%s, with kernel %s (via %s, %s)" "${OS}" "$(echo ${kernel} | sed -e 's,^.*/,,')" "${loader}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" else title="$(gettext_printf "%s, with kernel %s (via %s)" "${OS}" "$(echo ${kernel} | sed -e 's,^.*/,,')" "${loader}")" fi diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index af05bd276..62d663334 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -95,7 +95,7 @@ linux_entry () fi if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then - title="$(gettext_printf "%s, with Xen %s and Linux %s (recovery mode)" "${os}" "${xen_version}" "${version}")" + title="$(gettext_printf "%s, with Xen %s and Linux %s (%s)" "${os}" "${xen_version}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" else title="$(gettext_printf "%s, with Xen %s and Linux %s" "${os}" "${xen_version}" "${version}")" fi From e3ef14b4a42ee06514e281f7ab136cd0c1c938e8 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Mon, 13 Jan 2014 12:13:34 +0000 Subject: [PATCH 29/52] Don't run gettext_strings_test This test is mainly useful as an upstream maintenance check. Forwarded: not-needed Last-Update: 2013-12-23 Patch-Name: skip_gettext_strings_test.patch --- tests/gettext_strings_test.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/gettext_strings_test.in b/tests/gettext_strings_test.in index 5c305e75b..15405cfcd 100644 --- a/tests/gettext_strings_test.in +++ b/tests/gettext_strings_test.in @@ -1,5 +1,8 @@ #!/bin/sh +echo "Skipping upstream maintenance check." +exit 77 + cd '@srcdir@' tdir="$(mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX")" From 4ee18c4a571e455f1a14762006f91d6f0ae76f41 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 28 Jan 2014 14:40:02 +0000 Subject: [PATCH 30/52] Port yaboot logic for various powerpc machine types Some powerpc machines require not updating the NVRAM. This can be handled by existing grub-install command-line options, but it's friendlier to detect this automatically. On chrp_ibm machines, use the nvram utility rather than nvsetenv. (This is possibly suitable for other machines too, but that needs to be verified.) Forwarded: no Last-Update: 2014-10-15 Patch-Name: install_powerpc_machtypes.patch --- grub-core/osdep/basic/platform.c | 5 +++ grub-core/osdep/linux/platform.c | 72 ++++++++++++++++++++++++++++++ grub-core/osdep/unix/platform.c | 28 +++++++++--- grub-core/osdep/windows/platform.c | 6 +++ include/grub/util/install.h | 3 ++ util/grub-install.c | 11 +++++ 6 files changed, 119 insertions(+), 6 deletions(-) diff --git a/grub-core/osdep/basic/platform.c b/grub-core/osdep/basic/platform.c index 4b5502aeb..2ab907976 100644 --- a/grub-core/osdep/basic/platform.c +++ b/grub-core/osdep/basic/platform.c @@ -24,3 +24,8 @@ grub_install_get_default_x86_platform (void) return "i386-pc"; } +const char * +grub_install_get_default_powerpc_machtype (void) +{ + return "generic"; +} diff --git a/grub-core/osdep/linux/platform.c b/grub-core/osdep/linux/platform.c index 35f1bcc0e..9805c36d4 100644 --- a/grub-core/osdep/linux/platform.c +++ b/grub-core/osdep/linux/platform.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -145,3 +146,74 @@ grub_install_get_default_x86_platform (void) grub_util_info ("... not found"); return "i386-pc"; } + +const char * +grub_install_get_default_powerpc_machtype (void) +{ + FILE *fp; + char *buf = NULL; + size_t len = 0; + const char *machtype = "generic"; + + fp = grub_util_fopen ("/proc/cpuinfo", "r"); + if (! fp) + return machtype; + + while (getline (&buf, &len, fp) > 0) + { + if (strncmp (buf, "pmac-generation", + sizeof ("pmac-generation") - 1) == 0) + { + if (strstr (buf, "NewWorld")) + { + machtype = "pmac_newworld"; + break; + } + if (strstr (buf, "OldWorld")) + { + machtype = "pmac_oldworld"; + break; + } + } + + if (strncmp (buf, "motherboard", sizeof ("motherboard") - 1) == 0 && + strstr (buf, "AAPL")) + { + machtype = "pmac_oldworld"; + break; + } + + if (strncmp (buf, "machine", sizeof ("machine") - 1) == 0 && + strstr (buf, "CHRP IBM")) + { + if (strstr (buf, "qemu")) + { + machtype = "chrp_ibm_qemu"; + break; + } + else + { + machtype = "chrp_ibm"; + break; + } + } + + if (strncmp (buf, "platform", sizeof ("platform") - 1) == 0) + { + if (strstr (buf, "Maple")) + { + machtype = "maple"; + break; + } + if (strstr (buf, "Cell")) + { + machtype = "cell"; + break; + } + } + } + + free (buf); + fclose (fp); + return machtype; +} diff --git a/grub-core/osdep/unix/platform.c b/grub-core/osdep/unix/platform.c index a3fcfcaca..28cb37e15 100644 --- a/grub-core/osdep/unix/platform.c +++ b/grub-core/osdep/unix/platform.c @@ -212,13 +212,29 @@ grub_install_register_ieee1275 (int is_prep, const char *install_device, else boot_device = get_ofpathname (install_device); - if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device", - boot_device, NULL })) + if (strcmp (grub_install_get_default_powerpc_machtype (), "chrp_ibm") == 0) { - char *cmd = xasprintf ("setenv boot-device %s", boot_device); - grub_util_error (_("`nvsetenv' failed. \nYou will have to set `boot-device' variable manually. At the IEEE1275 prompt, type:\n %s\n"), - cmd); - free (cmd); + char *arg = xasprintf ("boot-device=%s", boot_device); + if (grub_util_exec ((const char * []){ "nvram", + "--update-config", arg, NULL })) + { + char *cmd = xasprintf ("setenv boot-device %s", boot_device); + grub_util_error (_("`nvram' failed. \nYou will have to set `boot-device' variable manually. At the IEEE1275 prompt, type:\n %s\n"), + cmd); + free (cmd); + } + free (arg); + } + else + { + if (grub_util_exec ((const char * []){ "nvsetenv", "boot-device", + boot_device, NULL })) + { + char *cmd = xasprintf ("setenv boot-device %s", boot_device); + grub_util_error (_("`nvsetenv' failed. \nYou will have to set `boot-device' variable manually. At the IEEE1275 prompt, type:\n %s\n"), + cmd); + free (cmd); + } } free (boot_device); diff --git a/grub-core/osdep/windows/platform.c b/grub-core/osdep/windows/platform.c index 912269191..c30025b13 100644 --- a/grub-core/osdep/windows/platform.c +++ b/grub-core/osdep/windows/platform.c @@ -128,6 +128,12 @@ grub_install_get_default_x86_platform (void) return "i386-efi"; } +const char * +grub_install_get_default_powerpc_machtype (void) +{ + return "generic"; +} + static void * get_efi_variable (const wchar_t *varname, ssize_t *len) { diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 5ca4811cd..9f517a1bb 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -206,6 +206,9 @@ grub_install_create_envblk_file (const char *name); const char * grub_install_get_default_x86_platform (void); +const char * +grub_install_get_default_powerpc_machtype (void); + void grub_install_register_efi (grub_device_t efidir_grub_dev, const char *efifile_path, diff --git a/util/grub-install.c b/util/grub-install.c index a73d12e87..57d6aab76 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1155,7 +1155,18 @@ main (int argc, char *argv[]) if (platform == GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275) { + const char *machtype = grub_install_get_default_powerpc_machtype (); int is_guess = 0; + + if (strcmp (machtype, "pmac_oldworld") == 0) + update_nvram = 0; + else if (strcmp (machtype, "cell") == 0) + update_nvram = 0; + else if (strcmp (machtype, "generic") == 0) + update_nvram = 0; + else if (strcmp (machtype, "chrp_ibm_qemu") == 0) + update_nvram = 0; + if (!macppcdir) { char *d; From e31237aea5ee424d14fb8514692d12482ebd78f9 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Thu, 25 Sep 2014 18:41:29 -0300 Subject: [PATCH 31/52] Include a text attribute reset in the clear command for ppc Always clear text attribute for clear command in order to avoid problems after it boots. * grub-core/term/terminfo.c: Add escape for text attribute reset Bug-Ubuntu: https://bugs.launchpad.net/bugs/1295255 Origin: other, https://lists.gnu.org/archive/html/grub-devel/2014-09/msg00076.html Last-Update: 2014-09-26 Patch-Name: ieee1275-clear-reset.patch --- grub-core/term/terminfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c index f0d3e3deb..7cb7909c8 100644 --- a/grub-core/term/terminfo.c +++ b/grub-core/term/terminfo.c @@ -151,7 +151,7 @@ grub_terminfo_set_current (struct grub_term_output *term, /* Clear the screen. Using serial console, screen(1) only recognizes the * ANSI escape sequence. Using video console, Apple Open Firmware * (version 3.1.1) only recognizes the literal ^L. So use both. */ - data->cls = grub_strdup (" \e[2J"); + data->cls = grub_strdup (" \e[2J\e[m"); data->reverse_video_on = grub_strdup ("\e[7m"); data->reverse_video_off = grub_strdup ("\e[m"); if (grub_strcmp ("ieee1275", str) == 0) From 670711393c4f958b9bd0669b4118376fccd56446 Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Thu, 25 Sep 2014 19:33:39 -0300 Subject: [PATCH 32/52] Disable VSX instruction VSX bit is enabled by default for Power7 and Power8 CPU models, so we need to disable them in order to avoid instruction exceptions. Kernel will activate it when necessary. * grub-core/kern/powerpc/ieee1275/startup.S: Disable VSX. Also-By: Adhemerval Zanella Also-By: Colin Watson Origin: other, https://lists.gnu.org/archive/html/grub-devel/2014-09/msg00078.html Last-Update: 2015-01-27 Patch-Name: ppc64el-disable-vsx.patch --- grub-core/kern/powerpc/ieee1275/startup.S | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/grub-core/kern/powerpc/ieee1275/startup.S b/grub-core/kern/powerpc/ieee1275/startup.S index 21c884b43..de9a9601a 100644 --- a/grub-core/kern/powerpc/ieee1275/startup.S +++ b/grub-core/kern/powerpc/ieee1275/startup.S @@ -20,6 +20,8 @@ #include #include +#define MSR_VSX 0x80 + .extern __bss_start .extern _end @@ -28,6 +30,16 @@ .globl start, _start start: _start: + _start: + + /* Disable VSX instruction */ + mfmsr 0 + oris 0,0,MSR_VSX + /* The "VSX Available" bit is in the lower half of the MSR, so we + don't need mtmsrd, which in any case won't work in 32-bit mode. */ + mtmsr 0 + isync + li 2, 0 li 13, 0 From a4b1e6234ba49999e4964462c948f163e97fcff0 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Sat, 6 Sep 2014 12:20:12 +0100 Subject: [PATCH 33/52] grub-install: Install PV Xen binaries into the upstream specified path Upstream have defined a specification for where guests ought to place their xenpv grub binaries in order to facilitate chainloading from a stage 1 grub loaded from dom0. http://xenbits.xen.org/docs/unstable-staging/misc/x86-xenpv-bootloader.html The spec calls for installation into /boot/xen/pvboot-i386.elf or /boot/xen/pvboot-x86_64.elf. Signed-off-by: Ian Campbell Bug-Debian: https://bugs.debian.org/762307 Forwarded: http://lists.gnu.org/archive/html/grub-devel/2014-10/msg00041.html Last-Update: 2014-10-24 Patch-Name: grub-install-pvxen-paths.patch --- v2: Respect bootdir, create /boot/xen as needed. --- util/grub-install.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 57d6aab76..7a24767c1 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -2010,6 +2010,28 @@ main (int argc, char *argv[]) } break; + case GRUB_INSTALL_PLATFORM_I386_XEN: + { + char *path = grub_util_path_concat (2, bootdir, "xen"); + char *dst = grub_util_path_concat (2, path, "pvboot-i386.elf"); + grub_install_mkdir_p (path); + grub_install_copy_file (imgfile, dst, 1); + free (dst); + free (path); + } + break; + + case GRUB_INSTALL_PLATFORM_X86_64_XEN: + { + char *path = grub_util_path_concat (2, bootdir, "xen"); + char *dst = grub_util_path_concat (2, path, "pvboot-x86_64.elf"); + grub_install_mkdir_p (path); + grub_install_copy_file (imgfile, dst, 1); + free (dst); + free (path); + } + break; + case GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON: case GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS: case GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS: @@ -2018,8 +2040,6 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_MIPSEL_ARC: case GRUB_INSTALL_PLATFORM_ARM_UBOOT: case GRUB_INSTALL_PLATFORM_I386_QEMU: - case GRUB_INSTALL_PLATFORM_I386_XEN: - case GRUB_INSTALL_PLATFORM_X86_64_XEN: grub_util_warn ("%s", _("WARNING: no platform-specific install was performed")); break; From 02d10b47f2e7625712397f03f25bdcd0d04dda39 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Sun, 30 Nov 2014 12:12:52 +0000 Subject: [PATCH 34/52] Arrange to insmod xzio and lzopio when booting a kernel as a Xen guest This is needed in case the Linux kernel is compiled with CONFIG_KERNEL_XZ or CONFIG_KERNEL_LZO rather than CONFIG_KERNEL_GZ (gzio is already loaded by grub.cfg today). Signed-off-by: Ian Campbell Bug-Debian: https://bugs.debian.org/755256 Forwarded: http://lists.gnu.org/archive/html/grub-devel/2014-11/msg00091.html Last-Update: 2014-11-30 Patch-Name: insmod-xzio-and-lzopio-on-xen.patch --- util/grub.d/10_linux.in | 1 + 1 file changed, 1 insertion(+) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index 704e834d5..d344f05e6 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -152,6 +152,7 @@ linux_entry () fi echo " insmod gzio" | sed "s/^/$submenu_indentation/" + echo " if [ x\$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi" | sed "s/^/$submenu_indentation/" if [ x$dirname = x/ ]; then if [ -z "${prepare_root_cache}" ]; then From c009126d6256205355a392dc7002ed1dac2dc59f Mon Sep 17 00:00:00 2001 From: Steve McIntyre <93sam@debian.org> Date: Wed, 3 Dec 2014 01:25:12 +0000 Subject: [PATCH 35/52] Add support for forcing EFI installation to the removable media path Add an extra option to grub-install "--force-extra-removable". On EFI platforms, this will cause an extra copy of the grub-efi image to be written to the appropriate removable media patch /boot/efi/EFI/BOOT/BOOT$ARCH.EFI as well. This will help with broken UEFI implementations where the firmware does not work when configured with new boot paths. Signed-off-by: Steve McIntyre <93sam@debian.org> Bug-Debian: https://bugs.debian.org/767037 https://bugs.debian.org/773092 Forwarded: Not yet Last-Update: 2014-12-20 Patch-Name: grub-install-extra-removable.patch --- util/grub-install.c | 110 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/util/grub-install.c b/util/grub-install.c index 7a24767c1..1fbb4c7da 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -56,6 +56,7 @@ static char *target; static int removable = 0; +static int force_extra_removable = 0; static int recheck = 0; static int update_nvram = 1; static char *install_device = NULL; @@ -113,7 +114,8 @@ enum OPTION_LABEL_BGCOLOR, OPTION_PRODUCT_VERSION, OPTION_UEFI_SECURE_BOOT, - OPTION_NO_UEFI_SECURE_BOOT + OPTION_NO_UEFI_SECURE_BOOT, + OPTION_FORCE_EXTRA_REMOVABLE }; static int fs_probe = 1; @@ -216,6 +218,10 @@ argp_parser (int key, char *arg, struct argp_state *state) removable = 1; return 0; + case OPTION_FORCE_EXTRA_REMOVABLE: + force_extra_removable = 1; + return 0; + case OPTION_ALLOW_FLOPPY: allow_floppy = 1; return 0; @@ -322,6 +328,9 @@ static struct argp_option options[] = { N_("do not install an image usable with UEFI Secure Boot, even if the " "system was currently started using it. " "This option is only available on EFI."), 2}, + {"force-extra-removable", OPTION_FORCE_EXTRA_REMOVABLE, 0, 0, + N_("force installation to the removable media path also. " + "This option is only available on EFI."), 2}, {0, 0, 0, 0, 0, 0} }; @@ -835,6 +844,91 @@ fill_core_services (const char *core_services) free (sysv_plist); } +/* Helper routine for also_install_removable() below. Walk through the + specified dir, looking to see if there is a file/dir that matches + the search string exactly, but in a case-insensitive manner. If so, + return a copy of the exact file/dir that *does* exist. If not, + return NULL */ +static char * +check_component_exists(const char *dir, + const char *search) +{ + grub_util_fd_dir_t d; + grub_util_fd_dirent_t de; + char *found = NULL; + + d = grub_util_fd_opendir (dir); + if (!d) + grub_util_error (_("cannot open directory `%s': %s"), + dir, grub_util_fd_strerror ()); + + while ((de = grub_util_fd_readdir (d))) + { + if (strcasecmp (de->d_name, search) == 0) + { + found = xstrdup (de->d_name); + break; + } + } + grub_util_fd_closedir (d); + return found; +} + +/* Some complex directory-handling stuff in here, to cope with + * case-insensitive FAT/VFAT filesystem semantics. Ugh. */ +static void +also_install_removable(const char *src, + const char *base_efidir, + const char *efi_suffix_upper) +{ + char *efi_file = NULL; + char *dst = NULL; + char *cur = NULL; + char *found = NULL; + + if (!efi_suffix_upper) + grub_util_error ("%s", _("efi_suffix_upper not set")); + efi_file = xasprintf ("BOOT%s.EFI", efi_suffix_upper); + + /* We need to install in $base_efidir/EFI/BOOT/$efi_file, but we + * need to cope with case-insensitive stuff here. Build the path one + * component at a time, checking for existing matches each time. */ + + /* Look for "EFI" in base_efidir. Make it if it does not exist in + * some form. */ + found = check_component_exists(base_efidir, "EFI"); + if (found == NULL) + found = xstrdup("EFI"); + dst = grub_util_path_concat (2, base_efidir, found); + cur = xstrdup (dst); + free (dst); + free (found); + grub_install_mkdir_p (cur); + + /* Now BOOT */ + found = check_component_exists(cur, "BOOT"); + if (found == NULL) + found = xstrdup("BOOT"); + dst = grub_util_path_concat (2, cur, found); + cur = xstrdup (dst); + free (dst); + free (found); + grub_install_mkdir_p (cur); + + /* Now $efi_file */ + found = check_component_exists(cur, efi_file); + if (found == NULL) + found = xstrdup(efi_file); + dst = grub_util_path_concat (2, cur, found); + cur = xstrdup (dst); + free (dst); + free (found); + grub_install_copy_file (src, cur, 1); + + free (cur); + free (efi_file); +} + int main (int argc, char *argv[]) { @@ -852,6 +946,7 @@ main (int argc, char *argv[]) char *relative_grubdir; char **efidir_device_names = NULL; grub_device_t efidir_grub_dev = NULL; + char *base_efidir = NULL; char *efidir_grub_devname; int efidir_is_mac = 0; int is_prep = 0; @@ -884,6 +979,9 @@ main (int argc, char *argv[]) bootloader_id = xstrdup ("grub"); } + if (removable && force_extra_removable) + grub_util_error (_("Invalid to use both --removable and --force_extra_removable")); + if (!grub_install_source_directory) { if (!target) @@ -1093,6 +1191,8 @@ main (int argc, char *argv[]) if (!efidir_is_mac && grub_strcmp (fs->name, "fat") != 0) grub_util_error (_("%s doesn't look like an EFI partition"), efidir); + base_efidir = xstrdup(efidir); + /* The EFI specification requires that an EFI System Partition must contain an "EFI" subdirectory, and that OS loaders are stored in subdirectories below EFI. Vendors are expected to pick names that do @@ -1980,9 +2080,15 @@ main (int argc, char *argv[]) fprintf (config_dst_f, "configfile $prefix/grub.cfg\n"); fclose (config_dst_f); free (config_dst); + if (force_extra_removable) + also_install_removable(efi_signed, base_efidir, efi_suffix_upper); } else - grub_install_copy_file (imgfile, dst, 1); + { + grub_install_copy_file (imgfile, dst, 1); + if (force_extra_removable) + also_install_removable(imgfile, base_efidir, efi_suffix_upper); + } free (dst); } if (!removable && update_nvram) From 63c966da63808b2251b7110b1b4b753b8a21576b Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sat, 3 Jan 2015 12:04:59 +0000 Subject: [PATCH 36/52] Generate alternative init entries in advanced menu Add fallback boot entries for alternative installed init systems. Based on patches from Michael Biebl and Didier Roche. Bug-Debian: https://bugs.debian.org/757298 Bug-Debian: https://bugs.debian.org/773173 Forwarded: no Last-Update: 2017-06-23 Patch-Name: mkconfig_other_inits.patch --- util/grub.d/10_linux.in | 10 ++++++++++ util/grub.d/20_linux_xen.in | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in index d344f05e6..4cf321c54 100644 --- a/util/grub.d/10_linux.in +++ b/util/grub.d/10_linux.in @@ -32,6 +32,7 @@ export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os" +SUPPORTED_INITS="sysvinit:/lib/sysvinit/init systemd:/lib/systemd/systemd upstart:/sbin/upstart" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS=GNU/Linux @@ -117,6 +118,8 @@ linux_entry () case $type in recovery) title="$(gettext_printf "%s, with Linux %s (%s)" "${os}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" ;; + init-*) + title="$(gettext_printf "%s, with Linux %s (%s)" "${os}" "${version}" "${type#init-}")" ;; *) title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;; esac @@ -344,6 +347,13 @@ while [ "x$list" != "x" ] ; do linux_entry "${OS}" "${version}" advanced \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + for supported_init in ${SUPPORTED_INITS}; do + init_path="${supported_init#*:}" + if [ -x "${init_path}" ] && [ "$(readlink -f /sbin/init)" != "$(readlink -f "${init_path}")" ]; then + linux_entry "${OS}" "${version}" "init-${supported_init%%:*}" \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} init=${init_path}" + fi + done if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then linux_entry "${OS}" "${version}" recovery \ "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}" diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 62d663334..d3edb46df 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -27,6 +27,7 @@ export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os --class xen" +SUPPORTED_INITS="sysvinit:/lib/sysvinit/init systemd:/lib/systemd/systemd upstart:/sbin/upstart" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then OS=GNU/Linux @@ -96,6 +97,8 @@ linux_entry () if [ x$type != xsimple ] ; then if [ x$type = xrecovery ] ; then title="$(gettext_printf "%s, with Xen %s and Linux %s (%s)" "${os}" "${xen_version}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")" + elif [ "${type#init-}" != "$type" ] ; then + title="$(gettext_printf "%s, with Xen %s and Linux %s (%s)" "${os}" "${xen_version}" "${version}" "${type#init-}")" else title="$(gettext_printf "%s, with Xen %s and Linux %s" "${os}" "${xen_version}" "${version}")" fi @@ -259,6 +262,14 @@ while [ "x${xen_list}" != "x" ] ; do linux_entry "${OS}" "${version}" "${xen_version}" advanced \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" "${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}" + for supported_init in ${SUPPORTED_INITS}; do + init_path="${supported_init#*:}" + if [ -x "${init_path}" ] && [ "$(readlink -f /sbin/init)" != "$(readlink -f "${init_path}")" ]; then + linux_entry "${OS}" "${version}" "${xen_version}" "init-${supported_init%%:*}" \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} init=${init_path}" "${GRUB_CMDLINE_XEN} ${GRUB_CMDLINE_XEN_DEFAULT}" + + fi + done if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then linux_entry "${OS}" "${version}" "${xen_version}" recovery \ "single ${GRUB_CMDLINE_LINUX}" "${GRUB_CMDLINE_XEN}" From 3773848cf605c69e54ad802ebf05e3de596eb5aa Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 18 Sep 2016 17:26:00 +0100 Subject: [PATCH 37/52] syslinux_test: Fix out-of-tree build handling When doing out-of-tree builds, abs_top_srcdir may well contain ".." segments, and grub-syslinux2cfg canonicalises its --root argument. As a result, the expansion of @abs_top_srcdir@ may not match what grub-syslinux2cfg produces. It's somewhat difficult to portably canonicalize a path in shell, and autoconf/automake don't offer any support for this. But there's a much simpler option: copy the test data to a temporary directory and make substitutions in the expected output file based on that. Forwarded: http://lists.gnu.org/archive/html/grub-devel/2016-09/msg00013.html Last-Update: 2016-09-19 Patch-Name: syslinux-test-out-of-tree.patch --- Makefile.am | 6 ------ tests/syslinux/ubuntu10.04_grub.cfg.in | 30 +++++++++++++------------- tests/syslinux_test.in | 14 ++++++++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Makefile.am b/Makefile.am index 7795baeb6..f0ab1adc3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -473,9 +473,3 @@ ChangeLog: FORCE fi EXTRA_DIST += ChangeLog ChangeLog-2015 - -syslinux_test: $(top_builddir)/config.status tests/syslinux/ubuntu10.04_grub.cfg - -tests/syslinux/ubuntu10.04_grub.cfg: $(top_builddir)/config.status tests/syslinux/ubuntu10.04_grub.cfg.in - (for x in tests/syslinux/ubuntu10.04_grub.cfg.in ; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:- -CLEANFILES += tests/syslinux/ubuntu10.04_grub.cfg diff --git a/tests/syslinux/ubuntu10.04_grub.cfg.in b/tests/syslinux/ubuntu10.04_grub.cfg.in index 846e4acf0..f285afb1e 100644 --- a/tests/syslinux/ubuntu10.04_grub.cfg.in +++ b/tests/syslinux/ubuntu10.04_grub.cfg.in @@ -1,4 +1,4 @@ - background_image '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux'/'splash.png' + background_image '@dir@/ubuntu10.04/isolinux'/'splash.png' # D-I config version 2.0 # UNSUPPORTED command 'menu hshift 13' # UNSUPPORTED command 'menu width 49' @@ -41,7 +41,7 @@ menuentry 'Test memory' --hotkey 'm' --id 'memtest' { linux$linux_suffix '/'/'/install/mt86plus' } menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/gtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux/gtk.cfg not found # UNSUPPORTED command 'menu begin advanced' # UNSUPPORTED command 'menu title Advanced options' # UNSUPPORTED command 'menu color title * #FFFFFFFF *' @@ -63,15 +63,15 @@ menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { } menuentry 'Back..' --hotkey 'b' --id 'mainmenu' { # UNSUPPORTED command 'menu exit' -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/adgtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux/adgtk.cfg not found # UNSUPPORTED command 'menu end' # UNSUPPORTED entry type 0 true; } menuentry 'Help' --hotkey 'h' --id 'help' { # UNSUPPORTED command 'ui gfxboot bootlogo' -#'@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux'/'prompt.cfg' (host)@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/prompt.cfg: - background_image '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/'/'splash.png' +#'@dir@/ubuntu10.04/isolinux'/'prompt.cfg' (host)@dir@/ubuntu10.04/isolinux/prompt.cfg: + background_image '@dir@/ubuntu10.04/isolinux/'/'splash.png' # UNSUPPORTED command 'display f1.txt' # UNSUPPORTED command 'menu hshift 13' # UNSUPPORTED command 'menu width 49' @@ -114,7 +114,7 @@ menuentry 'Test memory' --hotkey 'm' --id 'memtest' { linux$linux_suffix '/'/'/install/mt86plus' } menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//gtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux//gtk.cfg not found # UNSUPPORTED command 'menu begin advanced' # UNSUPPORTED command 'menu title Advanced options' # UNSUPPORTED command 'menu color title * #FFFFFFFF *' @@ -136,14 +136,14 @@ menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { } menuentry 'Back..' --hotkey 'b' --id 'mainmenu' { # UNSUPPORTED command 'menu exit' -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//adgtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux//adgtk.cfg not found # UNSUPPORTED command 'menu end' # UNSUPPORTED entry type 0 true; } menuentry 'Help' --hotkey 'h' --id 'help' { -#'@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/'/'prompt.cfg' (host)@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/prompt.cfg: - syslinux_configfile -r '/'/'/' -c '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/'/'' '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/'/'prompt.cfg' +#'@dir@/ubuntu10.04/isolinux/'/'prompt.cfg' (host)@dir@/ubuntu10.04/isolinux/prompt.cfg: + syslinux_configfile -r '/'/'/' -c '@dir@/ubuntu10.04/isolinux/'/'' '@dir@/ubuntu10.04/isolinux/'/'prompt.cfg' } menuentry 'menu' --id 'menu' { # UNSUPPORTED command 'f1 f1.txt' @@ -156,8 +156,8 @@ menuentry 'menu' --id 'menu' { # UNSUPPORTED command 'f8 f8.txt' # UNSUPPORTED command 'f9 f9.txt' # UNSUPPORTED command 'f0 f10.txt' -#'@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/'/'isolinux.cfg' (host)@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/isolinux.cfg: - background_image '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//'/'splash.png' +#'@dir@/ubuntu10.04/isolinux/'/'isolinux.cfg' (host)@dir@/ubuntu10.04/isolinux/isolinux.cfg: + background_image '@dir@/ubuntu10.04/isolinux//'/'splash.png' # D-I config version 2.0 # UNSUPPORTED command 'menu hshift 13' # UNSUPPORTED command 'menu width 49' @@ -200,7 +200,7 @@ menuentry 'Test memory' --hotkey 'm' --id 'memtest' { linux$linux_suffix '/'/'/install/mt86plus' } menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux///gtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux///gtk.cfg not found # UNSUPPORTED command 'menu begin advanced' # UNSUPPORTED command 'menu title Advanced options' # UNSUPPORTED command 'menu color title * #FFFFFFFF *' @@ -222,15 +222,15 @@ menuentry 'Boot from first hard disk' --hotkey 'b' --id 'hd' { } menuentry 'Back..' --hotkey 'b' --id 'mainmenu' { # UNSUPPORTED command 'menu exit' -# File (host)/@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux///adgtk.cfg not found +# File (host)/@dir@/ubuntu10.04/isolinux///adgtk.cfg not found # UNSUPPORTED command 'menu end' # UNSUPPORTED entry type 0 true; } menuentry 'Help' --hotkey 'h' --id 'help' { # UNSUPPORTED command 'ui gfxboot bootlogo' -#'@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//'/'prompt.cfg' (host)@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/prompt.cfg: - syslinux_configfile -r '/'/'/' -c '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//'/'' '@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux//'/'prompt.cfg' +#'@dir@/ubuntu10.04/isolinux//'/'prompt.cfg' (host)@dir@/ubuntu10.04/isolinux/prompt.cfg: + syslinux_configfile -r '/'/'/' -c '@dir@/ubuntu10.04/isolinux//'/'' '@dir@/ubuntu10.04/isolinux//'/'prompt.cfg' } } } diff --git a/tests/syslinux_test.in b/tests/syslinux_test.in index fc4edd8ef..4ba94c576 100644 --- a/tests/syslinux_test.in +++ b/tests/syslinux_test.in @@ -2,15 +2,21 @@ set -e +tdir="`mktemp -d "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 outfile="`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"`" || exit 1 -"@builddir@/grub-syslinux2cfg" -r "@abs_top_srcdir@/tests/syslinux/ubuntu10.04" "@abs_top_srcdir@/tests/syslinux/ubuntu10.04/isolinux/isolinux.cfg" -o "$outfile" +cp -a "@top_srcdir@/tests/syslinux/ubuntu10.04" "$tdir/ubuntu10.04" -echo "$outfile" +"@builddir@/grub-syslinux2cfg" -r "$tdir/ubuntu10.04" "$tdir/ubuntu10.04/isolinux/isolinux.cfg" -o "$tdir/observed.cfg" -if ! diff -u "$outfile" "@builddir@/tests/syslinux/ubuntu10.04_grub.cfg"; then +echo "$tdir/observed.cfg" + +sed -e "s,@dir@,$tdir,g" "@top_srcdir@/tests/syslinux/ubuntu10.04_grub.cfg.in" >"$tdir/expected.cfg" +if ! diff -u "$tdir/observed.cfg" "$tdir/expected.cfg"; then echo "Mismatch in ubuntu10.04" - exit 1; + rm -rf "$tdir" + exit 1 fi +rm -rf "$tdir" exit 0 From 821844713b075e5e27b49db307896a3ccb010c51 Mon Sep 17 00:00:00 2001 From: Chad MILLER Date: Thu, 27 Oct 2016 17:15:07 -0400 Subject: [PATCH 38/52] Tell zpool to emit full device names zfs-initramfs currently provides extraneous, undesired symlinks to devices directly underneath /dev/ to satisfy zpool's historical output of unqualified device names. By including this environment variable to signal our intent to zpool, zfs-linux packages can drop the symlink behavior when updating to its upstream or backported output behavior. Bug: https://savannah.gnu.org/bugs/?43653 Bug-Debian: https://bugs.debian.org/824974 Bug-Ubuntu: https://bugs.launchpad.net/bugs/1527727 Last-Update: 2016-11-01 Patch-Name: zpool_full_device_name.patch --- grub-core/osdep/unix/getroot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index 4bf37b027..d5fde7f86 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -240,6 +240,7 @@ grub_util_find_root_devices_from_poolname (char *poolname) argv[2] = poolname; argv[3] = NULL; + setenv ("ZPOOL_VDEV_NAME_PATH", "YES", 1); pid = grub_util_exec_pipe (argv, &fd); if (!pid) return NULL; From d1029b7aa12849339d1f23dfecb17afeb4fa292b Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Thu, 27 Oct 2016 17:33:07 -0400 Subject: [PATCH 39/52] misc: fix invalid character recongition in strto*l Would previously allow digits larger than the base and didn't check that subtracting the difference from 0-9 to lowercase letters for characters larger than 9 didn't result in a value lower than 9, which allowed the parses: ` = 9, _ = 8, ^ = 7, ] = 6, \ = 5, and [ = 4 Patch-Name: misc-fix-invalid-char-strtol.patch --- grub-core/kern/misc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index d1a54df6c..3a14d679e 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -394,9 +394,13 @@ grub_strtoull (const char *str, char **end, int base) if (digit > 9) { digit += '0' - 'a' + 10; - if (digit >= (unsigned long) base) + /* digit <= 9 check is needed to keep chars larger than + '9' but less than 'a' from being read as numbers */ + if (digit >= (unsigned long) base || digit <= 9) break; } + if (digit >= (unsigned long) base) + break; found = 1; From 8fef2df852f48b22897dd016186cc2d9f996c0da Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Thu, 27 Oct 2016 17:39:49 -0400 Subject: [PATCH 40/52] net: read bracketed ipv6 addrs and port numbers Allow specifying port numbers for http and tftp paths, and allow ipv6 addresses to be recognized with brackets around them, which is required to specify a port number Patch-Name: net_read_bracketed_ipv6_addr.patch --- grub-core/net/http.c | 21 ++++++++-- grub-core/net/net.c | 93 +++++++++++++++++++++++++++++++++++++++++--- grub-core/net/tftp.c | 6 ++- include/grub/net.h | 1 + 4 files changed, 110 insertions(+), 11 deletions(-) diff --git a/grub-core/net/http.c b/grub-core/net/http.c index 5aa4ad3be..f182d7b87 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -312,12 +312,14 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) int i; struct grub_net_buff *nb; grub_err_t err; + char* server = file->device->net->server; + int port = file->device->net->port; nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + sizeof ("GET ") - 1 + grub_strlen (data->filename) + sizeof (" HTTP/1.1\r\nHost: ") - 1 - + grub_strlen (file->device->net->server) + + grub_strlen (server) + sizeof (":XXXXXXXXXX") + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1 + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX" @@ -356,7 +358,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) sizeof (" HTTP/1.1\r\nHost: ") - 1); ptr = nb->tail; - err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); + err = grub_netbuff_put (nb, grub_strlen (server)); if (err) { grub_netbuff_free (nb); @@ -365,6 +367,15 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) grub_memcpy (ptr, file->device->net->server, grub_strlen (file->device->net->server)); + if (port) + { + ptr = nb->tail; + grub_snprintf ((char *) ptr, + sizeof (":XXXXXXXXXX"), + ":%d", + port); + } + ptr = nb->tail; err = grub_netbuff_put (nb, sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") @@ -390,8 +401,10 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) grub_netbuff_put (nb, 2); grub_memcpy (ptr, "\r\n", 2); - data->sock = grub_net_tcp_open (file->device->net->server, - HTTP_PORT, http_receive, + grub_dprintf ("http", "opening path %s on host %s TCP port %d\n", + data->filename, server, port ? port : HTTP_PORT); + data->sock = grub_net_tcp_open (server, + port ? port : HTTP_PORT, http_receive, http_err, http_err, file); if (!data->sock) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 10773fc34..fbac15e0f 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -437,6 +437,12 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) grub_uint16_t newip[8]; const char *ptr = val; int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') { + bracketed = 1; + ptr++; + } if (ptr[0] == ':' && ptr[1] != ':') return 0; @@ -475,6 +481,9 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); } grub_memcpy (ip, newip, 16); + if (bracketed && *ptr == ']') { + ptr++; + } if (rest) *rest = ptr; return 1; @@ -1260,8 +1269,10 @@ grub_net_open_real (const char *name) { grub_net_app_level_t proto; const char *protname, *server; + char *host; grub_size_t protnamelen; int try; + int port = 0; if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) { @@ -1299,6 +1310,72 @@ grub_net_open_real (const char *name) return NULL; } + char* port_start; + /* ipv6 or port specified? */ + if ((port_start = grub_strchr (server, ':'))) + { + char* ipv6_begin; + if((ipv6_begin = grub_strchr (server, '['))) + { + char* ipv6_end = grub_strchr (server, ']'); + if(!ipv6_end) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("mismatched [ in address")); + return NULL; + } + /* port number after bracketed ipv6 addr */ + if(ipv6_end[1] == ':') + { + port = grub_strtoul (ipv6_end + 2, NULL, 10); + if(port > 65535) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("bad port number")); + return NULL; + } + } + host = grub_strndup (ipv6_begin, (ipv6_end - ipv6_begin) + 1); + } + else + { + if (grub_strchr (port_start + 1, ':')) + { + int iplen = grub_strlen (server); + /* bracket bare ipv6 addrs */ + host = grub_malloc (iplen + 3); + if(!host) + { + return NULL; + } + host[0] = '['; + grub_memcpy (host + 1, server, iplen); + host[iplen + 1] = ']'; + host[iplen + 2] = '\0'; + } + else + { + /* hostname:port or ipv4:port */ + port = grub_strtol (port_start + 1, NULL, 10); + if(port > 65535) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("bad port number")); + return NULL; + } + host = grub_strndup (server, port_start - server); + } + } + } + else + { + host = grub_strdup (server); + } + if (!host) + { + return NULL; + } + for (try = 0; try < 2; try++) { FOR_NET_APP_LEVEL (proto) @@ -1308,15 +1385,19 @@ grub_net_open_real (const char *name) { grub_net_t ret = grub_zalloc (sizeof (*ret)); if (!ret) - return NULL; - ret->protocol = proto; - ret->server = grub_strdup (server); - if (!ret->server) + grub_free (host); + if (host) { - grub_free (ret); - return NULL; + ret->server = grub_strdup (host); + if (!ret->server) + { + grub_free (ret); + return NULL; + } } ret->fs = &grub_net_fs; + ret->protocol = proto; + ret->port = port; return ret; } } diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 7d90bf66e..a0817a075 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -314,6 +314,7 @@ tftp_open (struct grub_file *file, const char *filename) grub_err_t err; grub_uint8_t *nbd; grub_net_network_level_address_t addr; + int port = file->device->net->port; data = grub_zalloc (sizeof (*data)); if (!data) @@ -382,13 +383,16 @@ tftp_open (struct grub_file *file, const char *filename) err = grub_net_resolve_address (file->device->net->server, &addr); if (err) { + grub_dprintf ("tftp", "file_size is %llu, block_size is %llu\n", + (unsigned long long)data->file_size, + (unsigned long long)data->block_size); destroy_pq (data); grub_free (data); return err; } data->sock = grub_net_udp_open (addr, - TFTP_SERVER_PORT, tftp_receive, + port ? port : TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) { diff --git a/include/grub/net.h b/include/grub/net.h index 2192fa186..ccc169c2d 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -270,6 +270,7 @@ typedef struct grub_net { char *server; char *name; + int port; grub_net_app_level_t protocol; grub_net_packets_t packs; grub_off_t offset; From 7c754fff55f261e39d25da72413213600b2577c3 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 27 Oct 2016 17:41:04 -0400 Subject: [PATCH 41/52] bootp: New net_bootp6 command Implement new net_bootp6 command for IPv6 network auto configuration via the DHCPv6 protocol (RFC3315). Signed-off-by: Michael Chang Signed-off-by: Ken Lin Patch-Name: bootp_new_net_bootp6_command.patch --- grub-core/net/bootp.c | 908 +++++++++++++++++++++++++++++++++++++++++- grub-core/net/ip.c | 39 ++ include/grub/net.h | 72 ++++ 3 files changed, 1018 insertions(+), 1 deletion(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 9e2fdb795..172528ee8 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -24,6 +24,98 @@ #include #include #include +#include +#include + +static int +dissect_url (const char *url, char **proto, char **host, char **path) +{ + const char *p, *ps; + grub_size_t l; + + *proto = *host = *path = NULL; + ps = p = url; + + while ((p = grub_strchr (p, ':'))) + { + if (grub_strlen (p) < sizeof ("://") - 1) + break; + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) + { + l = p - ps; + *proto = grub_malloc (l + 1); + if (!*proto) + { + grub_print_error (); + return 0; + } + + grub_memcpy (*proto, ps, l); + (*proto)[l] = '\0'; + p += sizeof ("://") - 1; + break; + } + ++p; + } + + if (!*proto) + { + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); + return 0; + } + + ps = p; + p = grub_strchr (p, '/'); + + if (!p) + { + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); + grub_free (*proto); + *proto = NULL; + return 0; + } + + l = p - ps; + + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') + { + *host = grub_malloc (l - 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps + 1, l - 2); + (*host)[l - 2] = 0; + } + else + { + *host = grub_malloc (l + 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps, l); + (*host)[l] = 0; + } + + *path = grub_strdup (p); + if (!*path) + { + grub_print_error (); + grub_free (*host); + grub_free (*proto); + *host = NULL; + *proto = NULL; + return 0; + } + return 1; +} static void parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) @@ -270,6 +362,578 @@ grub_net_configure_by_dhcp_ack (const char *name, return inter; } +/* The default netbuff size for sending DHCPv6 packets which should be + large enough to hold the information */ +#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512 + +struct grub_dhcp6_options +{ + grub_uint8_t *client_duid; + grub_uint16_t client_duid_len; + grub_uint8_t *server_duid; + grub_uint16_t server_duid_len; + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_net_network_level_address_t *ia_addr; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_net_network_level_address_t *dns_server_addrs; + grub_uint16_t num_dns_server; + char *boot_file_proto; + char *boot_file_server_ip; + char *boot_file_path; +}; + +typedef struct grub_dhcp6_options *grub_dhcp6_options_t; + +struct grub_dhcp6_session +{ + struct grub_dhcp6_session *next; + struct grub_dhcp6_session **prev; + grub_uint32_t iaid; + grub_uint32_t transaction_id:24; + grub_uint64_t start_time; + struct grub_net_dhcp6_option_duid_ll duid; + struct grub_net_network_level_interface *iface; + + /* The associated dhcpv6 options */ + grub_dhcp6_options_t adv; + grub_dhcp6_options_t reply; +}; + +typedef struct grub_dhcp6_session *grub_dhcp6_session_t; + +typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data); + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, + dhcp6_option_hook_fn hook, void *hook_data); + +static void +parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data; + + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + if (code == GRUB_NET_DHCP6_OPTION_IAADDR) + { + const struct grub_net_dhcp6_option_iaaddr *iaaddr; + iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data; + + if (len < sizeof (*iaaddr)) + { + grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len); + return; + } + if (!dhcp6->ia_addr) + { + dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr)); + dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr); + dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8); + dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime); + dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime); + } + } +} + +static void +parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data; + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + switch (code) + { + case GRUB_NET_DHCP6_OPTION_CLIENTID: + + if (dhcp6->client_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len); + break; + } + dhcp6->client_duid = grub_malloc (len); + grub_memcpy (dhcp6->client_duid, opt->data, len); + dhcp6->client_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_SERVERID: + + if (dhcp6->server_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len); + break; + } + dhcp6->server_duid = grub_malloc (len); + grub_memcpy (dhcp6->server_duid, opt->data, len); + dhcp6->server_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_IA_NA: + { + const struct grub_net_dhcp6_option_iana *ia_na; + grub_uint16_t data_len; + + if (dhcp6->iaid || len < sizeof (*ia_na)) + { + grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len); + break; + } + ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data; + dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid); + dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1); + dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2); + + data_len = len - sizeof (*ia_na); + if (data_len) + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6); + } + break; + + case GRUB_NET_DHCP6_OPTION_DNS_SERVERS: + { + const grub_uint8_t *po; + grub_uint16_t ln; + grub_net_network_level_address_t *la; + + if (!len || len & 0xf) + { + grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n"); + break; + } + dhcp6->num_dns_server = ln = len >> 4; + dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la)); + + for (po = opt->data; ln > 0; po += 0x10, la++, ln--) + { + la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + la->ipv6[0] = grub_get_unaligned64 (po); + la->ipv6[1] = grub_get_unaligned64 (po + 8); + la->option = DNS_OPTION_PREFER_IPV6; + } + } + break; + + case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL: + dissect_url ((const char *)opt->data, + &dhcp6->boot_file_proto, + &dhcp6->boot_file_server_ip, + &dhcp6->boot_file_path); + break; + + default: + break; + } +} + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data) +{ + while (size) + { + grub_uint16_t code, len; + + if (size < sizeof (*opt)) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size); + break; + } + size -= sizeof (*opt); + len = grub_be_to_cpu16 (opt->len); + code = grub_be_to_cpu16 (opt->code); + if (size < len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code); + break; + } + if (!len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code); + break; + } + else + { + if (hook) + hook (opt, hook_data); + size -= len; + opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt)); + } + } +} + +static grub_dhcp6_options_t +grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h, + grub_size_t size) +{ + grub_dhcp6_options_t options; + + if (size < sizeof (*v6h)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); + return NULL; + } + + options = grub_zalloc (sizeof(*options)); + if (!options) + return NULL; + + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options, + size - sizeof (*v6h), parse_dhcp6_option, options); + + return options; +} + +static void +grub_dhcp6_options_free (grub_dhcp6_options_t options) +{ + if (options->client_duid) + grub_free (options->client_duid); + if (options->server_duid) + grub_free (options->server_duid); + if (options->ia_addr) + grub_free (options->ia_addr); + if (options->dns_server_addrs) + grub_free (options->dns_server_addrs); + if (options->boot_file_proto) + grub_free (options->boot_file_proto); + if (options->boot_file_server_ip) + grub_free (options->boot_file_server_ip); + if (options->boot_file_path) + grub_free (options->boot_file_path); + + grub_free (options); +} + +static grub_dhcp6_session_t grub_dhcp6_sessions; +#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions) + +static void +grub_net_configure_by_dhcp6_info (const char *name, + struct grub_net_card *card, + grub_dhcp6_options_t dhcp6, + int is_def, + int flags, + struct grub_net_network_level_interface **ret_inf) +{ + grub_net_network_level_netaddress_t netaddr; + struct grub_net_network_level_interface *inf; + + if (dhcp6->ia_addr) + { + inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags); + + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0]; + netaddr.ipv6.base[1] = 0; + netaddr.ipv6.masksize = 64; + grub_net_add_route (name, netaddr, inf); + + if (ret_inf) + *ret_inf = inf; + } + + if (dhcp6->dns_server_addrs) + { + grub_uint16_t i; + + for (i = 0; i < dhcp6->num_dns_server; ++i) + grub_net_add_dns_server (dhcp6->dns_server_addrs + i); + } + + if (dhcp6->boot_file_path) + grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path, + grub_strlen (dhcp6->boot_file_path)); + + if (is_def && dhcp6->boot_file_server_ip) + { + grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } +} + +static void +grub_dhcp6_session_add (struct grub_net_network_level_interface *iface, + grub_uint32_t iaid) +{ + grub_dhcp6_session_t se; + struct grub_datetime date; + grub_err_t err; + grub_int32_t t = 0; + + se = grub_malloc (sizeof (*se)); + + err = grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + t = 0; + } + + se->iface = iface; + se->iaid = iaid; + se->transaction_id = t; + se->start_time = grub_get_time_ms (); + se->duid.type = grub_cpu_to_be16_compile_time (3) ; + se->duid.hw_type = grub_cpu_to_be16_compile_time (1); + grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr)); + se->adv = NULL; + se->reply = NULL; + grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se)); +} + +static void +grub_dhcp6_session_remove (grub_dhcp6_session_t se) +{ + grub_list_remove (GRUB_AS_LIST (se)); + if (se->adv) + grub_dhcp6_options_free (se->adv); + if (se->reply) + grub_dhcp6_options_free (se->reply); + grub_free (se); +} + +static void +grub_dhcp6_session_remove_all (void) +{ + grub_dhcp6_session_t se; + + FOR_DHCP6_SESSIONS (se) + { + grub_dhcp6_session_remove (se); + se = grub_dhcp6_sessions; + } +} + +static grub_err_t +grub_dhcp6_session_configure_network (grub_dhcp6_session_t se) +{ + char *name; + + name = grub_xasprintf ("%s:dhcp6", se->iface->card->name); + if (!name) + return grub_errno; + + grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0); + grub_free (name); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_dhcp6_session_send_request (grub_dhcp6_session_t se) +{ + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_iana *ia_na; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + struct udphdr *udph; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + grub_uint64_t elapsed; + struct grub_net_network_level_interface *inf = se->iface; + grub_dhcp6_options_t dhcp6 = se->adv; + grub_err_t err = GRUB_ERR_NONE; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); + if (err) + return err; + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + return grub_errno; + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len); + grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len); + + err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID); + opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len); + grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len); + + err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + if (dhcp6->ia_addr) + { + err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + if (dhcp6->ia_addr) + opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt)); + + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid); + + ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1); + ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2); + + if (dhcp6->ia_addr) + { + opt = (struct grub_net_dhcp6_option *)ia_na->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16 (sizeof (*iaaddr)); + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data; + grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]); + grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]); + + iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime); + iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime); + } + + err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO); + opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)); + grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + + /* the time is expressed in hundredths of a second */ + elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0); + + if (elapsed > 0xffff) + elapsed = 0xffff; + + grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed)); + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *) nb->data; + v6h->message_type = GRUB_NET_DHCP6_REQUEST; + v6h->transaction_id = se->transaction_id; + + err = grub_netbuff_push (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, + GRUB_NET_IP_UDP); + + grub_netbuff_free (nb); + + return err; +} + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6h, + grub_size_t size, + int is_def, + char **device, char **path) +{ + struct grub_net_network_level_interface *inf; + grub_dhcp6_options_t dhcp6; + + dhcp6 = grub_dhcp6_options_get (v6h, size); + if (!dhcp6) + { + grub_print_error (); + return NULL; + } + + grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf); + + if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip) + { + *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip); + grub_print_error (); + } + if (path && dhcp6->boot_file_path) + { + *path = grub_strdup (dhcp6->boot_file_path); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + + grub_dhcp6_options_free (dhcp6); + return inf; +} + void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_card *card) @@ -302,6 +966,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb, } } +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card __attribute__ ((unused))) +{ + const struct grub_net_dhcp6_packet *v6h; + grub_dhcp6_session_t se; + grub_size_t size; + grub_dhcp6_options_t options; + + v6h = (const struct grub_net_dhcp6_packet *) nb->data; + size = nb->tail - nb->data; + + options = grub_dhcp6_options_get (v6h, size); + if (!options) + return grub_errno; + + if (!options->client_duid || !options->server_duid || !options->ia_addr) + { + grub_dhcp6_options_free (options); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet"); + } + + FOR_DHCP6_SESSIONS (se) + { + if (se->transaction_id == v6h->transaction_id && + grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 && + se->iaid == options->iaid) + break; + } + + if (!se) + { + grub_dprintf ("bootp", "DHCPv6 session not found\n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE) + { + if (se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->adv = options; + return grub_dhcp6_session_send_request (se); + } + else if (v6h->message_type == GRUB_NET_DHCP6_REPLY) + { + if (!se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->reply = options; + grub_dhcp6_session_configure_network (se); + grub_dhcp6_session_remove (se); + return GRUB_ERR_NONE; + } + else + { + grub_dhcp6_options_free (options); + } + + return GRUB_ERR_NONE; +} + static char hexdigit (grub_uint8_t val) { @@ -582,7 +1317,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd_getdhcp, cmd_bootp; +static grub_err_t +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + grub_uint32_t iaid = 0; + int interval; + grub_err_t err; + grub_dhcp6_session_t se; + + err = GRUB_ERR_NONE; + + FOR_NET_CARDS (card) + { + struct grub_net_network_level_interface *iface; + + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + + iface = grub_net_ipv6_get_link_local (card, &card->default_address); + if (!iface) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } + + grub_dhcp6_session_add (iface, iaid++); + } + + for (interval = 200; interval < 10000; interval *= 2) + { + int done = 1; + + FOR_DHCP6_SESSIONS (se) + { + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_duid_ll *duid; + struct grub_net_dhcp6_option_iana *ia_na; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + struct udphdr *udph; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (se->iface, + &multicast, &ll_multicast); + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, 0); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (sizeof (*duid)); + + duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data; + grub_memcpy (duid, &se->duid, sizeof (*duid)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (se->iaid); + ia_na->t1 = 0; + ia_na->t2 = 0; + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *)nb->data; + v6h->message_type = GRUB_NET_DHCP6_SOLICIT; + v6h->transaction_id = se->transaction_id; + + grub_netbuff_push (nb, sizeof (*udph)); + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &se->iface->address, &multicast); + + err = grub_net_send_ip_packet (se->iface, &multicast, + &ll_multicast, nb, GRUB_NET_IP_UDP); + done = 0; + grub_netbuff_free (nb); + + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + } + if (!done) + grub_net_poll_cards (interval, 0); + } + + FOR_DHCP6_SESSIONS (se) + { + grub_error_push (); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("couldn't autoconfigure %s"), + se->iface->card->name); + } + + grub_dhcp6_session_remove_all (); + + return err; +} + +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_bootp6; void grub_bootp_init (void) @@ -593,6 +1495,9 @@ grub_bootp_init (void) cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, N_("VAR INTERFACE NUMBER DESCRIPTION"), N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6, + N_("[CARD]"), + N_("perform a DHCPv6 autoconfiguration")); } void @@ -600,4 +1505,5 @@ grub_bootp_fini (void) { grub_unregister_command (cmd_getdhcp); grub_unregister_command (cmd_bootp); + grub_unregister_command (cmd_bootp6); } diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index aba4f8908..59970ab74 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -238,6 +238,45 @@ handle_dgram (struct grub_net_buff *nb, { struct udphdr *udph; udph = (struct udphdr *) nb->data; + + if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT)) + { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_UDP, + source, + dest); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_net_process_dhcp6 (nb, card); + if (err) + grub_print_error (); + + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { const struct grub_net_bootp_packet *bootp; diff --git a/include/grub/net.h b/include/grub/net.h index ccc169c2d..38a3973f3 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -442,6 +442,66 @@ struct grub_net_bootp_packet grub_uint8_t vendor[0]; } GRUB_PACKED; +struct grub_net_dhcp6_packet +{ + grub_uint32_t message_type:8; + grub_uint32_t transaction_id:24; + grub_uint8_t dhcp_options[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option { + grub_uint16_t code; + grub_uint16_t len; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_iana { + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_iaaddr { + grub_uint8_t addr[16]; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_duid_ll +{ + grub_uint16_t type; + grub_uint16_t hw_type; + grub_uint8_t hwaddr[6]; +} GRUB_PACKED; + +enum + { + GRUB_NET_DHCP6_SOLICIT = 1, + GRUB_NET_DHCP6_ADVERTISE = 2, + GRUB_NET_DHCP6_REQUEST = 3, + GRUB_NET_DHCP6_REPLY = 7 + }; + +enum + { + DHCP6_CLIENT_PORT = 546, + DHCP6_SERVER_PORT = 547 + }; + +enum + { + GRUB_NET_DHCP6_OPTION_CLIENTID = 1, + GRUB_NET_DHCP6_OPTION_SERVERID = 2, + GRUB_NET_DHCP6_OPTION_IA_NA = 3, + GRUB_NET_DHCP6_OPTION_IAADDR = 5, + GRUB_NET_DHCP6_OPTION_ORO = 6, + GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8, + GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23, + GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59 + }; + #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 @@ -468,6 +528,14 @@ grub_net_configure_by_dhcp_ack (const char *name, grub_size_t size, int is_def, char **device, char **path); +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6, + grub_size_t size, + int is_def, char **device, char **path); + grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, int mask); @@ -476,6 +544,10 @@ void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_card *card); +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card); + int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, const grub_net_link_level_address_t *b); From d948a72a7c909b18c424ea732aec2788705646b5 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 27 Oct 2016 17:41:21 -0400 Subject: [PATCH 42/52] efinet: UEFI IPv6 PXE support When grub2 image is booted from UEFI IPv6 PXE, the DHCPv6 Reply packet is cached in firmware buffer which can be obtained by PXE Base Code protocol. The network interface can be setup through the parameters in that obtained packet. Signed-off-by: Michael Chang Signed-off-by: Ken Lin Patch-Name: efinet_uefi_ipv6_pxe_support.patch --- grub-core/net/drivers/efi/efinet.c | 24 ++++++++++--- include/grub/efi/api.h | 55 +++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 5388f952b..fc90415f2 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -378,11 +378,25 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, if (! pxe) continue; pxe_mode = pxe->mode; - grub_net_configure_by_dhcp_ack (card->name, card, 0, - (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), - 1, device, path); + + if (pxe_mode->using_ipv6) + { + grub_net_configure_by_dhcpv6_reply (card->name, card, 0, + (struct grub_net_dhcp6_packet *) + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), + 1, device, path); + if (grub_errno) + grub_print_error (); + } + else + { + grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + &pxe_mode->dhcp_ack, + sizeof (pxe_mode->dhcp_ack), + 1, device, path); + } return; } } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index c7c9f0e1d..92f9b5a6f 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1452,14 +1452,67 @@ typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; +typedef struct { + grub_uint8_t addr[4]; +} grub_efi_pxe_ipv4_address_t; + +typedef struct { + grub_uint8_t addr[16]; +} grub_efi_pxe_ipv6_address_t; + +typedef struct { + grub_uint8_t addr[32]; +} grub_efi_pxe_mac_address_t; + +typedef union { + grub_uint32_t addr[4]; + grub_efi_pxe_ipv4_address_t v4; + grub_efi_pxe_ipv6_address_t v6; +} grub_efi_pxe_ip_address_t; + +#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 +typedef struct { + grub_uint8_t filters; + grub_uint8_t ip_cnt; + grub_uint16_t reserved; + grub_efi_pxe_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT]; +} grub_efi_pxe_ip_filter_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_mac_address_t mac_addr; +} grub_efi_pxe_arp_entry_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_ip_address_t subnet_mask; + grub_efi_pxe_ip_address_t gw_addr; +} grub_efi_pxe_route_entry_t; + + +#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 +#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 + typedef struct grub_efi_pxe_mode { - grub_uint8_t unused[52]; + grub_uint8_t started; + grub_uint8_t ipv6_available; + grub_uint8_t ipv6_supported; + grub_uint8_t using_ipv6; + grub_uint8_t unused[16]; + grub_efi_pxe_ip_address_t station_ip; + grub_efi_pxe_ip_address_t subnet_mask; grub_efi_pxe_packet_t dhcp_discover; grub_efi_pxe_packet_t dhcp_ack; grub_efi_pxe_packet_t proxy_offer; grub_efi_pxe_packet_t pxe_discover; grub_efi_pxe_packet_t pxe_reply; + grub_efi_pxe_packet_t pxe_bis_reply; + grub_efi_pxe_ip_filter_t ip_filter; + grub_uint32_t arp_cache_entries; + grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; + grub_uint32_t route_table_entries; + grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; } grub_efi_pxe_mode_t; typedef struct grub_efi_pxe From 74c824f2b0d39b6fb5645fd55f9726d5aabc81f1 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 27 Oct 2016 17:42:19 -0400 Subject: [PATCH 43/52] bootp: Add processing DHCPACK packet from HTTP Boot The vendor class identifier with the string "HTTPClient" is used to denote the packet as responding to HTTP boot request. In DHCP4 config, the filename for HTTP boot is the URL of the boot file while for PXE boot it is the path to the boot file. As a consequence, the next-server becomes obseleted because the HTTP URL already contains the server address for the boot file. For DHCP6 config, there's no difference definition in existing config as dhcp6.bootfile-url can be used to specify URL for both HTTP and PXE boot file. This patch adds processing for "HTTPClient" vendor class identifier in DHCPACK packet by treating it as HTTP format, not as the PXE format. Signed-off-by: Michael Chang Signed-off-by: Ken Lin Patch-Name: bootp_process_dhcpack_http_boot.patch --- grub-core/net/bootp.c | 67 +++++++++++++++++++++++++++++++++++++++++-- include/grub/net.h | 1 + 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index 172528ee8..de9239c16 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -207,6 +207,11 @@ parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask) taglength); break; + case GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER: + grub_env_set_net_property (name, "vendor_class_identifier", (const char *) ptr, + taglength); + break; + case GRUB_NET_BOOTP_EXTENSIONS_PATH: grub_env_set_net_property (name, "extensionspath", (const char *) ptr, taglength); @@ -282,6 +287,66 @@ grub_net_configure_by_dhcp_ack (const char *name, } #endif + if (size > OFFSET_OF (vendor, bp)) + { + char *cidvar; + const char *cid; + + parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask); + cidvar = grub_xasprintf ("net_%s_%s", name, "vendor_class_identifier"); + cid = grub_env_get (cidvar); + grub_free (cidvar); + + if (cid && grub_strcmp (cid, "HTTPClient") == 0) + { + char *proto, *ip, *pa; + + if (!dissect_url (bp->boot_file, &proto, &ip, &pa)) + return inter; + + grub_env_set_net_property (name, "boot_file", pa, grub_strlen (pa)); + if (is_def) + { + grub_net_default_server = grub_strdup (ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } + if (device && !*device) + { + *device = grub_xasprintf ("%s,%s", proto, ip); + grub_print_error (); + } + if (path) + { + *path = grub_strdup (pa); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + grub_net_add_ipv4_local (inter, mask); + inter->dhcp_ack = grub_malloc (size); + if (inter->dhcp_ack) + { + grub_memcpy (inter->dhcp_ack, bp, size); + inter->dhcp_acklen = size; + } + else + grub_errno = GRUB_ERR_NONE; + + grub_free (proto); + grub_free (ip); + grub_free (pa); + return inter; + } + } + if (size > OFFSET_OF (boot_file, bp)) grub_env_set_net_property (name, "boot_file", bp->boot_file, sizeof (bp->boot_file)); @@ -346,8 +411,6 @@ grub_net_configure_by_dhcp_ack (const char *name, **path = 0; } } - if (size > OFFSET_OF (vendor, bp)) - parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask); grub_net_add_ipv4_local (inter, mask); inter->dhcp_ack = grub_malloc (size); diff --git a/include/grub/net.h b/include/grub/net.h index 38a3973f3..e4bf678db 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -517,6 +517,7 @@ enum GRUB_NET_BOOTP_DOMAIN = 0x0f, GRUB_NET_BOOTP_ROOT_PATH = 0x11, GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12, + GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER = 0x3C, GRUB_NET_BOOTP_END = 0xff }; From e03383171dce3d640bf78109f633274f5936f1df Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 27 Oct 2016 17:43:05 -0400 Subject: [PATCH 44/52] efinet: Setting network from UEFI device path The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no longer provided for HTTP Boot. Instead, we have to get the HTTP boot information from the device path nodes defined in following UEFI Specification sections. 9.3.5.12 IPv4 Device Path 9.3.5.13 IPv6 Device Path 9.3.5.23 Uniform Resource Identifiers (URI) Device Path This patch basically does: include/grub/efi/api.h: Add new structure of Uniform Resource Identifiers (URI) Device Path grub-core/net/drivers/efi/efinet.c: Check if PXE Base Code is available, if not it will try to obtain the netboot information from the device path where the image booted from. The DHCPACK packet is recoverd from the information in device patch and feed into the same DHCP packet processing functions to ensure the network interface is setting up the same way it used to be. Signed-off-by: Michael Chang Signed-off-by: Ken Lin Patch-Name: efinet_set_network_from_uefi_devpath.patch --- grub-core/net/drivers/efi/efinet.c | 268 ++++++++++++++++++++++++++++- include/grub/efi/api.h | 11 ++ 2 files changed, 270 insertions(+), 9 deletions(-) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index fc90415f2..2d3b00f0e 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -324,6 +325,221 @@ grub_efinet_findcards (void) grub_free (handles); } +static struct grub_net_buff * +grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) +{ + grub_efi_uint16_t uri_len; + grub_efi_device_path_t *ldp, *ddp; + grub_efi_uri_device_path_t *uri_dp; + struct grub_net_buff *nb; + grub_err_t err; + + ddp = grub_efi_duplicate_device_path (dp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_free (ddp); + return NULL; + } + + uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0; + + if (!uri_len) + { + grub_free (ddp); + return NULL; + } + + uri_dp = (grub_efi_uri_device_path_t *) ldp; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + { + grub_free (ddp); + return NULL; + } + + nb = grub_netbuff_alloc (512); + if (!nb) + return NULL; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; + struct grub_net_bootp_packet *bp; + grub_uint8_t *ptr; + + bp = (struct grub_net_bootp_packet *) nb->tail; + err = grub_netbuff_put (nb, sizeof (*bp) + 4); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + if (sizeof(bp->boot_file) < uri_len) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (bp->boot_file, uri_dp->uri, uri_len); + grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip)); + grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip)); + + bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0; + bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1; + bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2; + bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3; + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_NETMASK; + *ptr++ = sizeof (ipv4->subnet_mask); + grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_ROUTER; + *ptr++ = sizeof (ipv4->gateway_ip_address); + grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER; + *ptr++ = sizeof ("HTTPClient") - 1; + grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr = GRUB_NET_BOOTP_END; + *use_ipv6 = 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + { + grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp; + bp->hw_type = mac->if_type; + bp->hw_len = sizeof (bp->mac_addr); + grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len); + } + } + else + { + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp; + + struct grub_net_dhcp6_packet *d6p; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_option_iana *iana; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + + d6p = (struct grub_net_dhcp6_packet *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*d6p)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + d6p->message_type = GRUB_NET_DHCP6_REPLY; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr)); + + err = grub_netbuff_put (nb, sizeof(*iana)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr)); + + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*iaaddr)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address)); + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + uri_len); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL); + opt->len = grub_cpu_to_be16 (uri_len); + grub_memcpy (opt->data, uri_dp->uri, uri_len); + + *use_ipv6 = 1; + } + + grub_free (ddp); + return nb; +} + static void grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, char **path) @@ -340,6 +556,11 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, grub_efi_device_path_t *cdp; struct grub_efi_pxe *pxe; struct grub_efi_pxe_mode *pxe_mode; + grub_uint8_t *packet_buf; + grub_size_t packet_bufsz ; + int ipv6; + struct grub_net_buff *nb = NULL; + if (card->driver != &efidriver) continue; cdp = grub_efi_get_device_path (card->efi_handle); @@ -359,11 +580,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, ldp = grub_efi_find_last_device_path (dp); if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE - && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) continue; dup_dp = grub_efi_duplicate_device_path (dp); if (!dup_dp) continue; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + dup_ldp = grub_efi_find_last_device_path (dup_dp); dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -375,16 +606,31 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, } pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (! pxe) - continue; - pxe_mode = pxe->mode; + if (!pxe) + { + nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6); + if (!nb) + { + grub_print_error (); + continue; + } + packet_buf = nb->head; + packet_bufsz = nb->tail - nb->head; + } + else + { + pxe_mode = pxe->mode; + packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack; + packet_bufsz = sizeof (pxe_mode->dhcp_ack); + ipv6 = pxe_mode->using_ipv6; + } - if (pxe_mode->using_ipv6) + if (ipv6) { grub_net_configure_by_dhcpv6_reply (card->name, card, 0, (struct grub_net_dhcp6_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); if (grub_errno) grub_print_error (); @@ -393,10 +639,14 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, { grub_net_configure_by_dhcp_ack (card->name, card, 0, (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), + packet_buf, + packet_bufsz, 1, device, path); } + + if (nb) + grub_netbuff_free (nb); + return; } } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 92f9b5a6f..d5a12569b 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -825,6 +825,8 @@ struct grub_efi_ipv4_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_ipv4_address_t gateway_ip_address; + grub_efi_ipv4_address_t subnet_mask; } GRUB_PACKED; typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t; @@ -879,6 +881,15 @@ struct grub_efi_sata_device_path } GRUB_PACKED; typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t; +#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24 + +struct grub_efi_uri_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t uri[0]; +} GRUB_PACKED; +typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 /* Media Device Path. */ From ca0032fc42ff1ee941a77b5b6fdca866c3b94b5b Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 27 Oct 2016 17:43:21 -0400 Subject: [PATCH 45/52] efinet: Setting DNS server from UEFI protocol In the URI device path node, any name rahter than address can be used for looking up the resources so that DNS service become needed to get answer of the name's address. Unfortunately the DNS is not defined in any of the device path nodes so that we use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain it. These two protcols are defined the sections of UEFI specification. 27.5 EFI IPv4 Configuration II Protocol 27.7 EFI IPv6 Configuration Protocol include/grub/efi/api.h: Add new structure and protocol UUID of EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL. grub-core/net/drivers/efi/efinet.c: Use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain the list of DNS server address for IPv4 and IPv6 respectively. The address of DNS servers is structured into DHCPACK packet and feed into the same DHCP packet processing functions to ensure the network interface is setting up the same way it used to be. Signed-off-by: Michael Chang Signed-off-by: Ken Lin Patch-Name: efinet_set_dns_from_uefi_proto.patch --- grub-core/net/drivers/efi/efinet.c | 163 +++++++++++++++++++++++++++++ include/grub/efi/api.h | 76 ++++++++++++++ 2 files changed, 239 insertions(+) diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 2d3b00f0e..82a28fb6e 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -30,6 +30,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* GUID. */ static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID; static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; +static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; +static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; static grub_err_t send_card_buffer (struct grub_net_card *dev, @@ -325,6 +327,125 @@ grub_efinet_findcards (void) grub_free (handles); } +static grub_efi_handle_t +grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) +{ + grub_efi_handle_t handle; + grub_efi_status_t status; + + status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path, + protocol, &device_path, &handle); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (r_device_path) + *r_device_path = device_path; + + return handle; +} + +static grub_efi_ipv4_address_t * +grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip4_config2_protocol_t *conf; + grub_efi_ipv4_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t); + + hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip4_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv4_address_t); + return addrs; +} + +static grub_efi_ipv6_address_t * +grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip6_config_protocol_t *conf; + grub_efi_ipv6_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t); + + hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip6_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = efi_call_4 (conf->get_data, conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv6_address_t); + return addrs; +} + static struct grub_net_buff * grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) { @@ -377,6 +498,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; struct grub_net_bootp_packet *bp; grub_uint8_t *ptr; + grub_efi_ipv4_address_t *dns; + grub_efi_uintn_t num_dns; bp = (struct grub_net_bootp_packet *) nb->tail; err = grub_netbuff_put (nb, sizeof (*bp) + 4); @@ -438,6 +561,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u *ptr++ = sizeof ("HTTPClient") - 1; grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + dns = grub_dns_server_ip4_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + ptr = nb->tail; + err = grub_netbuff_put (nb, size_dns + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_DNS; + *ptr++ = size_dns; + grub_memcpy (ptr, dns, size_dns); + grub_free (dns); + } + ptr = nb->tail; err = grub_netbuff_put (nb, 1); if (err) @@ -470,6 +612,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u struct grub_net_dhcp6_option *opt; struct grub_net_dhcp6_option_iana *iana; struct grub_net_dhcp6_option_iaaddr *iaaddr; + grub_efi_ipv6_address_t *dns; + grub_efi_uintn_t num_dns; d6p = (struct grub_net_dhcp6_packet *)nb->tail; err = grub_netbuff_put (nb, sizeof(*d6p)); @@ -533,6 +677,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u opt->len = grub_cpu_to_be16 (uri_len); grub_memcpy (opt->data, uri_dp->uri, uri_len); + dns = grub_dns_server_ip6_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + size_dns); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS); + opt->len = grub_cpu_to_be16 (size_dns); + grub_memcpy (opt->data, dns, size_dns); + grub_free (dns); + } + *use_ipv6 = 1; } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index d5a12569b..99ba068e3 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -334,6 +334,16 @@ { 0x8B, 0x8C, 0xE2, 0x1B, 0x01, 0xAE, 0xF2, 0xB7 } \ } +#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \ + { 0x5b446ed1, 0xe30b, 0x4faa, \ + { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \ + } + +#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \ + { 0x937fe521, 0x95ae, 0x4d1a, \ + { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; @@ -1749,6 +1759,72 @@ struct grub_efi_block_io }; typedef struct grub_efi_block_io grub_efi_block_io_t; +enum grub_efi_ip4_config2_data_type { + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t; + +struct grub_efi_ip4_config2_protocol +{ + grub_efi_status_t (*set_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (*get_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (*register_data_notify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; + +enum grub_efi_ip6_config_data_type { + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t; + +struct grub_efi_ip6_config_protocol +{ + grub_efi_status_t (*set_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (*get_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (*register_data_notify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; + #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) From af3887505e8ae435eda3a431bd401494001cfceb Mon Sep 17 00:00:00 2001 From: Steve McIntyre <93sam@debian.org> Date: Mon, 30 Jan 2017 19:04:51 +0000 Subject: [PATCH 46/52] Make grub-install check for errors from efibootmgr Code is currently ignoring errors from efibootmgr, giving users clearly bogus output like: Setting up grub-efi-amd64 (2.02~beta3-4) ... Installing for x86_64-efi platform. Could not delete variable: No space left on device Could not prepare Boot variable: No space left on device Installation finished. No error reported. and then potentially unbootable systems. If efibootmgr fails, grub-install should know that and report it! Signed-off-by: Steve McIntyre <93sam@debian.org> Bug-Debian: https://bugs.debian.org/853234 Forwarded: https://lists.gnu.org/archive/html/grub-devel/2017-01/msg00107.html Patch-Name: grub-install-efibootmgr-check.patch --- grub-core/osdep/unix/platform.c | 24 +++++++++++++++--------- include/grub/util/install.h | 2 +- util/grub-install.c | 14 +++++++++++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/grub-core/osdep/unix/platform.c b/grub-core/osdep/unix/platform.c index 28cb37e15..f9c376c35 100644 --- a/grub-core/osdep/unix/platform.c +++ b/grub-core/osdep/unix/platform.c @@ -78,19 +78,20 @@ get_ofpathname (const char *dev) dev); } -static void +static int grub_install_remove_efi_entries_by_distributor (const char *efi_distributor) { int fd; pid_t pid = grub_util_exec_pipe ((const char * []){ "efibootmgr", NULL }, &fd); char *line = NULL; size_t len = 0; + int error = 0; if (!pid) { grub_util_warn (_("Unable to open stream from %s: %s"), "efibootmgr", strerror (errno)); - return; + return errno; } FILE *fp = fdopen (fd, "r"); @@ -98,7 +99,7 @@ grub_install_remove_efi_entries_by_distributor (const char *efi_distributor) { grub_util_warn (_("Unable to open stream from %s: %s"), "efibootmgr", strerror (errno)); - return; + return errno; } line = xmalloc (80); @@ -119,23 +120,25 @@ grub_install_remove_efi_entries_by_distributor (const char *efi_distributor) bootnum = line + sizeof ("Boot") - 1; bootnum[4] = '\0'; if (!verbosity) - grub_util_exec ((const char * []){ "efibootmgr", "-q", + error = grub_util_exec ((const char * []){ "efibootmgr", "-q", "-b", bootnum, "-B", NULL }); else - grub_util_exec ((const char * []){ "efibootmgr", + error = grub_util_exec ((const char * []){ "efibootmgr", "-b", bootnum, "-B", NULL }); } free (line); + return error; } -void +int grub_install_register_efi (grub_device_t efidir_grub_dev, const char *efifile_path, const char *efi_distributor) { const char * efidir_disk; int efidir_part; + int error = 0; efidir_disk = grub_util_biosdisk_get_osdev (efidir_grub_dev->disk); efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1; @@ -151,23 +154,26 @@ grub_install_register_efi (grub_device_t efidir_grub_dev, grub_util_exec ((const char * []){ "modprobe", "-q", "efivars", NULL }); #endif /* Delete old entries from the same distributor. */ - grub_install_remove_efi_entries_by_distributor (efi_distributor); + error = grub_install_remove_efi_entries_by_distributor (efi_distributor); + if (error) + return error; char *efidir_part_str = xasprintf ("%d", efidir_part); if (!verbosity) - grub_util_exec ((const char * []){ "efibootmgr", "-q", + error = grub_util_exec ((const char * []){ "efibootmgr", "-q", "-c", "-d", efidir_disk, "-p", efidir_part_str, "-w", "-L", efi_distributor, "-l", efifile_path, NULL }); else - grub_util_exec ((const char * []){ "efibootmgr", + error = grub_util_exec ((const char * []){ "efibootmgr", "-c", "-d", efidir_disk, "-p", efidir_part_str, "-w", "-L", efi_distributor, "-l", efifile_path, NULL }); free (efidir_part_str); + return error; } void diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 9f517a1bb..58648e209 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -209,7 +209,7 @@ grub_install_get_default_x86_platform (void); const char * grub_install_get_default_powerpc_machtype (void); -void +int grub_install_register_efi (grub_device_t efidir_grub_dev, const char *efifile_path, const char *efi_distributor); diff --git a/util/grub-install.c b/util/grub-install.c index 1fbb4c7da..04a520481 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -2002,9 +2002,13 @@ main (int argc, char *argv[]) if (!removable && update_nvram) { /* Try to make this image bootable using the EFI Boot Manager, if available. */ - grub_install_register_efi (efidir_grub_dev, + int error = 0; + error = grub_install_register_efi (efidir_grub_dev, "\\System\\Library\\CoreServices", efi_distributor); + if (error) + grub_util_error (_("efibootmgr failed to register the boot entry: %s"), + strerror (error)); } grub_device_close (ins_dev); @@ -2095,6 +2099,7 @@ main (int argc, char *argv[]) { char * efifile_path; char * part; + int error = 0; /* Try to make this image bootable using the EFI Boot Manager, if available. */ if (!efi_distributor || efi_distributor[0] == '\0') @@ -2111,8 +2116,11 @@ main (int argc, char *argv[]) efidir_grub_dev->disk->name, (part ? ",": ""), (part ? : "")); grub_free (part); - grub_install_register_efi (efidir_grub_dev, - efifile_path, efi_distributor); + error = grub_install_register_efi (efidir_grub_dev, + efifile_path, efi_distributor); + if (error) + grub_util_error (_("efibootmgr failed to register the boot entry: %s"), + strerror (error)); } break; From af7ca56feae2e2c7b804ce43ff75421ec0f8a551 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 29 Jun 2017 13:27:49 +0000 Subject: [PATCH 47/52] Allow GRUB to mount ext2/3/4 filesystems that have the encryption feature. On such a filesystem, inodes may have EXT4_ENCRYPT_FLAG set. For a regular file, this means its contents are encrypted; for a directory, this means the filenames in its directory entries are encrypted; and for a symlink, this means its target is encrypted. Since GRUB cannot decrypt encrypted contents or filenames, just issue an error if it would need to do so. This is sufficient to allow unencrypted boot files to co-exist with encrypted files elsewhere on the filesystem. (Note that encrypted regular files and symlinks will not normally be encountered outside an encrypted directory; however, it's possible via hard links, so they still need to be handled.) Tested by booting from an ext4 /boot partition on which I had run 'tune2fs -O encrypt'. I also verified that the expected error messages are printed when trying to access encrypted directories, files, and symlinks from the GRUB command line. Also ran 'sudo ./grub-fs-tester ext4_encrypt'; note that this requires e2fsprogs v1.43+ and Linux v4.1+. Signed-off-by: Eric Biggers Origin: upstream, https://git.savannah.gnu.org/cgit/grub.git/commit/?id=734668238fcc0ef691a080839e04f33854fa133a Bug-Debian: https://bugs.debian.org/840204 Last-Update: 2017-07-06 Patch-Name: ext4_feature_encrypt.patch --- grub-core/fs/ext2.c | 23 ++++++++++++++++++++++- tests/ext234_test.in | 1 + tests/util/grub-fs-tester.in | 10 ++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index cdce63bcc..b8ad75a0f 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -102,6 +102,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 /* The set of back-incompatible features this driver DOES support. Add (OR) * flags here as the related features are implemented into the driver. */ @@ -109,7 +110,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); | EXT4_FEATURE_INCOMPAT_EXTENTS \ | EXT4_FEATURE_INCOMPAT_FLEX_BG \ | EXT2_FEATURE_INCOMPAT_META_BG \ - | EXT4_FEATURE_INCOMPAT_64BIT) + | EXT4_FEATURE_INCOMPAT_64BIT \ + | EXT4_FEATURE_INCOMPAT_ENCRYPT) /* List of rationales for the ignored "incompatible" features: * needs_recovery: Not really back-incompatible - was added as such to forbid * ext2 drivers from mounting an ext3 volume with a dirty @@ -138,6 +140,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define EXT3_JOURNAL_FLAG_DELETED 4 #define EXT3_JOURNAL_FLAG_LAST_TAG 8 +#define EXT4_ENCRYPT_FLAG 0x800 #define EXT4_EXTENTS_FLAG 0x80000 /* The ext2 superblock. */ @@ -706,6 +709,12 @@ grub_ext2_read_symlink (grub_fshelp_node_t node) grub_ext2_read_inode (diro->data, diro->ino, &diro->inode); if (grub_errno) return 0; + + if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "symlink is encrypted"); + return 0; + } } symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1); @@ -749,6 +758,12 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir, return 0; } + if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "directory is encrypted"); + return 0; + } + /* Search the file. */ while (fpos < grub_le_to_cpu32 (diro->inode.size)) { @@ -859,6 +874,12 @@ grub_ext2_open (struct grub_file *file, const char *name) goto fail; } + if (fdiro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "file is encrypted"); + goto fail; + } + grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode)); grub_free (fdiro); diff --git a/tests/ext234_test.in b/tests/ext234_test.in index c986960a8..5f4553607 100644 --- a/tests/ext234_test.in +++ b/tests/ext234_test.in @@ -30,3 +30,4 @@ fi "@builddir@/grub-fs-tester" ext3 "@builddir@/grub-fs-tester" ext4 "@builddir@/grub-fs-tester" ext4_metabg +"@builddir@/grub-fs-tester" ext4_encrypt diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in index 2337771a1..5219aa8b4 100644 --- a/tests/util/grub-fs-tester.in +++ b/tests/util/grub-fs-tester.in @@ -135,6 +135,12 @@ for ((LOGSECSIZE=MINLOGSECSIZE;LOGSECSIZE<=MAXLOGSECSIZE;LOGSECSIZE=LOGSECSIZE + # Could go further but what's the point? MAXBLKSIZE=$((65536*1024)) ;; + xext4_encrypt) + # OS LIMITATION: Linux currently only allows the 'encrypt' feature + # in combination with block_size = PAGE_SIZE (4096 bytes on x86). + MINBLKSIZE=$(getconf PAGE_SIZE) + MAXBLKSIZE=$MINBLKSIZE + ;; xext*) MINBLKSIZE=1024 if [ $MINBLKSIZE -lt $SECSIZE ]; then @@ -766,6 +772,10 @@ for ((LOGSECSIZE=MINLOGSECSIZE;LOGSECSIZE<=MAXLOGSECSIZE;LOGSECSIZE=LOGSECSIZE + MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext4" -O meta_bg,^resize_inode -b $BLKSIZE -L "$FSLABEL" -q "${LODEVICES[0]}" MOUNTFS=ext4 ;; + xext4_encrypt) + MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.ext4" -O encrypt -b $BLKSIZE -L "$FSLABEL" -q "${MOUNTDEVICE}" + MOUNTFS=ext4 + ;; xext*) MKE2FS_DEVICE_SECTSIZE=$SECSIZE "mkfs.$fs" -b $BLKSIZE -L "$FSLABEL" -q "${LODEVICES[0]}" ;; xxfs) From 8b643f33f749ba56ba06a745a571bb6b76dbf3dd Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 15 Sep 2017 15:37:05 -0700 Subject: [PATCH 48/52] tsc: Change default tsc calibration method to pmtimer on EFI systems On efi systems, make pmtimer based tsc calibration the default over the pit. This prevents Grub from hanging on Intel SoC systems that power gate the pit. Signed-off-by: David E. Box Reviewed-by: Daniel Kiper Origin: upstream, https://git.savannah.gnu.org/cgit/grub.git/commit/?id=446794de8da4329ea532cbee4ca877bcafd0e534 Bug-Debian: https://bugs.debian.org/883193 Last-Update: 2017-12-01 Patch-Name: tsc_efi_default_to_pmtimer.patch --- grub-core/kern/i386/tsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c index 2e85289d8..f266eb131 100644 --- a/grub-core/kern/i386/tsc.c +++ b/grub-core/kern/i386/tsc.c @@ -68,7 +68,7 @@ grub_tsc_init (void) #ifdef GRUB_MACHINE_XEN (void) (grub_tsc_calibrate_from_xen () || calibrate_tsc_hardcode()); #elif defined (GRUB_MACHINE_EFI) - (void) (grub_tsc_calibrate_from_pit () || grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_efi() || calibrate_tsc_hardcode()); + (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || grub_tsc_calibrate_from_efi() || calibrate_tsc_hardcode()); #elif defined (GRUB_MACHINE_COREBOOT) (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || calibrate_tsc_hardcode()); #else From ba84c8d1b4830e9fcb14d9f0e4a36e03ac40a09d Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 30 Jan 2018 14:08:26 +0000 Subject: [PATCH 49/52] build: Capitalise *freetype_* variables Using FREETYPE_CFLAGS and FREETYPE_LIBS is more in line with the naming scheme used by pkg-config macros. Bug-Debian: https://bugs.debian.org/887721 Last-Update: 2018-02-11 Patch-Name: freetype-capitalise-variables.patch --- Makefile.am | 6 +++--- Makefile.util.def | 4 ++-- configure.ac | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Makefile.am b/Makefile.am index f0ab1adc3..b47b4b1ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,7 +71,7 @@ endif starfield_theme_files = $(srcdir)/themes/starfield/blob_w.png $(srcdir)/themes/starfield/boot_menu_c.png $(srcdir)/themes/starfield/boot_menu_e.png $(srcdir)/themes/starfield/boot_menu_ne.png $(srcdir)/themes/starfield/boot_menu_n.png $(srcdir)/themes/starfield/boot_menu_nw.png $(srcdir)/themes/starfield/boot_menu_se.png $(srcdir)/themes/starfield/boot_menu_s.png $(srcdir)/themes/starfield/boot_menu_sw.png $(srcdir)/themes/starfield/boot_menu_w.png $(srcdir)/themes/starfield/slider_c.png $(srcdir)/themes/starfield/slider_n.png $(srcdir)/themes/starfield/slider_s.png $(srcdir)/themes/starfield/starfield.png $(srcdir)/themes/starfield/terminal_box_c.png $(srcdir)/themes/starfield/terminal_box_e.png $(srcdir)/themes/starfield/terminal_box_ne.png $(srcdir)/themes/starfield/terminal_box_n.png $(srcdir)/themes/starfield/terminal_box_nw.png $(srcdir)/themes/starfield/terminal_box_se.png $(srcdir)/themes/starfield/terminal_box_s.png $(srcdir)/themes/starfield/terminal_box_sw.png $(srcdir)/themes/starfield/terminal_box_w.png $(srcdir)/themes/starfield/theme.txt $(srcdir)/themes/starfield/README $(srcdir)/themes/starfield/COPYING.CC-BY-SA-3.0 build-grub-mkfont$(BUILD_EXEEXT): util/grub-mkfont.c grub-core/unidata.c grub-core/kern/emu/misc.c util/misc.c - $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-mkfont\" $^ $(build_freetype_cflags) $(build_freetype_libs) + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-mkfont\" $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) CLEANFILES += build-grub-mkfont$(BUILD_EXEEXT) garbage-gen$(BUILD_EXEEXT): util/garbage-gen.c @@ -80,11 +80,11 @@ CLEANFILES += garbage-gen$(BUILD_EXEEXT) EXTRA_DIST += util/garbage-gen.c build-grub-gen-asciih$(BUILD_EXEEXT): util/grub-gen-asciih.c - $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(build_freetype_cflags) $(build_freetype_libs) -Wall -Werror + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) -Wall -Werror CLEANFILES += build-grub-gen-asciih$(BUILD_EXEEXT) build-grub-gen-widthspec$(BUILD_EXEEXT): util/grub-gen-widthspec.c - $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(build_freetype_cflags) $(build_freetype_libs) -Wall -Werror + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) -Wall -Werror CLEANFILES += build-grub-gen-widthspec$(BUILD_EXEEXT) if COND_STARFIELD diff --git a/Makefile.util.def b/Makefile.util.def index 168acbe59..fa39d8bd1 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -302,14 +302,14 @@ program = { common = grub-core/kern/emu/argp_common.c; common = grub-core/osdep/init.c; - cflags = '$(freetype_cflags)'; + cflags = '$(FREETYPE_CFLAGS)'; cppflags = '-DGRUB_MKFONT=1'; ldadd = libgrubmods.a; ldadd = libgrubgcry.a; ldadd = libgrubkern.a; ldadd = grub-core/gnulib/libgnu.a; - ldadd = '$(freetype_libs)'; + ldadd = '$(FREETYPE_LIBS)'; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; condition = COND_GRUB_MKFONT; }; diff --git a/configure.ac b/configure.ac index cd1f49837..85c23bd62 100644 --- a/configure.ac +++ b/configure.ac @@ -1505,12 +1505,12 @@ unset ac_cv_header_ft2build_h if test x"$grub_mkfont_excuse" = x ; then # Check for freetype libraries. - freetype_cflags=`$FREETYPE --cflags` - freetype_libs=`$FREETYPE --libs` + FREETYPE_CFLAGS=`$FREETYPE --cflags` + FREETYPE_LIBS=`$FREETYPE --libs` SAVED_CPPFLAGS="$CPPFLAGS" SAVED_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $freetype_cflags" - LIBS="$LIBS $freetype_libs" + CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" + LIBS="$LIBS $FREETYPE_LIBS" AC_CHECK_HEADERS([ft2build.h], [], [grub_mkfont_excuse=["need freetype2 headers"]]) AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], [grub_mkfont_excuse=["freetype2 library unusable"]]) @@ -1527,8 +1527,8 @@ else enable_grub_mkfont=no fi AC_SUBST([enable_grub_mkfont]) -AC_SUBST([freetype_cflags]) -AC_SUBST([freetype_libs]) +AC_SUBST([FREETYPE_CFLAGS]) +AC_SUBST([FREETYPE_LIBS]) SAVED_CC="$CC" SAVED_CPP="$CPP" @@ -1566,12 +1566,12 @@ fi if test x"$grub_build_mkfont_excuse" = x ; then # Check for freetype libraries. - build_freetype_cflags=`$BUILD_FREETYPE --cflags` - build_freetype_libs=`$BUILD_FREETYPE --libs` + BUILD_FREETYPE_CFLAGS=`$BUILD_FREETYPE --cflags` + BUILD_FREETYPE_LIBS=`$BUILD_FREETYPE --libs` SAVED_CPPFLAGS_2="$CPPFLAGS" SAVED_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $build_freetype_cflags" - LIBS="$LIBS $build_freetype_libs" + CPPFLAGS="$CPPFLAGS $BUILD_FREETYPE_CFLAGS" + LIBS="$LIBS $BUILD_FREETYPE_LIBS" AC_CHECK_HEADERS([ft2build.h], [], [grub_build_mkfont_excuse=["need freetype2 headers"]]) AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], [grub_build_mkfont_excuse=["freetype2 library unusable"]]) @@ -1595,8 +1595,8 @@ if test x"$enable_build_grub_mkfont" = xno && ( test "x$platform" = xqemu || tes fi fi -AC_SUBST([build_freetype_cflags]) -AC_SUBST([build_freetype_libs]) +AC_SUBST([BUILD_FREETYPE_CFLAGS]) +AC_SUBST([BUILD_FREETYPE_LIBS]) CC="$SAVED_CC" CPP="$SAVED_CPP" From 3eec911197081a63d9dae28f1784ad01a06fb60a Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 30 Jan 2018 21:54:17 +0000 Subject: [PATCH 50/52] build: Use pkg-config to find FreeType pkg-config is apparently preferred over freetype-config these days (see the BUGS section of freetype-config(1)). pkg-config support was added to FreeType in version 2.1.5, which was released in 2003, so it should comfortably be available everywhere by now. We no longer need to explicitly substitute FREETYPE_CFLAGS and FREETYPE_LIBS, since PKG_CHECK_MODULES does that automatically. Fixes Debian bug #887721. Reported-by: Hugh McMaster Signed-off-by: Colin Watson Bug-Debian: https://bugs.debian.org/887721 Last-Update: 2018-02-11 Patch-Name: freetype-pkg-config.patch --- INSTALL | 11 ++++---- configure.ac | 74 ++++++++++++++++++++++------------------------------ 2 files changed, 37 insertions(+), 48 deletions(-) diff --git a/INSTALL b/INSTALL index f3c20edc8..b370d7753 100644 --- a/INSTALL +++ b/INSTALL @@ -37,6 +37,7 @@ configuring the GRUB. * GNU gettext 0.17 or later * GNU binutils 2.9.1.0.23 or later * Flex 2.5.35 or later +* pkg-config * Other standard GNU/Unix tools * a libc with large file support (e.g. glibc 2.1 or later) @@ -52,7 +53,7 @@ For optional grub-emu features, you need: To build GRUB's graphical terminal (gfxterm), you need: -* FreeType 2 or later +* FreeType 2.1.5 or later * GNU Unifont If you use a development snapshot or want to hack on GRUB you may @@ -158,8 +159,8 @@ For this example the configure line might look like (more details below) (some options are optional and included here for completeness but some rarely used options are omitted): -./configure BUILD_CC=gcc BUILD_FREETYPE=freetype-config --host=amd64-linux-gnu -CC=amd64-linux-gnu-gcc CFLAGS="-g -O2" FREETYPE=amd64-linux-gnu-freetype-config +./configure BUILD_CC=gcc BUILD_PKG_CONFIG=pkg-config --host=amd64-linux-gnu +CC=amd64-linux-gnu-gcc CFLAGS="-g -O2" PKG_CONFIG=amd64-linux-gnu-pkg-config --target=arm --with-platform=uboot TARGET_CC=arm-elf-gcc TARGET_CFLAGS="-Os -march=armv6" TARGET_CCASFLAGS="-march=armv6" TARGET_OBJCOPY="arm-elf-objcopy" TARGET_STRIP="arm-elf-strip" @@ -176,7 +177,7 @@ corresponding platform are not needed for the platform in question. 2. BUILD_CFLAGS= for C options for build. 3. BUILD_CPPFLAGS= for C preprocessor options for build. 4. BUILD_LDFLAGS= for linker options for build. - 5. BUILD_FREETYPE= for freetype-config for build (optional). + 5. BUILD_PKG_CONFIG= for pkg-config for build (optional). - For host 1. --host= to autoconf name of host. @@ -184,7 +185,7 @@ corresponding platform are not needed for the platform in question. 3. HOST_CFLAGS= for C options for host. 4. HOST_CPPFLAGS= for C preprocessor options for host. 5. HOST_LDFLAGS= for linker options for host. - 6. FREETYPE= for freetype-config for host (optional). + 6. PKG_CONFIG= for pkg-config for host (optional). 7. Libdevmapper if any must be in standard linker folders (-ldevmapper) (optional). 8. Libfuse if any must be in standard linker folders (-lfuse) (optional). 9. Libzfs if any must be in standard linker folders (-lzfs) (optional). diff --git a/configure.ac b/configure.ac index 85c23bd62..f102b7024 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,10 @@ AC_PREREQ(2.60) AC_CONFIG_SRCDIR([include/grub/dl.h]) AC_CONFIG_HEADER([config-util.h]) +# Explicitly check for pkg-config early on, since otherwise conditional +# calls are problematic. +PKG_PROG_PKG_CONFIG + # Program name transformations AC_ARG_PROGRAM grub_TRANSFORM([grub-bios-setup]) @@ -1493,29 +1497,22 @@ if test x"$enable_grub_mkfont" = xno ; then grub_mkfont_excuse="explicitly disabled" fi -if test x"$grub_mkfont_excuse" = x ; then - # Check for freetype libraries. - AC_CHECK_TOOLS([FREETYPE], [freetype-config]) - if test "x$FREETYPE" = x ; then - grub_mkfont_excuse=["need freetype2 library"] - fi -fi - unset ac_cv_header_ft2build_h if test x"$grub_mkfont_excuse" = x ; then # Check for freetype libraries. - FREETYPE_CFLAGS=`$FREETYPE --cflags` - FREETYPE_LIBS=`$FREETYPE --libs` - SAVED_CPPFLAGS="$CPPFLAGS" - SAVED_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" - LIBS="$LIBS $FREETYPE_LIBS" - AC_CHECK_HEADERS([ft2build.h], [], - [grub_mkfont_excuse=["need freetype2 headers"]]) - AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], [grub_mkfont_excuse=["freetype2 library unusable"]]) - CPPFLAGS="$SAVED_CPPFLAGS" - LIBS="$SAVED_LIBS" + PKG_CHECK_MODULES([FREETYPE], [freetype2], [ + SAVED_CPPFLAGS="$CPPFLAGS" + SAVED_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" + LIBS="$LIBS $FREETYPE_LIBS" + AC_CHECK_HEADERS([ft2build.h], [], + [grub_mkfont_excuse=["need freetype2 headers"]]) + AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], + [grub_mkfont_excuse=["freetype2 library unusable"]]) + CPPFLAGS="$SAVED_CPPFLAGS" + LIBS="$SAVED_LIBS" + ], [grub_mkfont_excuse=["need freetype2 library"]]) fi if test x"$enable_grub_mkfont" = xyes && test x"$grub_mkfont_excuse" != x ; then @@ -1527,8 +1524,6 @@ else enable_grub_mkfont=no fi AC_SUBST([enable_grub_mkfont]) -AC_SUBST([FREETYPE_CFLAGS]) -AC_SUBST([FREETYPE_LIBS]) SAVED_CC="$CC" SAVED_CPP="$CPP" @@ -1558,25 +1553,21 @@ AC_SUBST([BUILD_WORDS_BIGENDIAN]) if test x"$grub_build_mkfont_excuse" = x ; then # Check for freetype libraries. - AC_CHECK_PROGS([BUILD_FREETYPE], [freetype-config]) - if test "x$BUILD_FREETYPE" = x ; then - grub_build_mkfont_excuse=["need freetype2 library"] - fi -fi - -if test x"$grub_build_mkfont_excuse" = x ; then - # Check for freetype libraries. - BUILD_FREETYPE_CFLAGS=`$BUILD_FREETYPE --cflags` - BUILD_FREETYPE_LIBS=`$BUILD_FREETYPE --libs` - SAVED_CPPFLAGS_2="$CPPFLAGS" - SAVED_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $BUILD_FREETYPE_CFLAGS" - LIBS="$LIBS $BUILD_FREETYPE_LIBS" - AC_CHECK_HEADERS([ft2build.h], [], - [grub_build_mkfont_excuse=["need freetype2 headers"]]) - AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], [grub_build_mkfont_excuse=["freetype2 library unusable"]]) - LIBS="$SAVED_LIBS" - CPPFLAGS="$SAVED_CPPFLAGS_2" + SAVED_PKG_CONFIG="$PKG_CONFIG" + test -z "$BUILD_PKG_CONFIG" || PKG_CONFIG="$BUILD_PKG_CONFIG" + PKG_CHECK_MODULES([BUILD_FREETYPE], [freetype2], [ + SAVED_CPPFLAGS_2="$CPPFLAGS" + SAVED_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $BUILD_FREETYPE_CFLAGS" + LIBS="$LIBS $BUILD_FREETYPE_LIBS" + AC_CHECK_HEADERS([ft2build.h], [], + [grub_build_mkfont_excuse=["need freetype2 headers"]]) + AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], + [grub_build_mkfont_excuse=["freetype2 library unusable"]]) + LIBS="$SAVED_LIBS" + CPPFLAGS="$SAVED_CPPFLAGS_2" + ], [grub_build_mkfont_excuse=["need freetype2 library"]]) + PKG_CONFIG="$SAVED_PKG_CONFIG" fi if test x"$enable_build_grub_mkfont" = xyes && test x"$grub_build_mkfont_excuse" != x ; then @@ -1595,9 +1586,6 @@ if test x"$enable_build_grub_mkfont" = xno && ( test "x$platform" = xqemu || tes fi fi -AC_SUBST([BUILD_FREETYPE_CFLAGS]) -AC_SUBST([BUILD_FREETYPE_LIBS]) - CC="$SAVED_CC" CPP="$SAVED_CPP" CFLAGS="$SAVED_CFLAGS" From 09953790985d9b8b76db7645c3f4f131a66abaff Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 14 Aug 2017 14:11:43 +0200 Subject: [PATCH 51/52] yylex: Explicilty cast fprintf to void. It's needed to avoid warning on recent GCC. Bug-Debian: https://bugs.debian.org/890431 Last-Update: 2018-02-16 Patch-Name: fix-yylex-build.patch --- grub-core/script/yylex.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grub-core/script/yylex.l b/grub-core/script/yylex.l index 95b219170..7b44c37b7 100644 --- a/grub-core/script/yylex.l +++ b/grub-core/script/yylex.l @@ -91,7 +91,7 @@ typedef size_t yy_size_t; #define stdin 0 #define stdout 0 -#define fprintf(...) 0 +#define fprintf(...) (void)0 #define exit(...) grub_fatal("fatal error in lexer") #endif From 8bb113820827608a4d12fbafe12bfb3adcf3babf Mon Sep 17 00:00:00 2001 From: Eric Snowberg Date: Thu, 22 Feb 2018 10:03:46 +0000 Subject: [PATCH 52/52] Add support for modern sparc64 hardware Origin: other, https://github.com/esnowberg/grub2-sparc/tree/sparc-next-v4 Bug-Debian: https://bugs.debian.org/854568 Last-Update: 2018-03-02 Patch-Name: sparc64-support.patch --- grub-core/Makefile.core.def | 1 + grub-core/boot/sparc64/ieee1275/boot.S | 10 + grub-core/commands/ls.c | 2 + grub-core/commands/nativedisk.c | 1 + grub-core/disk/ieee1275/obdisk.c | 1079 ++++++++++++++++++++ grub-core/disk/ieee1275/ofdisk.c | 30 +- grub-core/kern/ieee1275/cmain.c | 3 + grub-core/kern/ieee1275/ieee1275.c | 199 ++++ grub-core/kern/ieee1275/init.c | 36 +- grub-core/kern/ieee1275/openfw.c | 27 + grub-core/kern/parser.c | 1 - grub-core/kern/sparc64/ieee1275/ieee1275.c | 53 + grub-core/osdep/linux/blocklist.c | 5 + grub-core/osdep/linux/ofpath.c | 204 +++- include/grub/disk.h | 1 + include/grub/ieee1275/ieee1275.h | 27 + include/grub/ieee1275/obdisk.h | 25 + include/grub/sparc64/ieee1275/ieee1275.h | 2 + util/grub-install.c | 1 + util/ieee1275/grub-ofpathname.c | 4 +- util/setup.c | 87 +- 21 files changed, 1743 insertions(+), 55 deletions(-) create mode 100644 grub-core/disk/ieee1275/obdisk.c create mode 100644 include/grub/ieee1275/obdisk.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 3b389277c..e751e334c 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -270,6 +270,7 @@ kernel = { sparc64_ieee1275 = kern/sparc64/cache.S; sparc64_ieee1275 = kern/sparc64/dl.c; sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c; + sparc64_ieee1275 = disk/ieee1275/obdisk.c; arm = kern/arm/dl.c; arm = kern/arm/dl_helper.c; diff --git a/grub-core/boot/sparc64/ieee1275/boot.S b/grub-core/boot/sparc64/ieee1275/boot.S index 586efb401..9ea9b4e06 100644 --- a/grub-core/boot/sparc64/ieee1275/boot.S +++ b/grub-core/boot/sparc64/ieee1275/boot.S @@ -69,6 +69,10 @@ prom_seek_name: .asciz "seek" prom_read_name: .asciz "read" prom_exit_name: .asciz "exit" grub_name: .asciz "GRUB " +#ifdef CDBOOT +prom_close_name: .asciz "close" +#endif + #define GRUB_NAME_LEN 5 .align 4 @@ -213,6 +217,12 @@ bootpath_known: call prom_call_3_1_o1 #ifdef CDBOOT LDUW_ABS(kernel_size, 0x00, %o3) + + GET_ABS(prom_close_name, %o0) + mov 1, %g1 + mov 0, %o5 + call prom_call + mov BOOTDEV_REG, %o1 #else mov 512, %o3 #endif diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 0eaf83652..a7318ab84 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -201,6 +201,8 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) if (grub_errno == GRUB_ERR_UNKNOWN_FS) grub_errno = GRUB_ERR_NONE; + grub_device_close (dev); + dev = NULL; grub_normal_print_device_info (device_name); } else if (fs) diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c index 2f56a870e..2f2315d77 100644 --- a/grub-core/commands/nativedisk.c +++ b/grub-core/commands/nativedisk.c @@ -66,6 +66,7 @@ get_uuid (const char *name, char **uuid, int getnative) /* Firmware disks. */ case GRUB_DISK_DEVICE_BIOSDISK_ID: case GRUB_DISK_DEVICE_OFDISK_ID: + case GRUB_DISK_DEVICE_OBDISK_ID: case GRUB_DISK_DEVICE_EFIDISK_ID: case GRUB_DISK_DEVICE_NAND_ID: case GRUB_DISK_DEVICE_ARCDISK_ID: diff --git a/grub-core/disk/ieee1275/obdisk.c b/grub-core/disk/ieee1275/obdisk.c new file mode 100644 index 000000000..006bb6f91 --- /dev/null +++ b/grub-core/disk/ieee1275/obdisk.c @@ -0,0 +1,1079 @@ +/* obdisk.c - Open Boot disk access. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct disk_dev +{ + struct disk_dev *next; + struct disk_dev **prev; + /* open boot canonical name */ + char *name; + /* open boot raw disk name to access entire disk */ + char *raw_name; + /* grub encoded device name */ + char *grub_devpath; + /* grub encoded alias name */ + char *grub_alias_devpath; + /* grub unescaped name */ + char *grub_decoded_devpath; + grub_ieee1275_ihandle_t ihandle; + grub_uint32_t block_size; + grub_uint64_t num_blocks; + unsigned int log_sector_size; + grub_uint32_t opened; + grub_uint32_t valid; + grub_uint32_t boot_dev; +}; + +struct parent_dev +{ + struct parent_dev *next; + struct parent_dev **prev; + /* canonical parent name */ + char *name; + char *type; + grub_ieee1275_ihandle_t ihandle; + grub_uint32_t address_cells; +}; + +static struct grub_scsi_test_unit_ready tur = +{ + .opcode = grub_scsi_cmd_test_unit_ready, + .lun = 0, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + .control = 0, +}; + +static int disks_enumerated = 0; +static struct disk_dev *disk_devs = NULL; +static struct parent_dev *parent_devs = NULL; + +static const char *block_blacklist[] = { + /* Requires addition work in grub before being able to be used */ + "/iscsi-hba", + /* This block device should never be used by grub */ + "/reboot-memory@0", + 0 +}; + +#define STRCMP(a, b) ((a) && (b) && (grub_strcmp (a, b) == 0)) + +static char * +strip_ob_partition (char *path) +{ + char *sptr; + + sptr = grub_strstr (path, ":"); + + if (sptr) + *sptr = '\0'; + + return path; +} + +static char * +remove_escaped_commas (char *src) +{ + char *iptr; + + for (iptr = src; *iptr; ) + { + if ((*iptr == '\\') && (*(iptr + 1) == ',')) + { + *iptr++ = '_'; + *iptr++ = '_'; + } + iptr++; + } + + return src; +} + +static int +count_commas (const char *src) +{ + int count = 0; + + for ( ; *src; src++) + if (*src == ',') + count++; + + return count; +} + +static void +escape_commas (const char *src, char *dest) +{ + const char *iptr; + + for (iptr = src; *iptr; ) + { + if (*iptr == ',') + *dest++ ='\\'; + + *dest++ = *iptr++; + } + + *dest = '\0'; +} + +static char * +decode_grub_devname (const char *name) +{ + char *devpath = grub_malloc (grub_strlen (name) + 1); + char *p, c; + + if (!devpath) + return NULL; + + /* Un-escape commas. */ + p = devpath; + while ((c = *name++) != '\0') + { + if (c == '\\' && *name == ',') + { + *p++ = ','; + name++; + } + else + *p++ = c; + } + + *p++ = '\0'; + + return devpath; +} + +static char * +encode_grub_devname (const char *path) +{ + char *encoding, *optr; + + if (path == NULL) + return NULL; + + encoding = grub_malloc (sizeof ("ieee1275/") + count_commas (path) + + grub_strlen (path) + 1); + + if (encoding == NULL) + { + grub_print_error (); + return NULL; + } + + optr = grub_stpcpy (encoding, "ieee1275/"); + escape_commas (path, optr); + return encoding; +} + +static char * +get_parent_devname (const char *devname) +{ + char *parent, *pptr; + + parent = grub_strdup (devname); + + if (parent == NULL) + { + grub_print_error (); + return NULL; + } + + pptr = grub_strstr (parent, "/disk@"); + + if (pptr) + *pptr = '\0'; + + return parent; +} + +static void +free_parent_dev (struct parent_dev *parent) +{ + if (parent) + { + grub_free (parent->name); + grub_free (parent->type); + grub_free (parent); + } +} + +static struct parent_dev * +init_parent (const char *parent) +{ + struct parent_dev *op; + + op = grub_zalloc (sizeof (struct parent_dev)); + + if (op == NULL) + { + grub_print_error (); + return NULL; + } + + op->name = grub_strdup (parent); + op->type = grub_malloc (IEEE1275_MAX_PROP_LEN); + + if ((op->name == NULL) || (op->type == NULL)) + { + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + return op; +} + +static struct parent_dev * +open_new_parent (const char *parent) +{ + struct parent_dev *op = init_parent(parent); + grub_ieee1275_ihandle_t ihandle; + grub_ieee1275_phandle_t phandle; + grub_uint32_t address_cells = 2; + grub_ssize_t actual = 0; + + if (op == NULL) + return NULL; + + grub_ieee1275_open (parent, &ihandle); + + if (ihandle == 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to open %s", parent); + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + if (grub_ieee1275_instance_to_package (ihandle, &phandle)) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to get parent %s", parent); + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + /* IEEE Std 1275-1994 page 110: A missing “#address-cells” property + signifies that the number of address cells is two. So ignore on error. */ + grub_ieee1275_get_integer_property (phandle, "#address-cells", &address_cells, + sizeof (address_cells), 0); + + grub_ieee1275_get_property (phandle, "device_type", op->type, + IEEE1275_MAX_PROP_LEN, &actual); + op->ihandle = ihandle; + op->address_cells = address_cells; + return op; +} + +static struct parent_dev * +open_parent (const char *parent) +{ + struct parent_dev *op; + + if ((op = + grub_named_list_find (GRUB_AS_NAMED_LIST (parent_devs), parent)) == NULL) + { + op = open_new_parent (parent); + + if (op) + grub_list_push (GRUB_AS_LIST_P (&parent_devs), GRUB_AS_LIST (op)); + } + + return op; +} + +static void +display_parents (void) +{ + struct parent_dev *parent; + + grub_printf ("-------------------- PARENTS --------------------\n"); + + FOR_LIST_ELEMENTS (parent, parent_devs) + { + grub_printf ("name: %s\n", parent->name); + grub_printf ("type: %s\n", parent->type); + grub_printf ("address_cells %x\n", parent->address_cells); + } + + grub_printf ("-------------------------------------------------\n"); +} + +static char * +canonicalise_4cell_ua (grub_ieee1275_ihandle_t ihandle, char *unit_address) +{ + grub_uint32_t phy_lo, phy_hi, lun_lo, lun_hi; + int valid_phy = 0; + grub_size_t size; + char *canon = NULL; + + valid_phy = grub_ieee1275_decode_unit4 (ihandle, unit_address, + grub_strlen (unit_address), &phy_lo, + &phy_hi, &lun_lo, &lun_hi); + + if ((!valid_phy) && (phy_hi != 0xffffffff)) + canon = grub_ieee1275_encode_uint4 (ihandle, phy_lo, phy_hi, + lun_lo, lun_hi, &size); + + return canon; +} + +static char * +canonicalise_disk (const char *devname) +{ + char *canon, *parent; + struct parent_dev *op; + + canon = grub_ieee1275_canonicalise_devname (devname); + + if (canon == NULL) + { + /* This should not happen */ + grub_error (GRUB_ERR_BAD_DEVICE, "canonicalise devname failed"); + grub_print_error (); + return NULL; + } + + /* Don't try to open the parent of a virtual device */ + if (grub_strstr (canon, "virtual-devices")) + return canon; + + parent = get_parent_devname (canon); + + if (parent == NULL) + return NULL; + + op = open_parent (parent); + + /* Devices with 4 address cells can have many different types of addressing + (phy, wwn, and target lun). Use the parents encode-unit / decode-unit + to find the true canonical name. */ + if ((op) && (op->address_cells == 4)) + { + char *unit_address, *real_unit_address, *real_canon; + + unit_address = grub_strstr (canon, "/disk@"); + unit_address += grub_strlen ("/disk@"); + + if (unit_address == NULL) + { + /* This should not be possible, but return the canonical name for + the non-disk block device */ + grub_free (parent); + return (canon); + } + + real_unit_address = canonicalise_4cell_ua (op->ihandle, unit_address); + + if (real_unit_address == NULL) + { + /* This is not an error, since this function could be called with a devalias + containing a drive that isn't installed in the system. */ + grub_free (parent); + return NULL; + } + + real_canon = grub_malloc (grub_strlen (op->name) + sizeof ("/disk@") + + grub_strlen (real_unit_address)); + + grub_snprintf (real_canon, grub_strlen (op->name) + sizeof ("/disk@") + + grub_strlen (real_unit_address), "%s/disk@%s", + op->name, real_unit_address); + + grub_free (canon); + canon = real_canon; + } + + grub_free (parent); + return (canon); +} + +static struct disk_dev * +add_canon_disk (const char *cname) +{ + struct disk_dev *dev; + + dev = grub_zalloc (sizeof (struct disk_dev)); + + if (!dev) + goto failed; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_RAW_DEVNAMES)) + { + /* Append :nolabel to the end of all SPARC disks. + + nolabel is mutually exclusive with all other + arguments and allows a client program to open + the entire (raw) disk. Any disk label is ignored. */ + dev->raw_name = grub_malloc (grub_strlen (cname) + sizeof (":nolabel")); + + if (dev->raw_name == NULL) + goto failed; + + grub_snprintf (dev->raw_name, grub_strlen (cname) + sizeof (":nolabel"), + "%s:nolabel", cname); + } + + /* Don't use grub_ieee1275_encode_devname here, the devpath in grub.cfg doesn't + understand device aliases, which the layer above sometimes sends us. */ + dev->grub_devpath = encode_grub_devname(cname); + + if (dev->grub_devpath == NULL) + goto failed; + + dev->name = grub_strdup (cname); + + if (dev->name == NULL) + goto failed; + + dev->valid = 1; + grub_list_push (GRUB_AS_LIST_P (&disk_devs), GRUB_AS_LIST (dev)); + return dev; + +failed: + grub_print_error (); + + if (dev) + { + grub_free (dev->name); + grub_free (dev->grub_devpath); + grub_free (dev->raw_name); + } + + grub_free (dev); + return NULL; +} + +static grub_err_t +add_disk (const char *path) +{ + grub_err_t rval = GRUB_ERR_NONE; + struct disk_dev *dev; + char *canon; + + canon = canonicalise_disk (path); + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + if ((canon != NULL) && (dev == NULL)) + { + struct disk_dev *ob_device; + + ob_device = add_canon_disk (canon); + + if (ob_device == NULL) + rval = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure to add disk"); + } + else if (dev) + dev->valid = 1; + + grub_free (canon); + return (rval); +} + +static grub_err_t +grub_obdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *dest) +{ + grub_err_t rval = GRUB_ERR_NONE; + struct disk_dev *dev; + unsigned long long pos; + grub_ssize_t result = 0; + + if (disk->data == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, "invalid disk data"); + + dev = (struct disk_dev *)disk->data; + pos = sector << disk->log_sector_size; + grub_ieee1275_seek (dev->ihandle, pos, &result); + + if (result < 0) + { + dev->opened = 0; + return grub_error (GRUB_ERR_READ_ERROR, "seek error, can't seek block %llu", + (long long) sector); + } + + grub_ieee1275_read (dev->ihandle, dest, size << disk->log_sector_size, + &result); + + if (result != (grub_ssize_t) (size << disk->log_sector_size)) + { + dev->opened = 0; + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + } + return rval; +} + +static void +grub_obdisk_close (grub_disk_t disk) +{ + disk->data = NULL; + disk->id = 0; + disk->total_sectors = 0; + disk->log_sector_size = 0; +} + +static void +scan_usb_disk (const char *parent) +{ + struct parent_dev *op; + grub_ssize_t result; + + op = open_parent (parent); + + if (op == NULL) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to open %s", parent); + grub_print_error (); + return; + } + + if ((grub_ieee1275_set_address (op->ihandle, 0, 0) == 0) && + (grub_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && + (result == 0)) + { + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@0", parent); + add_disk (buf); + grub_free (buf); + } +} + +static void +scan_nvme_disk (const char *path) +{ + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@1", path); + add_disk (buf); + grub_free (buf); +} + +static void +scan_sparc_sas_2cell (struct parent_dev *op) +{ + grub_ssize_t result; + grub_uint8_t tgt; + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + for (tgt = 0; tgt < 0xf; tgt++) + { + + if ((grub_ieee1275_set_address(op->ihandle, tgt, 0) == 0) && + (grub_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && + (result == 0)) + { + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%" + PRIxGRUB_UINT32_T, op->name, tgt); + + add_disk (buf); + } + } +} + +static void +scan_sparc_sas_4cell (struct parent_dev *op) +{ + grub_uint16_t exp; + grub_uint8_t phy; + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + for (exp = 0; exp <= 0x100; exp+=0x100) + + for (phy = 0; phy < 0x20; phy++) + { + char *canon = NULL; + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "p%" PRIxGRUB_UINT32_T ",0", + exp | phy); + + canon = canonicalise_4cell_ua (op->ihandle, buf); + + if (canon) + { + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%s", + op->name, canon); + + add_disk (buf); + grub_free (canon); + } + } + + grub_free (buf); +} + +static void +scan_sparc_sas_disk (const char *parent) +{ + struct parent_dev *op; + + op = open_parent (parent); + + if ((op) && (op->address_cells == 4)) + scan_sparc_sas_4cell (op); + else if ((op) && (op->address_cells == 2)) + scan_sparc_sas_2cell (op); +} + +static void +iterate_devtree (const struct grub_ieee1275_devalias *alias) +{ + struct grub_ieee1275_devalias child; + + if ((grub_strcmp (alias->type, "scsi-2") == 0) || + (grub_strcmp (alias->type, "scsi-sas") == 0)) + return scan_sparc_sas_disk (alias->path); + + else if (grub_strcmp (alias->type, "nvme") == 0) + return scan_nvme_disk (alias->path); + + else if (grub_strcmp (alias->type, "scsi-usb") == 0) + return scan_usb_disk (alias->path); + + else if (grub_strcmp (alias->type, "block") == 0) + { + const char **bl = block_blacklist; + + while (*bl != NULL) + { + if (grub_strstr (alias->path, *bl)) + return; + bl++; + } + + add_disk (alias->path); + return; + } + + FOR_IEEE1275_DEVCHILDREN (alias->path, child) + iterate_devtree (&child); +} + +static void +unescape_devices (void) +{ + struct disk_dev *dev; + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + grub_free (dev->grub_decoded_devpath); + + if ((dev->grub_alias_devpath) && + (grub_strcmp (dev->grub_alias_devpath, dev->grub_devpath) != 0)) + dev->grub_decoded_devpath = + remove_escaped_commas (grub_strdup (dev->grub_alias_devpath)); + else + dev->grub_decoded_devpath = + remove_escaped_commas (grub_strdup (dev->grub_devpath)); + } +} + +static void +enumerate_disks (void) +{ + struct grub_ieee1275_devalias alias; + + FOR_IEEE1275_DEVCHILDREN("/", alias) + iterate_devtree (&alias); +} + +static grub_err_t +add_bootpath (void) +{ + struct disk_dev *ob_device; + grub_err_t rval = GRUB_ERR_NONE; + char *dev, *alias = NULL; + char *type; + + grub_ieee1275_get_boot_dev (&dev); + type = grub_ieee1275_get_device_type (dev); + + if (!(type && grub_strcmp (type, "network") == 0)) + { + dev = strip_ob_partition (dev); + ob_device = add_canon_disk (dev); + + if (ob_device == NULL) + rval = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure adding boot device"); + + ob_device->valid = 1; + + alias = grub_ieee1275_get_devname (dev); + + if (grub_strcmp (alias, dev) != 0) + ob_device->grub_alias_devpath = grub_ieee1275_encode_devname (dev); + + ob_device->boot_dev = 1; + } + + grub_free (type); + grub_free (dev); + grub_free (alias); + return rval; +} + +static void +enumerate_aliases (void) +{ + struct grub_ieee1275_devalias alias; + + /* Some block device aliases are not in canonical form + + For example: + + disk3 /pci@301/pci@1/scsi@0/disk@p3 + disk2 /pci@301/pci@1/scsi@0/disk@p2 + disk1 /pci@301/pci@1/scsi@0/disk@p1 + disk /pci@301/pci@1/scsi@0/disk@p0 + disk0 /pci@301/pci@1/scsi@0/disk@p0 + + None of these devices are in canonical form. + + Also, just because there is a devalias, doesn't mean there is a disk + at that location. And a valid boot block device doesn't have to have + a devalias at all. + + At this point, all valid disks have been found in the system + and devaliases that point to canonical names are stored in the + disk_devs list already. */ + FOR_IEEE1275_DEVALIASES (alias) + { + struct disk_dev *dev; + char *canon; + + if (grub_strcmp (alias.type, "block") != 0) + continue; + + canon = canonicalise_disk (alias.name); + + if (canon == NULL) + /* This is not an error, a devalias could point to a + nonexistent disk */ + continue; + + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + if (dev) + { + /* If more than one alias points to the same device, + remove the previous one unless it is the boot dev, + since the upper level will use the first one. The reason + all the others are redone is in the case of hot-plugging + a disk. If the boot disk gets hot-plugged, it will come + thru here with a different name without the boot_dev flag + set. */ + if ((dev->boot_dev) && (dev->grub_alias_devpath)) + continue; + + grub_free (dev->grub_alias_devpath); + dev->grub_alias_devpath = grub_ieee1275_encode_devname (alias.path); + } + grub_free (canon); + } +} + +static void +display_disks (void) +{ + struct disk_dev *dev; + + grub_printf ("--------------------- DISKS ---------------------\n"); + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + grub_printf ("name: %s\n", dev->name); + grub_printf ("grub_devpath: %s\n", dev->grub_devpath); + grub_printf ("grub_alias_devpath: %s\n", dev->grub_alias_devpath); + grub_printf ("grub_decoded_devpath: %s\n", dev->grub_decoded_devpath); + grub_printf ("valid: %s\n", (dev->valid) ? "yes" : "no"); + grub_printf ("boot_dev: %s\n", (dev->boot_dev) ? "yes" : "no"); + grub_printf ("opened: %s\n", (dev->ihandle) ? "yes" : "no"); + grub_printf ("block size: %" PRIuGRUB_UINT32_T "\n", dev->block_size); + grub_printf ("num blocks: %" PRIuGRUB_UINT64_T "\n", dev->num_blocks); + grub_printf ("log sector size: %" PRIuGRUB_UINT32_T "\n", + dev->log_sector_size); + grub_printf ("\n"); + } + + grub_printf ("-------------------------------------------------\n"); +} + +static void +display_stats (void) +{ + const char *debug = grub_env_get ("debug"); + + if (! debug) + return; + + if (grub_strword (debug, "all") || grub_strword (debug, "obdisk")) + { + display_parents (); + display_disks (); + } +} + +static void +invalidate_all_disks (void) +{ + struct disk_dev *dev = NULL; + + if (disks_enumerated) + FOR_LIST_ELEMENTS (dev, disk_devs) + dev->valid = 0; +} + +/* This is for backwards compatibility, since the path should be generated + correctly now. */ +static struct disk_dev * +find_legacy_grub_devpath (const char *name) +{ + struct disk_dev *dev = NULL; + char *canon, *devpath = NULL; + + devpath = decode_grub_devname (name + sizeof ("ieee1275")); + canon = canonicalise_disk (devpath); + + if (canon != NULL) + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + grub_free (devpath); + grub_free (canon); + return dev; +} + +static void +enumerate_devices (void) +{ + invalidate_all_disks (); + enumerate_disks (); + enumerate_aliases (); + unescape_devices (); + disks_enumerated = 1; + display_stats (); +} + +static struct disk_dev * +find_grub_devpath_real (const char *name) +{ + struct disk_dev *dev = NULL; + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if ((STRCMP (dev->grub_devpath, name)) + || (STRCMP (dev->grub_alias_devpath, name)) + || (STRCMP (dev->grub_decoded_devpath, name))) + break; + } + + return dev; +} + +static struct disk_dev * +find_grub_devpath (const char *name) +{ + struct disk_dev *dev = NULL; + int enumerated; + + do { + enumerated = disks_enumerated; + dev = find_grub_devpath_real (name); + + if (dev) + break; + + dev = find_legacy_grub_devpath (name); + + if (dev) + break; + + enumerate_devices (); + } while (enumerated == 0); + + return dev; +} + +static int +grub_obdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct disk_dev *dev; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + enumerate_devices (); + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if (dev->valid == 1) + if (hook (dev->grub_decoded_devpath, hook_data)) + return 1; + } + + return 0; +} + +static grub_err_t +grub_obdisk_open (const char *name, grub_disk_t disk) +{ + grub_ieee1275_ihandle_t ihandle = 0; + struct disk_dev *dev = NULL; + + if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not IEEE1275 device"); + + dev = find_grub_devpath (name); + + if (dev == NULL) + { + grub_printf ("UNKNOWN DEVICE: %s\n", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "%s", name); + } + + if (dev->opened == 0) + { + if (dev->raw_name) + grub_ieee1275_open (dev->raw_name, &ihandle); + else + grub_ieee1275_open (dev->name, &ihandle); + + if (ihandle == 0) + { + grub_printf ("Can't open device %s\n", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device %s", name); + } + + dev->block_size = grub_ieee1275_get_block_size (ihandle); + dev->num_blocks = grub_ieee1275_num_blocks (ihandle); + + if (dev->num_blocks == 0) + dev->num_blocks = grub_ieee1275_num_blocks64 (ihandle); + + if (dev->num_blocks == 0) + dev->num_blocks = GRUB_DISK_SIZE_UNKNOWN; + + if (dev->block_size != 0) + { + for (dev->log_sector_size = 0; + (1U << dev->log_sector_size) < dev->block_size; + dev->log_sector_size++); + } + else + dev->log_sector_size = 9; + + dev->ihandle = ihandle; + dev->opened = 1; + } + + disk->total_sectors = dev->num_blocks; + disk->id = dev->ihandle; + disk->data = dev; + disk->log_sector_size = dev->log_sector_size; + return GRUB_ERR_NONE; +} + + +static struct grub_disk_dev grub_obdisk_dev = + { + .name = "obdisk", + .id = GRUB_DISK_DEVICE_OBDISK_ID, + .iterate = grub_obdisk_iterate, + .open = grub_obdisk_open, + .close = grub_obdisk_close, + .read = grub_obdisk_read, + .next = 0 + }; + +void +grub_obdisk_init (void) +{ + grub_disk_firmware_fini = grub_obdisk_fini; + add_bootpath (); + grub_disk_dev_register (&grub_obdisk_dev); +} + +void +grub_obdisk_fini (void) +{ + struct disk_dev *dev; + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if (dev->opened) + grub_ieee1275_close (dev->ihandle); + } + + grub_disk_dev_unregister (&grub_obdisk_dev); +} diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index 235c0fe2c..18d2e9512 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -74,7 +74,7 @@ ofdisk_hash_find (const char *devpath) } static struct ofdisk_hash_ent * -ofdisk_hash_add_real (char *devpath) +ofdisk_hash_add_real (const char *devpath) { struct ofdisk_hash_ent *p; struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; @@ -85,13 +85,20 @@ ofdisk_hash_add_real (char *devpath) if (!p) return NULL; - p->devpath = devpath; + p->devpath = grub_strdup (devpath); + + if (!p->devpath) + { + grub_free (p); + return NULL; + } p->grub_devpath = grub_malloc (sizeof ("ieee1275/") + 2 * grub_strlen (p->devpath)); if (!p->grub_devpath) { + grub_free (p->devpath); grub_free (p); return NULL; } @@ -101,6 +108,7 @@ ofdisk_hash_add_real (char *devpath) p->open_path = grub_malloc (grub_strlen (p->devpath) + 3); if (!p->open_path) { + grub_free (p->devpath); grub_free (p->grub_devpath); grub_free (p); return NULL; @@ -140,7 +148,7 @@ check_string_removable (const char *str) } static struct ofdisk_hash_ent * -ofdisk_hash_add (char *devpath, char *curcan) +ofdisk_hash_add (const char *devpath, const char *curcan) { struct ofdisk_hash_ent *p, *pcan; @@ -160,8 +168,6 @@ ofdisk_hash_add (char *devpath, char *curcan) pcan = ofdisk_hash_find (curcan); if (!pcan) pcan = ofdisk_hash_add_real (curcan); - else - grub_free (curcan); if (check_string_removable (devpath) || check_string_removable (curcan)) pcan->is_removable = 1; @@ -191,18 +197,7 @@ dev_iterate_real (const char *name, const char *path) op = ofdisk_hash_find (path); if (!op) - { - char *name_dup = grub_strdup (name); - char *can = grub_strdup (path); - if (!name_dup || !can) - { - grub_errno = GRUB_ERR_NONE; - grub_free (name_dup); - grub_free (can); - return; - } - op = ofdisk_hash_add (name_dup, can); - } + op = ofdisk_hash_add (name, path); return; } @@ -658,6 +653,7 @@ insert_bootpath (void) char *device = grub_ieee1275_get_devname (bootpath); op = ofdisk_hash_add (device, NULL); op->is_boot = 1; + grub_free (device); } grub_free (type); grub_free (bootpath); diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index 3e12e6b24..20cbbd761 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -108,6 +108,9 @@ grub_ieee1275_find_options (void) if (rc >= 0) { char *ptr; + + if (grub_strncmp (tmp, "sun4v", 5) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_RAW_DEVNAMES); for (ptr = tmp; ptr - tmp < actual; ptr += grub_strlen (ptr) + 1) { if (grub_memcmp (ptr, "MacRISC", sizeof ("MacRISC") - 1) == 0 diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c index 98217029f..e14170821 100644 --- a/grub-core/kern/ieee1275/ieee1275.c +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -19,6 +19,7 @@ #include #include +#include #define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1) #define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) @@ -482,6 +483,93 @@ grub_ieee1275_close (grub_ieee1275_ihandle_t ihandle) return 0; } +int +grub_ieee1275_decode_unit4 (grub_ieee1275_ihandle_t ihandle, + void *addr, grub_size_t size, + grub_uint32_t *phy_lo, grub_uint32_t *phy_hi, + grub_uint32_t *lun_lo, grub_uint32_t *lun_hi) +{ + struct decode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 5); + args.method = (grub_ieee1275_cell_t) "decode-unit"; + args.ihandle = ihandle; + args.size = size; + args.addr = (grub_ieee1275_cell_t) addr; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "decode-unit failed\n"); + return -1; + } + + *phy_lo = args.tgt_l; + *phy_hi = args.tgt_h; + *lun_lo = args.lun_l; + *lun_hi = args.lun_h; + return 0; +} + +char * +grub_ieee1275_encode_uint4 (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t phy_lo, grub_uint32_t phy_hi, + grub_uint32_t lun_lo, grub_uint32_t lun_hi, + grub_size_t *size) +{ + char *addr; + struct encode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 3); + args.method = (grub_ieee1275_cell_t) "encode-unit"; + args.ihandle = ihandle; + + args.tgt_l = phy_lo; + args.tgt_h = phy_hi; + args.lun_l = lun_lo; + args.lun_h = lun_hi; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "encode-unit failed\n"); + return 0; + } + + addr = (void *)args.addr; + *size = args.size; + addr = grub_strdup ((char *)args.addr); + return addr; +} + + + int grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, grub_addr_t *result) @@ -607,3 +695,114 @@ grub_ieee1275_milliseconds (grub_uint32_t *msecs) *msecs = args.msecs; return 0; } + +int +grub_ieee1275_set_address (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t target, grub_uint32_t lun) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt; + grub_ieee1275_cell_t lun; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 1); + + /* IEEE Standard for Boot (Initialization Configuration) + Firmware: Core Requirements and Practices + E.3.2.2 Bus-specific methods for bus nodes + + A package implementing the scsi-2 device type shall implement the + following bus-specific method: + + set-address ( unit# target# -- ) + Sets the SCSI target number (0x0..0xf) and unit number (0..7) to which + subsequent commands apply. + */ + args.method = (grub_ieee1275_cell_t) "set-address"; + args.ihandle = ihandle; + args.tgt = target; + args.lun = lun; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return args.catch_result; +} + +int +grub_ieee1275_no_data_command (grub_ieee1275_ihandle_t ihandle, + const void *cmd_addr, grub_ssize_t *result) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t cmd_addr; + grub_ieee1275_cell_t error; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + /* IEEE 1275-1994 Standard for Boot (Initialization Configuration) + Firmware: Core Requirements and Practices + + E.3.2.2 Bus-specific methods for bus nodes + + A package implementing the scsi-2 device type shall implement the + following bus-specific method: + + no-data-command ( cmd-addr -- error? ) + Executes a simple SCSI command, automatically retrying under + certain conditions. cmd-addr is the address of a 6-byte command buffer + containing an SCSI command that does not have a data transfer phase. + Executes the command, retrying indefinitely with the same retry criteria + as retry-command. + + error? is nonzero if an error occurred, zero otherwise. + NOTE no-data-command is a convenience function. It provides + no capabilities that are not present in retry-command, but for + those commands that meet its restrictions, it is easier to use. + */ + args.method = (grub_ieee1275_cell_t) "no-data-command"; + args.ihandle = ihandle; + args.cmd_addr = (grub_ieee1275_cell_t) cmd_addr; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (result) + *result = args.error; + + return args.catch_result; +} + +int +grub_ieee1275_get_block_size (grub_ieee1275_ihandle_t ihandle) +{ + struct size_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t result; + grub_ieee1275_cell_t size; + } args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "block-size"; + args.ihandle = ihandle; + args.result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.result)) + return 0; + + return args.size; +} diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 9d163abcd..6e87cada6 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -30,6 +30,9 @@ #include #include #include +#ifdef __sparc__ +#include +#endif #include #include #include @@ -103,29 +106,13 @@ grub_machine_get_bootlocation (char **device __attribute__ ((unused)), void grub_machine_get_bootlocation (char **device, char **path) { - char *bootpath; - grub_ssize_t bootpath_size; + char *bootpath = NULL; char *filename; char *type; - if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", - &bootpath_size) - || bootpath_size <= 0) - { - /* Should never happen. */ - grub_printf ("/chosen/bootpath property missing!\n"); - return; - } - - bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); - if (! bootpath) - { - grub_print_error (); - return; - } - grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath, - (grub_size_t) bootpath_size + 1, 0); - bootpath[bootpath_size] = '\0'; + grub_ieee1275_get_boot_dev (&bootpath); + if (bootpath == NULL) + return; /* Transform an OF device path to a GRUB path. */ @@ -305,8 +292,11 @@ grub_machine_init (void) grub_console_init_early (); grub_claim_heap (); grub_console_init_lately (); +#ifdef __sparc__ + grub_obdisk_init (); +#else grub_ofdisk_init (); - +#endif grub_parse_cmdline (); #ifdef __i386__ @@ -321,7 +311,11 @@ grub_machine_fini (int flags) { if (flags & GRUB_LOADER_FLAG_NORETURN) { +#ifdef __sparc__ + grub_obdisk_fini (); +#else grub_ofdisk_fini (); +#endif grub_console_fini (); } } diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index ddb778340..81c03cf03 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -561,3 +561,30 @@ grub_ieee1275_canonicalise_devname (const char *path) return NULL; } +void +grub_ieee1275_get_boot_dev (char **boot_dev) +{ + char *bootpath; + grub_ssize_t bootpath_size; + + if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", + &bootpath_size) + || bootpath_size <= 0) + { + /* Should never happen. */ + grub_printf ("/chosen/bootpath property missing!\n"); + return; + } + + bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); + if (! bootpath) + { + grub_print_error (); + return; + } + grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath, + (grub_size_t) bootpath_size + 1, 0); + bootpath[bootpath_size] = '\0'; + + *boot_dev = bootpath; +} diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c index 78175aac2..be88baa60 100644 --- a/grub-core/kern/parser.c +++ b/grub-core/kern/parser.c @@ -30,7 +30,6 @@ static struct grub_parser_state_transition state_transitions[] = { {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0}, {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0}, {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0}, - {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0}, {GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1}, diff --git a/grub-core/kern/sparc64/ieee1275/ieee1275.c b/grub-core/kern/sparc64/ieee1275/ieee1275.c index 53be692c3..6e5b90a4f 100644 --- a/grub-core/kern/sparc64/ieee1275/ieee1275.c +++ b/grub-core/kern/sparc64/ieee1275/ieee1275.c @@ -89,3 +89,56 @@ grub_ieee1275_alloc_physmem (grub_addr_t *paddr, grub_size_t size, return args.catch_result; } + +grub_uint64_t +grub_ieee1275_num_blocks (grub_ieee1275_ihandle_t ihandle) +{ + struct nblocks_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t blocks; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "#blocks"; + args.ihandle = ihandle; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result != 0)) + return -1; + + /* If the number of blocks exceeds the range of an unsigned number, + return 0 to alert the caller to try the #blocks64 command. */ + if (args.blocks >= 0xffffffffULL) + return 0; + + return args.blocks; +} +grub_uint64_t +grub_ieee1275_num_blocks64 (grub_ieee1275_ihandle_t ihandle) +{ + struct nblocks_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t hi_blocks; + grub_ieee1275_cell_t lo_blocks; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 3); + args.method = (grub_ieee1275_cell_t) "#blocks64"; + args.ihandle = ihandle; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result != 0)) + return -1; + + return ((args.hi_blocks << 32) | (args.lo_blocks)); +} diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c index c77d6085c..caf8d4ee5 100644 --- a/grub-core/osdep/linux/blocklist.c +++ b/grub-core/osdep/linux/blocklist.c @@ -58,6 +58,11 @@ grub_install_get_blocklist (grub_device_t root_dev, struct fiemap fie1; int fd; +#ifdef __sparc__ + if (grub_strstr (container->partmap->name, "gpt")) + container_start = 0; +#endif + /* Write the first two sectors of the core image onto the disk. */ grub_util_info ("opening the core image `%s'", core_path); fd = open (core_path, O_RDONLY); diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c index a79682a5e..5155b0620 100644 --- a/grub-core/osdep/linux/ofpath.c +++ b/grub-core/osdep/linux/ofpath.c @@ -38,6 +38,44 @@ #include #include +#ifdef __sparc__ +typedef enum + { + GRUB_OFPATH_SPARC_WWN_ADDR = 1, + GRUB_OFPATH_SPARC_TGT_LUN, + } ofpath_sparc_addressing; + +struct ofpath_sparc_hba +{ + grub_uint32_t device_id; + ofpath_sparc_addressing addressing; +}; + +static struct ofpath_sparc_hba sparc_lsi_hba[] = { + /* Rhea, Jasper 320, LSI53C1020/1030. */ + {0x30, GRUB_OFPATH_SPARC_TGT_LUN}, + /* SAS-1068E. */ + {0x50, GRUB_OFPATH_SPARC_TGT_LUN}, + /* SAS-1064E. */ + {0x56, GRUB_OFPATH_SPARC_TGT_LUN}, + /* Pandora SAS-1068E. */ + {0x58, GRUB_OFPATH_SPARC_TGT_LUN}, + /* Aspen, Invader, LSI SAS-3108. */ + {0x5d, GRUB_OFPATH_SPARC_TGT_LUN}, + /* Niwot, SAS 2108. */ + {0x79, GRUB_OFPATH_SPARC_TGT_LUN}, + /* Erie, Falcon, LSI SAS 2008. */ + {0x72, GRUB_OFPATH_SPARC_WWN_ADDR}, + /* LSI WarpDrive 6203. */ + {0x7e, GRUB_OFPATH_SPARC_WWN_ADDR}, + /* LSI SAS 2308. */ + {0x87, GRUB_OFPATH_SPARC_WWN_ADDR}, + /* LSI SAS 3008. */ + {0x97, GRUB_OFPATH_SPARC_WWN_ADDR}, + {0, 0} +}; +#endif + #ifdef OFPATH_STANDALONE #define xmalloc malloc void @@ -120,6 +158,8 @@ find_obppath (const char *sysfs_path_orig) #endif fd = open(path, O_RDONLY); + +#ifndef __sparc__ if (fd < 0 || fstat (fd, &st) < 0) { if (fd >= 0) @@ -127,6 +167,7 @@ find_obppath (const char *sysfs_path_orig) snprintf(path, path_size, "%s/devspec", sysfs_path); fd = open(path, O_RDONLY); } +#endif if (fd < 0 || fstat (fd, &st) < 0) { @@ -307,6 +348,55 @@ of_path_of_ide(const char *sys_devname __attribute__((unused)), const char *devi return ret; } +#ifdef __sparc__ +static char * +of_path_of_nvme(const char *sys_devname __attribute__((unused)), + const char *device, + const char *devnode __attribute__((unused)), + const char *devicenode) +{ + char *sysfs_path, *of_path, disk[MAX_DISK_CAT]; + const char *digit_string, *part_end; + + digit_string = trailing_digits (device); + part_end = devicenode + strlen (devicenode) - 1; + + if ((*digit_string != '\0') && (*part_end == 'p')) + { + /* We have a partition number, strip it off. */ + int part; + char *nvmedev, *end; + + nvmedev = strdup (devicenode); + + if (nvmedev == NULL) + return NULL; + + end = nvmedev + strlen (nvmedev) - 1; + /* Remove the p. */ + *end = '\0'; + sscanf (digit_string, "%d", &part); + snprintf (disk, sizeof (disk), "/disk@1:%c", 'a' + (part - 1)); + sysfs_path = block_device_get_sysfs_path_and_link (nvmedev); + free (nvmedev); + } + else + { + /* We do not have the parition. */ + snprintf (disk, sizeof (disk), "/disk@1"); + sysfs_path = block_device_get_sysfs_path_and_link (device); + } + + of_path = find_obppath (sysfs_path); + + if (of_path) + strcat (of_path, disk); + + free (sysfs_path); + return of_path; +} +#endif + static int vendor_is_ATA(const char *path) { @@ -335,6 +425,66 @@ vendor_is_ATA(const char *path) return (memcmp(bufcont, "ATA", 3) == 0); } +#ifdef __sparc__ +static void +check_hba_identifiers (const char *sysfs_path, int *vendor, int *device_id) +{ + char *ed = strstr (sysfs_path, "host"); + size_t path_size; + char *p = NULL, *path = NULL; + char buf[8]; + int fd; + + if (!ed) + return; + + p = xstrdup (sysfs_path); + ed = strstr (p, "host"); + + if (!ed) + goto out; + + *ed = '\0'; + + path_size = (strlen (p) + sizeof ("vendor")); + path = xmalloc (path_size); + + if (!path) + goto out; + + snprintf (path, path_size, "%svendor", p); + fd = open (path, O_RDONLY); + + if (fd < 0) + goto out; + + memset (buf, 0, sizeof (buf)); + + if (read (fd, buf, sizeof (buf) - 1) < 0) + goto out; + + close (fd); + sscanf (buf, "%x", vendor); + snprintf (path, path_size, "%sdevice", p); + fd = open (path, O_RDONLY); + + if (fd < 0) + goto out; + + memset (buf, 0, sizeof (buf)); + + if (read (fd, buf, sizeof (buf) - 1) < 0) + goto out; + + close (fd); + sscanf (buf, "%x", device_id); + +out: + free (path); + free (p); +} +#endif + static void check_sas (const char *sysfs_path, int *tgt, unsigned long int *sas_address) { @@ -396,7 +546,7 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev { const char *p, *digit_string, *disk_name; int host, bus, tgt, lun; - unsigned long int sas_address; + unsigned long int sas_address = 0; char *sysfs_path, disk[MAX_DISK_CAT - sizeof ("/fp@0,0")]; char *of_path; @@ -413,9 +563,11 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev } of_path = find_obppath(sysfs_path); - free (sysfs_path); if (!of_path) - return NULL; + { + free (sysfs_path); + return NULL; + } if (strstr (of_path, "qlc")) strcat (of_path, "/fp@0,0"); @@ -444,6 +596,45 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev } else { +#ifdef __sparc__ + ofpath_sparc_addressing addressing = GRUB_OFPATH_SPARC_TGT_LUN; + int vendor = 0, device_id = 0; + char *optr = disk; + + check_hba_identifiers (sysfs_path, &vendor, &device_id); + + /* LSI Logic Vendor ID */ + if (vendor == 0x1000) + { + struct ofpath_sparc_hba *lsi_hba; + + /* Over time different OF addressing schemes have been supported. + There is no generic addressing scheme that works across + every HBA. */ + for (lsi_hba = sparc_lsi_hba; lsi_hba->device_id; lsi_hba++) + if (lsi_hba->device_id == device_id) + { + addressing = lsi_hba->addressing; + break; + } + } + + if (addressing == GRUB_OFPATH_SPARC_WWN_ADDR) + optr += snprintf (disk, sizeof (disk), "/%s@w%lx,%x", disk_name, + sas_address, lun); + else + optr += snprintf (disk, sizeof (disk), "/%s@%x,%x", disk_name, tgt, + lun); + + if (*digit_string != '\0') + { + int part; + + sscanf (digit_string, "%d", &part); + snprintf (optr, sizeof (disk) - (optr - disk - 1), ":%c", 'a' + + (part - 1)); + } +#else if (lun == 0) { int sas_id = 0; @@ -491,7 +682,9 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev } free (lunstr); } +#endif } + free (sysfs_path); strcat(of_path, disk); return of_path; } @@ -537,6 +730,11 @@ grub_util_devname_to_ofpath (const char *sys_devname) /* All the models I've seen have a devalias "floppy". New models have no floppy at all. */ ofpath = xstrdup ("floppy"); +#ifdef __sparc__ + else if (device[0] == 'n' && device[1] == 'v' && device[2] == 'm' + && device[3] == 'e') + ofpath = of_path_of_nvme (name_buf, device, devnode, devicenode); +#endif else { grub_util_warn (_("unknown device type %s"), device); diff --git a/include/grub/disk.h b/include/grub/disk.h index b385af826..bd58b11d5 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -49,6 +49,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_CBFSDISK_ID, GRUB_DISK_DEVICE_UBOOTDISK_ID, GRUB_DISK_DEVICE_XEN, + GRUB_DISK_DEVICE_OBDISK_ID, }; struct grub_disk; diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index 8e4251303..27b173678 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -146,6 +146,8 @@ enum grub_ieee1275_flag GRUB_IEEE1275_FLAG_BROKEN_REPEAT, GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN, + + GRUB_IEEE1275_FLAG_RAW_DEVNAMES, }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); @@ -211,6 +213,30 @@ int EXPORT_FUNC(grub_ieee1275_set_color) (grub_ieee1275_ihandle_t ihandle, int index, int r, int g, int b); int EXPORT_FUNC(grub_ieee1275_milliseconds) (grub_uint32_t *msecs); +int EXPORT_FUNC(grub_ieee1275_set_address) (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t target, + grub_uint32_t lun); + +int EXPORT_FUNC(grub_ieee1275_no_data_command) (grub_ieee1275_ihandle_t ihandle, + const void *cmd_addr, + grub_ssize_t *result); + +int EXPORT_FUNC(grub_ieee1275_decode_unit4) (grub_ieee1275_ihandle_t ihandle, + void *addr, grub_size_t size, + grub_uint32_t *phy_lo, + grub_uint32_t *phy_hi, + grub_uint32_t *lun_lo, + grub_uint32_t *lun_hi); + +char *EXPORT_FUNC(grub_ieee1275_encode_uint4) (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t phy_lo, + grub_uint32_t phy_hi, + grub_uint32_t lun_lo, + grub_uint32_t lun_hi, + grub_size_t *size); + +int EXPORT_FUNC(grub_ieee1275_get_block_size) (grub_ieee1275_ihandle_t ihandle); + grub_err_t EXPORT_FUNC(grub_claimmap) (grub_addr_t addr, grub_size_t size); @@ -235,6 +261,7 @@ void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *al void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath, struct grub_ieee1275_devalias *alias); +void EXPORT_FUNC(grub_ieee1275_get_boot_dev) (char **boot_dev); #define FOR_IEEE1275_DEVALIASES(alias) for (grub_ieee1275_devalias_init_iterator (&(alias)); grub_ieee1275_devalias_next (&(alias));) #define FOR_IEEE1275_DEVCHILDREN(devpath, alias) for (grub_ieee1275_children_first ((devpath), &(alias)); \ diff --git a/include/grub/ieee1275/obdisk.h b/include/grub/ieee1275/obdisk.h new file mode 100644 index 000000000..3d6fa0e62 --- /dev/null +++ b/include/grub/ieee1275/obdisk.h @@ -0,0 +1,25 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_OBDISK_HEADER +#define GRUB_OBDISK_HEADER 1 + +extern void grub_obdisk_init (void); +extern void grub_obdisk_fini (void); + +#endif diff --git a/include/grub/sparc64/ieee1275/ieee1275.h b/include/grub/sparc64/ieee1275/ieee1275.h index 32c77f80f..4b18468d8 100644 --- a/include/grub/sparc64/ieee1275/ieee1275.h +++ b/include/grub/sparc64/ieee1275/ieee1275.h @@ -42,6 +42,8 @@ extern int EXPORT_FUNC(grub_ieee1275_claim_vaddr) (grub_addr_t vaddr, extern int EXPORT_FUNC(grub_ieee1275_alloc_physmem) (grub_addr_t *paddr, grub_size_t size, grub_uint32_t align); +extern grub_uint64_t EXPORT_FUNC(grub_ieee1275_num_blocks) (grub_uint32_t ihandle); +extern grub_uint64_t EXPORT_FUNC(grub_ieee1275_num_blocks64) (grub_uint32_t ihandle); extern grub_addr_t EXPORT_VAR (grub_ieee1275_original_stack); diff --git a/util/grub-install.c b/util/grub-install.c index 04a520481..0b97c1ac0 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -1616,6 +1616,7 @@ main (int argc, char *argv[]) { grub_util_fprint_full_disk_name (load_cfg_f, g, dev); fprintf (load_cfg_f, " "); + free (g); } if (dev != grub_dev) grub_device_close (dev); diff --git a/util/ieee1275/grub-ofpathname.c b/util/ieee1275/grub-ofpathname.c index 8e5d766cb..300fbddad 100644 --- a/util/ieee1275/grub-ofpathname.c +++ b/util/ieee1275/grub-ofpathname.c @@ -46,7 +46,9 @@ int main(int argc, char **argv) } of_path = grub_util_devname_to_ofpath (argv[1]); - printf("%s\n", of_path); + + if (of_path) + printf ("%s\n", of_path); free (of_path); diff --git a/util/setup.c b/util/setup.c index 3b5c46fde..15e7de0a7 100644 --- a/util/setup.c +++ b/util/setup.c @@ -200,7 +200,6 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length, #endif } -#ifdef GRUB_SETUP_BIOS /* Context for setup/identify_partmap. */ struct identify_partmap_ctx { @@ -236,7 +235,6 @@ identify_partmap (grub_disk_t disk __attribute__ ((unused)), ctx->multiple_partmaps = 1; return 1; } -#endif #ifdef GRUB_SETUP_BIOS #define SETUP grub_util_bios_setup @@ -257,9 +255,7 @@ SETUP (const char *dir, char *boot_img, *core_img, *boot_path; char *root = 0; size_t boot_size, core_size; -#ifdef GRUB_SETUP_BIOS grub_uint16_t core_sectors; -#endif grub_device_t root_dev = 0, dest_dev, core_dev; grub_util_fd_t fp; struct blocklists bl; @@ -283,10 +279,8 @@ SETUP (const char *dir, core_path = grub_util_get_path (dir, core_file); core_size = grub_util_get_image_size (core_path); -#ifdef GRUB_SETUP_BIOS core_sectors = ((core_size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); -#endif if (core_size < GRUB_DISK_SECTOR_SIZE) grub_util_error (_("the size of `%s' is too small"), core_path); #ifdef GRUB_SETUP_BIOS @@ -368,8 +362,8 @@ SETUP (const char *dir, if (grub_env_set ("root", root) != GRUB_ERR_NONE) grub_util_error ("%s", grub_errmsg); -#ifdef GRUB_SETUP_BIOS { +#ifdef GRUB_SETUP_BIOS char *tmp_img; grub_uint8_t *boot_drive_check; @@ -394,6 +388,7 @@ SETUP (const char *dir, boot_drive_check[0] = 0x90; boot_drive_check[1] = 0x90; } +#endif struct identify_partmap_ctx ctx = { .dest_partmap = NULL, @@ -409,6 +404,7 @@ SETUP (const char *dir, grub_partition_iterate (dest_dev->disk, identify_partmap, &ctx); +#ifdef GRUB_SETUP_BIOS /* Copy the partition table. */ if (ctx.dest_partmap || (!allow_floppy && !grub_util_biosdisk_is_floppy (dest_dev->disk))) @@ -417,6 +413,7 @@ SETUP (const char *dir, GRUB_BOOT_MACHINE_PART_END - GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC); free (tmp_img); +#endif if (ctx.container && grub_strcmp (ctx.container->partmap->name, "msdos") == 0 @@ -504,10 +501,21 @@ SETUP (const char *dir, else maxsec = core_sectors; +#ifdef GRUB_SETUP_BIOS if (maxsec > ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) >> GRUB_DISK_SECTOR_BITS)) maxsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) >> GRUB_DISK_SECTOR_BITS); +#endif + +#ifdef GRUB_SETUP_SPARC64 + /* On SPARC we need two extra. One is because we are combining the + * core.img with the boot.img. The other is because the boot sector + * starts at 1. + */ + nsec += 2; + maxsec += 2; +#endif if (is_ldm) err = grub_util_ldm_embed (dest_dev->disk, &nsec, maxsec, @@ -556,9 +564,35 @@ SETUP (const char *dir, bl.block--; bl.block->start = 0; bl.block->len = 0; +#ifdef GRUB_SETUP_BIOS bl.block->segment = 0; +#endif +#ifdef GRUB_SETUP_SPARC64 + { + /* On SPARC, the block-list entries need to be based off the beginning + of the parition, not the beginning of the disk. */ + struct grub_boot_blocklist *block; + block = bl.first_block; + + while (block->len) + { + block->start -= bl.first_sector; + block--; + } + } + + /* Reserve space for the boot block since it can not be in the + Parition table on SPARC */ + assert (bl.first_block->len > 2); + bl.first_block->start += 2; + bl.first_block->len -= 2; + write_rootdev (root_dev, boot_img, sectors[BOOT_SECTOR + 1] - bl.first_sector); +#endif + +#ifdef GRUB_SETUP_BIOS write_rootdev (root_dev, boot_img, bl.first_sector); +#endif /* Round up to the nearest sector boundary, and zero the extra memory */ core_img = xrealloc (core_img, nsec * GRUB_DISK_SECTOR_SIZE); @@ -568,7 +602,7 @@ SETUP (const char *dir, bl.first_block = (struct grub_boot_blocklist *) (core_img + GRUB_DISK_SECTOR_SIZE - sizeof (*bl.block)); - +#if GRUB_SETUP_BIOS grub_size_t no_rs_length; no_rs_length = grub_target_to_host16 (grub_get_unaligned16 (core_img @@ -599,6 +633,26 @@ SETUP (const char *dir, grub_disk_write (dest_dev->disk, sectors[i], 0, GRUB_DISK_SECTOR_SIZE, core_img + i * GRUB_DISK_SECTOR_SIZE); +#endif +#ifdef GRUB_SETUP_SPARC64 + { + int isec = BOOT_SECTOR; + + /* Write the boot image onto the disk. */ + if (grub_disk_write (dest_dev->disk, sectors[isec++], 0, + GRUB_DISK_SECTOR_SIZE, boot_img)) + grub_util_error ("%s", grub_errmsg); + + /* Write the core image onto the disk. */ + for (i = 0 ; isec < nsec; i++, isec++) + { + if (grub_disk_write (dest_dev->disk, sectors[isec], 0, + GRUB_DISK_SECTOR_SIZE, + core_img + i * GRUB_DISK_SECTOR_SIZE)) + grub_util_error ("%s", grub_errmsg); + } + } +#endif grub_free (sectors); @@ -608,7 +662,6 @@ SETUP (const char *dir, } unable_to_embed: -#endif if (dest_dev->disk->dev->id != root_dev->disk->dev->id) grub_util_error ("%s", _("embedding is not possible, but this is required for " @@ -729,15 +782,21 @@ unable_to_embed: { char *buf, *ptr = core_img; size_t len = core_size; - grub_uint64_t blk; + grub_uint64_t blk, offset = 0; grub_partition_t container = core_dev->disk->partition; grub_err_t err; core_dev->disk->partition = 0; +#ifdef GRUB_SETUP_SPARC64 + { + if (grub_strstr (container->partmap->name, "gpt")) + offset = grub_partition_get_start (container); + } +#endif buf = xmalloc (core_size); blk = bl.first_sector; - err = grub_disk_read (core_dev->disk, blk, 0, GRUB_DISK_SECTOR_SIZE, buf); + err = grub_disk_read (core_dev->disk, blk + offset, 0, GRUB_DISK_SECTOR_SIZE, buf); if (err) grub_util_error (_("cannot read `%s': %s"), core_dev->disk->name, grub_errmsg); @@ -756,7 +815,7 @@ unable_to_embed: if (cur > len) cur = len; - err = grub_disk_read (core_dev->disk, blk, 0, cur, buf); + err = grub_disk_read (core_dev->disk, blk + offset, 0, cur, buf); if (err) grub_util_error (_("cannot read `%s': %s"), core_dev->disk->name, grub_errmsg); @@ -786,6 +845,10 @@ unable_to_embed: 0, GRUB_DISK_SECTOR_SIZE, boot_img)) grub_util_error ("%s", grub_errmsg); +#ifdef GRUB_SETUP_SPARC64 + finish: +#endif + grub_util_biosdisk_flush (root_dev->disk); grub_util_biosdisk_flush (dest_dev->disk);