File MARM-add-m-and-M-options.diff of Package efibootmgr

From f6f14847e1f37ed3b5e05ef830b3db5ea122aa17 Mon Sep 17 00:00:00 2001
From: "Luck, Tony" <tony.luck@intel.com>
Date: Tue, 9 Jun 2015 10:14:58 -0700
Subject: [PATCH] Add -m and -M options to support address range mirror

BIOS and OS pass messages for address range mirror using a pair of
UEFI variables:

   MirrorCurrent: Written by BIOS to tell the OS that the platform supports
	address range mirror and to say whether mirroring was set up using
	the desired parameters
   MirrorRequest: Written by OS to tell BIOS new paramters to use on next boot

This is documented in the RAS validation guide for memory address range
mirroring for the Xeon E7-v3 family:

  https://software.intel.com/sites/default/files/managed/43/6a/Memory%20Address%20Range%20Mirroring%20Validation%20Guide.pdf

Signed-off-by: Tony Luck <tony.luck@intel.com>
---
 src/efibootmgr/efibootmgr.c | 146 +++++++++++++++++++++++++++++++++++++++++++-
 src/include/efi.h           |  14 +++++
 src/include/efibootmgr.h    |   4 ++
 3 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/src/efibootmgr/efibootmgr.c b/src/efibootmgr/efibootmgr.c
index a5b5bf9..886f98c 100644
--- a/src/efibootmgr/efibootmgr.c
+++ b/src/efibootmgr/efibootmgr.c
@@ -878,9 +878,124 @@ set_active_state()
 	return EFI_NOT_FOUND;
 }
 
+static int
+get_mirror(int which, int *below4g, int *above4g, int *mirrorstatus)
+{
+	int rc;
+	ADDRESS_RANGE_MIRROR_VARIABLE_DATA *abm;
+	char *name, name_guid[PATH_MAX];
+	efi_guid_t guid;
+	char text_guid[40];
+	efi_variable_t *var;
+
+	if (which)
+		name = ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST;
+	else
+		name = ADDRESS_RANGE_MIRROR_VARIABLE_CURRENT;
+
+	var = malloc(sizeof(var_entry_t));
+	guid = ADDRESS_RANGE_MIRROR_VARIABLE_GUID;
+	efi_guid_unparse(&guid, text_guid); 
+	snprintf(name_guid, sizeof(name_guid), "%s-%s", name, text_guid);
+	rc = read_variable(name_guid, var);
+	if (rc == 0) {
+		abm = (ADDRESS_RANGE_MIRROR_VARIABLE_DATA *) var->Data;
+		if (!which && abm->mirror_version != MIRROR_VERSION) {
+			fprintf(stderr, "** Warning ** : unrecognised version for memory mirror i/f\n");
+			return 2;
+		}
+		*below4g = abm->mirror_memory_below_4gb;
+		*above4g = abm->mirror_amount_above_4gb;
+		*mirrorstatus = abm->mirror_status;
+	}
+	efichar_from_char(var->VariableName, name, 1024);
+	return rc;
+}
+
+static int
+set_mirror(int below4g, int above4g)
+{
+	int s, status, rc;
+	ADDRESS_RANGE_MIRROR_VARIABLE_DATA abm;
+	int oldbelow4g, oldabove4g;
+	efi_variable_t *var;
+	char *name;
+
+	if ((s = get_mirror(0, &oldbelow4g, &oldabove4g, &status)) == 0) {
+		if (oldbelow4g == below4g && oldabove4g == above4g)
+			return 0;
+	} else {
+		fprintf(stderr, "** Warning ** : platform does not support memory mirror\n");
+		return s;
+	}
+
+	var = malloc(sizeof(var_entry_t));
+	memset(var, 0, sizeof(efi_variable_t));
+	var->DataSize = sizeof (abm);
+	memcpy(&(var->Data), (uint8_t *)&abm, var->DataSize);
+	var->Attributes = EFI_VARIABLE_NON_VOLATILE
+		| EFI_VARIABLE_BOOTSERVICE_ACCESS
+		| EFI_VARIABLE_RUNTIME_ACCESS;
+
+	abm.mirror_version = MIRROR_VERSION;
+	abm.mirror_amount_above_4gb = opts.set_mirror_hi ? above4g : oldabove4g;
+	abm.mirror_memory_below_4gb = opts.set_mirror_lo ? below4g : oldbelow4g;
+	abm.mirror_status = 0;
+	memcpy(&(var->Data), (uint8_t *)&abm, var->DataSize);
+	var->VendorGuid = ADDRESS_RANGE_MIRROR_VARIABLE_GUID;
+	name = ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST;
+	efichar_from_char(var->VariableName, name, 1024);
+	rc = create_or_edit_variable(var);
+	return rc;
+}
+
+static void
+show_mirror(void)
+{
+	int status;
+	int below4g, above4g;
+	int rbelow4g, rabove4g;
+
+	if (get_mirror(0, &below4g, &above4g, &status) == 0) {
+		if (status == 0) {
+			printf("MirroredPercentageAbove4G: %d.%.2d\n", above4g/100, above4g%100);
+			printf("MirrorMemoryBelow4GB: %s\n", below4g ? "true" : "false");
+		} else {
+			printf("MirrorStatus: ");
+			switch (status) {
+			case 1:
+				printf("Platform does not support address range mirror\n");
+				break;
+			case 2:
+				printf("Invalid version number\n");
+				break;
+			case 3:
+				printf("MirroredMemoryAbove4GB > 50.00%%\n");
+				break;
+			case 4:
+				printf("DIMM configuration does not allow mirror\n");
+				break;
+			case 5:
+				printf("OEM specific method\n");
+				break;
+			default:
+				printf("%u\n", status);
+				break;
+			}
+			printf("DesiredMirroredPercentageAbove4G: %d.%.2d\n", above4g/100, above4g%100);
+			printf("DesiredMirrorMemoryBelow4GB: %s\n", below4g ? "true" : "false");
+		}
+	}
+	if ((get_mirror(1, &rbelow4g, &rabove4g, &status) == 0) &&
+		(above4g != rabove4g || below4g != rbelow4g)) {
+		printf("RequestMirroredPercentageAbove4G: %d.%.2d\n", rabove4g/100, rabove4g%100);
+		printf("RequestMirrorMemoryBelow4GB: %s\n", rbelow4g ? "true" : "false");
+	}
+}
+
 
 
 
 static void
 usage()
 {
@@ -901,6 +1010,8 @@ usage()
 	printf("\t-i | --iface name     create a netboot entry for the named interface\n");
 	printf("\t-l | --loader name     (defaults to \""DEFAULT_LOADER"\")\n");
 	printf("\t-L | --label label     Boot manager display label (defaults to \"Linux\")\n");
+	printf("\t-m | --mirror-below-4G t|f mirror memory below 4GB\n");
+	printf("\t-M | --mirror-above-4G X percentage memory to mirror above 4GB\n");
 	printf("\t-n | --bootnext XXXX   set BootNext to XXXX (hex)\n");
 	printf("\t-N | --delete-bootnext delete BootNext\n");
 	printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,...     explicitly set BootOrder (hex)\n");
@@ -943,6 +1054,7 @@ parse_opts(int argc, char **argv)
 {
 	int c, num, rc;
 	int option_index = 0;
+	float fnum;
 
 	while (1)
 	{
@@ -963,6 +1055,8 @@ parse_opts(int argc, char **argv)
 			{"gpt",                    no_argument, 0, 'g'},
 			{"loader",           required_argument, 0, 'l'},
 			{"label",            required_argument, 0, 'L'},
+			{"mirror-below-4G",  required_argument, 0, 'm'},
+			{"mirror-above-4G",  required_argument, 0, 'M'},
 			{"bootnext",         required_argument, 0, 'n'},
 			{"delete-bootnext",        no_argument, 0, 'N'},
 			{"bootorder",        required_argument, 0, 'o'},
@@ -984,7 +1002,7 @@ parse_opts(int argc, char **argv)
 		};
 
 		c = getopt_long (argc, argv,
-				 "AaBb:cCd:e:E:gH:i:l:L:n:N"
+				 "AaBb:cCd:e:E:gH:i:l:L:M:m:n:N"
 				 "o:Op:P:qt:TuU:v::Vw@:",
 				 long_options, &option_index);
 		if (c == -1)
@@ -1058,6 +1172,31 @@ parse_opts(int argc, char **argv)
 		case 'L':
 			opts.label = optarg;
 			break;
+		case 'm':
+			opts.set_mirror_lo = 1;
+			switch (optarg[0]) {
+			case '1': case 'y': case 't':
+				opts.below4g = 1;
+				break;
+			case '0': case 'n': case 'f':
+				opts.below4g = 0;
+				break;
+			default:
+				fprintf (stderr,"invalid boolean value %s\n",optarg);
+				exit(1);
+			}
+			break;
+		case 'M':
+			opts.set_mirror_hi = 1;
+			rc = sscanf(optarg, "%f", &fnum);
+			if (rc == 1 && fnum <= 50) {
+				opts.above4g = fnum * 100; /* percent to basis points */
+			}
+			else {
+				fprintf (stderr,"invalid numeric value %s\n",optarg);
+				exit(1);
+			}
+			break;
 		case 'N':
 			opts.delete_bootnext = 1;
 			break;
@@ -1271,6 +1310,10 @@ main(int argc, char **argv)
 			ret=set_boot_u16("Timeout", opts.timeout);
 		}
 
+		if (opts.set_mirror_lo || opts.set_mirror_hi) {
+			ret=set_mirror(opts.below4g, opts.above4g);
+		}
+
 		if (!opts.quiet && ret == 0) {
 			num = read_boot_u16("BootNext");
 			if (num != -1 ) {
@@ -1286,6 +1429,7 @@ main(int argc, char **argv)
 			}
 			show_boot_order();
 			show_boot_vars();
+			show_mirror();
 		} else if (ret > 1) {
 			/* only print efi_status_t values as others already have a message! */
 			fprintf (stderr,"\n\nrequested operation failed: status=%lx\n\n", ret);
diff --git a/src/include/efi.h b/src/include/efi.h
index b8d3b12..b1e760e 100644
--- a/src/include/efi.h
+++ b/src/include/efi.h
@@ -83,6 +83,8 @@
 EFI_GUID( 0x47c7b227, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
 #define ESP_UNKNOWN_GUID \
 EFI_GUID( 0x47c7b226, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+#define ADDRESS_RANGE_MIRROR_VARIABLE_GUID \
+EFI_GUID( 0x7b9be2e0, 0xe28a, 0x4197, 0xad, 0x3e, 0x32, 0xf0, 0x62, 0xf9, 0x46, 0x2c)
 
 static inline int
 efi_guidcmp(efi_guid_t left, efi_guid_t right)
@@ -369,4 +369,16 @@ extern ssize_t make_linux_load_option(uint8_t **data, size_t *data_size,
 extern int variable_to_name(efi_variable_t *var, char *name);
 extern int var_name_to_path(const char *name, char *path);
 
+typedef struct {
+	uint8_t		mirror_version;
+	uint8_t		mirror_memory_below_4gb;
+	uint16_t	mirror_amount_above_4gb;
+	uint8_t		mirror_status;
+} __attribute__((packed)) ADDRESS_RANGE_MIRROR_VARIABLE_DATA;
+
+#define        MIRROR_VERSION  1
+
+#define ADDRESS_RANGE_MIRROR_VARIABLE_CURRENT "MirrorCurrent"
+#define ADDRESS_RANGE_MIRROR_VARIABLE_REQUEST "MirrorRequest"
+
 #endif /* EFI_H */
diff --git a/src/include/efibootmgr.h b/src/include/efibootmgr.h
index f5738b3..10a6701 100644
--- a/src/include/efibootmgr.h
+++ b/src/include/efibootmgr.h
@@ -40,6 +40,8 @@ typedef struct {
 	int bootnext;
 	int verbose;
 	int active;
+	int below4g;
+	int above4g;
 	int delete;
 	uint32_t acpi_hid;
 	uint32_t acpi_uid;
@@ -54,6 +56,8 @@ typedef struct {
 	unsigned int forcegpt:1;
 	unsigned int set_timeout:1;
 	unsigned int delete_timeout:1;
+	unsigned int set_mirror_lo:1;
+	unsigned int set_mirror_hi:1;
 	unsigned short int timeout;
 } efibootmgr_opt_t;
 
openSUSE Build Service is sponsored by