diff mbox series

[RFC,1/5] firmware-utils/ptgen: add Chromium OS kernel partition support

Message ID 20200718205148.1743807-2-computersforpeace@gmail.com
State New
Headers show
Series Add support for Chromium OS and Google WiFi | expand

Commit Message

Brian Norris July 18, 2020, 8:51 p.m. UTC
Chrom{ium,e} OS (shortened as "CrOS") bootloaders use a custom GPT
partition type to locate their kernel(s), with custom attributes for
noting properties around which partition(s) should be active and how
many times they've been tried as part of their A/B in-place upgrade
system.

OpenWRT doesn't use A/B updates for upgrades (instead, just shutting
things down far enough to reprogram the necessary partitions), so all we
need to do is tell the bootloader which one is the kernel partition, and
how to use it (i.e., set the "successful" and "priority" attributes).

ptgen already supports some basic GPT partition creation, so just
add support for a '-T <GPT partition type>' argument. Currently, this
only supports '-T cros_kernel', but it could be extended if there are
other GPT partition types needed.

For GPT attribute and GUID definitions, see the CrOS verified boot
sources:

https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/refs/heads/master/firmware/lib/cgptlib/include/cgptlib_internal.h
https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/refs/heads/master/firmware/include/gpt.h

The GUID is also recognized in fdisk, and likely other utilities, but
creation/manipulation is typically done via the 'cgpt' utility, provided
as part of the vboot_reference project.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
---
 tools/firmware-utils/src/ptgen.c | 39 +++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/tools/firmware-utils/src/ptgen.c b/tools/firmware-utils/src/ptgen.c
index 223ee295611f..bc1da6d64061 100644
--- a/tools/firmware-utils/src/ptgen.c
+++ b/tools/firmware-utils/src/ptgen.c
@@ -80,6 +80,10 @@  typedef struct {
 	GUID_INIT( 0x21686148, 0x6449, 0x6E6F, \
 			0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49)
 
+#define GUID_PARTITION_CHROME_OS_KERNEL \
+	GUID_INIT( 0xFE3A2A5D, 0x4F32, 0x41A7, \
+			0xB7, 0x25, 0xAC, 0xCC, 0x32, 0x85, 0xA3, 0x09)
+
 #define GPT_HEADER_SIZE         92
 #define GPT_ENTRY_SIZE          128
 #define GPT_ENTRY_MAX           128
@@ -109,6 +113,9 @@  struct partinfo {
 	unsigned long start;
 	unsigned long size;
 	int type;
+	bool has_gtype;
+	guid_t gtype;  /* GPT partition type */
+	uint64_t gattr;  /* GPT partition attributes */
 };
 
 /* GPT Partition table header */
@@ -248,6 +255,19 @@  static inline int guid_parse(char *buf, guid_t *guid)
 	return 0;
 }
 
+/* Map GPT partition types to partition GUIDs. */
+static inline bool parse_gpt_parttype(const char *type, struct partinfo *part)
+{
+	if (!strcmp(type, "cros_kernel")) {
+		part->has_gtype = true;
+		part->gtype = GUID_PARTITION_CHROME_OS_KERNEL;
+		part->gattr = (1ULL << 48) |  /* priority=1 */
+			      (1ULL << 56);  /* success=1 */
+		return true;
+	}
+	return false;
+}
+
 /* init an utf-16 string from utf-8 string */
 static inline void init_utf16(char *str, uint16_t *buf, unsigned bufsize)
 {
@@ -396,12 +416,15 @@  static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr)
 		gpte[i].end = cpu_to_le64(sect -1);
 		gpte[i].guid = guid;
 		gpte[i].guid.b[sizeof(guid_t) -1] += i + 1;
-		if (parts[i].type == 0xEF || (i + 1) == (unsigned)active) {
+		if (parts[i].has_gtype) {
+			gpte[i].type = parts[i].gtype;
+		} else if (parts[i].type == 0xEF || (i + 1) == (unsigned)active) {
 			gpte[i].type = GUID_PARTITION_SYSTEM;
 			init_utf16("EFI System Partition", (uint16_t *)gpte[i].name, GPT_ENTRY_NAME_SIZE / sizeof(uint16_t));
 		} else {
 			gpte[i].type = GUID_PARTITION_BASIC_DATA;
 		}
+		gpte[i].attr = parts[i].gattr;
 
 		if (verbose)
 			fprintf(stderr, "Partition %d: start=%" PRIu64 ", end=%" PRIu64 ", size=%"  PRIu64 "\n",
@@ -498,7 +521,9 @@  fail:
 
 static void usage(char *prog)
 {
-	fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [-l <align kB>] [-G <guid>] [[-t <type>] -p <size>[@<start>]...] \n", prog);
+	fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h <heads> -s <sectors> -o <outputfile>\n"
+			"          [-a 0..4] [-l <align kB>] [-G <guid>]\n"
+			"          [[-t <type> | -T <GPT part type>] -p <size>[@<start>]...] \n", prog);
 	exit(EXIT_FAILURE);
 }
 
@@ -512,7 +537,7 @@  int main (int argc, char **argv)
 	guid_t guid = GUID_INIT( signature, 0x2211, 0x4433, \
 			0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x00);
 
-	while ((ch = getopt(argc, argv, "h:s:p:a:t:o:vngl:S:G:")) != -1) {
+	while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vngl:S:G:")) != -1) {
 		switch (ch) {
 		case 'o':
 			filename = optarg;
@@ -560,6 +585,14 @@  int main (int argc, char **argv)
 		case 'S':
 			signature = strtoul(optarg, NULL, 0);
 			break;
+		case 'T':
+			if (!parse_gpt_parttype(optarg, &parts[part])) {
+				fprintf(stderr,
+					"Invalid GPT partition type \"%s\"\n",
+					optarg);
+				exit(EXIT_FAILURE);
+			}
+			break;
 		case 'G':
 			if (guid_parse(optarg, &guid)) {
 				fputs("Invalid guid string\n", stderr);