Skip to content
This repository has been archived by the owner on Sep 18, 2020. It is now read-only.

Commit

Permalink
gpt: new gptprio.next command for selecting priority based partitions
Browse files Browse the repository at this point in the history
Basic usage would look something like this:

    gptprio.next -d usr_dev -u usr_uuid
    linuxefi ($usr_dev)/boot/vmlinuz mount.usr=PARTUUID=$usr_uuid

After booting the system should set the 'successful' bit on the
partition that was used.
  • Loading branch information
marineam committed Nov 23, 2014
1 parent d3c2759 commit e49d5b5
Show file tree
Hide file tree
Showing 5 changed files with 448 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Makefile.util.def
Expand Up @@ -1156,6 +1156,12 @@ script = {
common = tests/gptrepair_test.in;
};

script = {
testcase;
name = gptprio_test;
common = tests/gptprio_test.in;
};

program = {
testcase;
name = example_unit_test;
Expand Down
5 changes: 5 additions & 0 deletions grub-core/Makefile.core.def
Expand Up @@ -831,6 +831,11 @@ module = {
common = commands/gptrepair.c;
};

module = {
name = gptprio;
common = commands/gptprio.c;
};

module = {
name = gpt;
common = lib/gpt.c;
Expand Down
238 changes: 238 additions & 0 deletions grub-core/commands/gptprio.c
@@ -0,0 +1,238 @@
/* gptprio.c - manage priority based partition selection. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
* Copyright (C) 2014 CoreOS, 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 <http://www.gnu.org/licenses/>.
*/

#include <grub/device.h>
#include <grub/env.h>
#include <grub/err.h>
#include <grub/extcmd.h>
#include <grub/gpt_partition.h>
#include <grub/i18n.h>
#include <grub/misc.h>

GRUB_MOD_LICENSE ("GPLv3+");

static const struct grub_arg_option options_next[] = {
{"set-device", 'd', 0,
N_("Set a variable to the name of selected partition."),
N_("VARNAME"), ARG_TYPE_STRING},
{"set-uuid", 'u', 0,
N_("Set a variable to the GPT UUID of selected partition."),
N_("VARNAME"), ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};

enum options_next
{
NEXT_SET_DEVICE,
NEXT_SET_UUID,
};

static unsigned int
grub_gptprio_priority (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY, 4);
}

static unsigned int
grub_gptprio_tries_left (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4);
}

static void
grub_gptprio_set_tries_left (struct grub_gpt_partentry *entry,
unsigned int tries_left)
{
grub_gpt_entry_set_attribute
(entry, tries_left, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT, 4);
}

static unsigned int
grub_gptprio_successful (struct grub_gpt_partentry *entry)
{
return (unsigned int) grub_gpt_entry_attribute
(entry, GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL, 1);
}

static grub_err_t
grub_find_next (const char *disk_name,
const grub_gpt_part_type_t *part_type,
char **part_name, char **part_guid)
{
struct grub_gpt_partentry *part_found = NULL;
grub_device_t dev = NULL;
grub_gpt_t gpt = NULL;
grub_uint32_t i, part_index;

dev = grub_device_open (disk_name);
if (!dev)
goto done;

gpt = grub_gpt_read (dev->disk);
if (!gpt)
goto done;

if (!(gpt->status & GRUB_GPT_BOTH_VALID))
if (grub_gpt_repair (dev->disk, gpt))
goto done;

for (i = 0; i < grub_le_to_cpu32 (gpt->primary.maxpart); i++)
{
struct grub_gpt_partentry *part = &gpt->entries[i];

if (grub_memcmp (part_type, &part->type, sizeof (*part_type)) == 0)
{
unsigned int priority, tries_left, successful, old_priority = 0;

priority = grub_gptprio_priority (part);
tries_left = grub_gptprio_tries_left (part);
successful = grub_gptprio_successful (part);

if (part_found)
old_priority = grub_gptprio_priority (part_found);

if ((tries_left || successful) && priority > old_priority)
{
part_index = i;
part_found = part;
}
}
}

if (!part_found)
{
grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
goto done;
}

if (grub_gptprio_tries_left (part_found))
{
unsigned int tries_left = grub_gptprio_tries_left (part_found);

grub_gptprio_set_tries_left (part_found, tries_left - 1);

if (grub_gpt_update_checksums (gpt))
goto done;

if (grub_gpt_write (dev->disk, gpt))
goto done;
}

*part_name = grub_xasprintf ("%s,gpt%u", disk_name, part_index + 1);
if (!*part_name)
goto done;

*part_guid =
grub_xasprintf ("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
grub_le_to_cpu32 (part_found->guid.data1),
grub_le_to_cpu16 (part_found->guid.data2),
grub_le_to_cpu16 (part_found->guid.data3),
part_found->guid.data4[0],
part_found->guid.data4[1],
part_found->guid.data4[2],
part_found->guid.data4[3],
part_found->guid.data4[4],
part_found->guid.data4[5],
part_found->guid.data4[6],
part_found->guid.data4[7]);
if (!*part_name)
goto done;

grub_errno = GRUB_ERR_NONE;

done:
grub_gpt_free (gpt);

if (dev)
grub_device_close (dev);

return grub_errno;
}



static grub_err_t
grub_cmd_next (grub_extcmd_context_t ctxt, int argc, char **args)
{
struct grub_arg_list *state = ctxt->state;
char *p, *root = NULL, *part_name = NULL, *part_guid = NULL;

/* TODO: Add a uuid parser and a command line flag for providing type. */
grub_gpt_part_type_t part_type = GRUB_GPT_PARTITION_TYPE_USR_X86_64;

if (!state[NEXT_SET_DEVICE].set || !state[NEXT_SET_UUID].set)
{
grub_error (GRUB_ERR_INVALID_COMMAND, N_("-d and -u are required"));
goto done;
}

if (argc == 0)
root = grub_strdup (grub_env_get ("root"));
else if (argc == 1)
root = grub_strdup (args[0]);
else
{
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments"));
goto done;
}

if (!root)
goto done;

/* To make using $root practical strip off the partition name. */
p = grub_strchr (root, ',');
if (p)
*p = '\0';

if (grub_find_next (root, &part_type, &part_name, &part_guid))
goto done;

if (grub_env_set (state[NEXT_SET_DEVICE].arg, part_name))
goto done;

if (grub_env_set (state[NEXT_SET_UUID].arg, part_guid))
goto done;

grub_errno = GRUB_ERR_NONE;

done:
grub_free (root);
grub_free (part_name);
grub_free (part_guid);

return grub_errno;
}

static grub_extcmd_t cmd_next;

GRUB_MOD_INIT(gptprio)
{
cmd_next = grub_register_extcmd ("gptprio.next", grub_cmd_next, 0,
N_("-d VARNAME -u VARNAME [DEVICE]"),
N_("Select next partition to boot."),
options_next);
}

GRUB_MOD_FINI(gptprio)
{
grub_unregister_extcmd (cmd_next);
}
49 changes: 49 additions & 0 deletions include/grub/gpt_partition.h
Expand Up @@ -53,6 +53,10 @@ typedef struct grub_gpt_guid grub_gpt_part_type_t;
GRUB_GPT_GUID_INIT (0x5808c8aa, 0x7e8f, 0x42e0, \
0x85, 0xd2, 0xe1, 0xe9, 0x04, 0x34, 0xcf, 0xb3)

#define GRUB_GPT_PARTITION_TYPE_USR_X86_64 \
GRUB_GPT_GUID_INIT (0x5dfbf5f4, 0x2848, 0x4bac, \
0xaa, 0x5e, 0x0d, 0x9a, 0x20, 0xb7, 0x45, 0xa6)

#define GRUB_GPT_HEADER_MAGIC \
{ 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 }

Expand Down Expand Up @@ -87,6 +91,51 @@ struct grub_gpt_partentry
char name[72];
} GRUB_PACKED;

enum grub_gpt_part_attr_offset
{
/* Standard partition attribute bits defined by UEFI. */
GRUB_GPT_PART_ATTR_OFFSET_REQUIRED = 0,
GRUB_GPT_PART_ATTR_OFFSET_NO_BLOCK_IO_PROTOCOL = 1,
GRUB_GPT_PART_ATTR_OFFSET_LEGACY_BIOS_BOOTABLE = 2,

/* De facto standard attribute bits defined by Microsoft and reused by
* http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec */
GRUB_GPT_PART_ATTR_OFFSET_READ_ONLY = 60,
GRUB_GPT_PART_ATTR_OFFSET_NO_AUTO = 63,

/* Partition attributes for priority based selection,
* Currently only valid for PARTITION_TYPE_USR_X86_64.
* TRIES_LEFT and PRIORITY are 4 bit wide fields. */
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_PRIORITY = 48,
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_TRIES_LEFT = 52,
GRUB_GPT_PART_ATTR_OFFSET_GPTPRIO_SUCCESSFUL = 56,
};

/* Helpers for reading/writing partition attributes. */
static inline grub_uint64_t
grub_gpt_entry_attribute (struct grub_gpt_partentry *entry,
enum grub_gpt_part_attr_offset offset,
unsigned int bits)
{
grub_uint64_t attrib = grub_le_to_cpu64 (entry->attrib);

return (attrib >> offset) & ((1ULL << bits) - 1);
}

static inline void
grub_gpt_entry_set_attribute (struct grub_gpt_partentry *entry,
grub_uint64_t value,
enum grub_gpt_part_attr_offset offset,
unsigned int bits)
{
grub_uint64_t attrib, mask;

mask = (((1ULL << bits) - 1) << offset);
attrib = grub_le_to_cpu64 (entry->attrib) & ~mask;
attrib |= ((value << offset) & mask);
entry->attrib = grub_cpu_to_le64 (attrib);
}

/* Basic GPT partmap module. */
grub_err_t
grub_gpt_partition_map_iterate (grub_disk_t disk,
Expand Down

0 comments on commit e49d5b5

Please sign in to comment.