Merge tag 'efi-2023-01-rc5' of https://source.denx.de/u-boot/custodians/u-boot-efi

Pull request for efi-2023-01-rc5

UEFI:

* Improve parameter checking in efi_get_next_variable_name_mem()
* Fix a bugs in management of security database via the eficonfig command

Other:

* Allow sound command to play multiple sounds
diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 394ae67..ce7175a 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -1683,7 +1683,7 @@
 	u32 i;
 	u16 *bootorder;
 	efi_status_t ret;
-	u16 *var_name16 = NULL, *p;
+	u16 *var_name16 = NULL;
 	efi_uintn_t num, size, buf_size;
 	struct efimenu *efi_menu;
 	struct list_head *pos, *n;
@@ -1718,24 +1718,12 @@
 		int index;
 		efi_guid_t guid;
 
-		size = buf_size;
-		ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
 		if (ret == EFI_NOT_FOUND)
 			break;
-		if (ret == EFI_BUFFER_TOO_SMALL) {
-			buf_size = size;
-			p = realloc(var_name16, buf_size);
-			if (!p) {
-				free(var_name16);
-				return EFI_OUT_OF_RESOURCES;
-			}
-			var_name16 = p;
-			ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
-		}
-		if (ret != EFI_SUCCESS) {
-			free(var_name16);
-			return ret;
-		}
+		if (ret != EFI_SUCCESS)
+			goto out;
+
 		if (efi_varname_is_load_option(var_name16, &index)) {
 			/* If the index is included in the BootOrder, skip it */
 			if (search_bootorder(bootorder, num, index, NULL))
@@ -2026,7 +2014,7 @@
 	u32 i;
 	char *title;
 	efi_status_t ret;
-	u16 *var_name16 = NULL, *p;
+	u16 *var_name16 = NULL;
 	efi_uintn_t size, buf_size;
 
 	/* list the load option in the order of BootOrder variable */
@@ -2054,19 +2042,9 @@
 			break;
 
 		size = buf_size;
-		ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
+		ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
 		if (ret == EFI_NOT_FOUND)
 			break;
-		if (ret == EFI_BUFFER_TOO_SMALL) {
-			buf_size = size;
-			p = realloc(var_name16, buf_size);
-			if (!p) {
-				ret = EFI_OUT_OF_RESOURCES;
-				goto out;
-			}
-			var_name16 = p;
-			ret = efi_get_next_variable_name_int(&size, var_name16, &guid);
-		}
 		if (ret != EFI_SUCCESS)
 			goto out;
 
@@ -2332,14 +2310,15 @@
 efi_status_t eficonfig_delete_invalid_boot_option(struct eficonfig_media_boot_option *opt,
 						  efi_status_t count)
 {
-	u32 i;
 	efi_uintn_t size;
 	void *load_option;
+	u32 i, list_size = 0;
 	struct efi_load_option lo;
-	u16 *var_name16 = NULL, *p;
+	u16 *var_name16 = NULL;
 	u16 varname[] = u"Boot####";
 	efi_status_t ret = EFI_SUCCESS;
-	efi_uintn_t varname_size, buf_size;
+	u16 *delete_index_list = NULL, *p;
+	efi_uintn_t buf_size;
 
 	buf_size = 128;
 	var_name16 = malloc(buf_size);
@@ -2352,24 +2331,18 @@
 		efi_guid_t guid;
 		efi_uintn_t tmp;
 
-		varname_size = buf_size;
-		ret = efi_get_next_variable_name_int(&varname_size, var_name16, &guid);
-		if (ret == EFI_NOT_FOUND)
+		ret = efi_next_variable_name(&buf_size, &var_name16, &guid);
+		if (ret == EFI_NOT_FOUND) {
+			/*
+			 * EFI_NOT_FOUND indicates we retrieved all EFI variables.
+			 * This should be treated as success.
+			 */
+			ret = EFI_SUCCESS;
 			break;
-		if (ret == EFI_BUFFER_TOO_SMALL) {
-			buf_size = varname_size;
-			p = realloc(var_name16, buf_size);
-			if (!p) {
-				free(var_name16);
-				return EFI_OUT_OF_RESOURCES;
-			}
-			var_name16 = p;
-			ret = efi_get_next_variable_name_int(&varname_size, var_name16, &guid);
 		}
-		if (ret != EFI_SUCCESS) {
-			free(var_name16);
-			return ret;
-		}
+		if (ret != EFI_SUCCESS)
+			goto out;
+
 		if (!efi_varname_is_load_option(var_name16, &index))
 			continue;
 
@@ -2383,30 +2356,47 @@
 		if (ret != EFI_SUCCESS)
 			goto next;
 
-		if (size >= sizeof(efi_guid_bootmenu_auto_generated)) {
-			if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
-				for (i = 0; i < count; i++) {
-					if (opt[i].size == tmp &&
-					    memcmp(opt[i].lo, load_option, tmp) == 0) {
-						opt[i].exist = true;
-						break;
-					}
+		if (size >= sizeof(efi_guid_bootmenu_auto_generated) &&
+		    !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) {
+			for (i = 0; i < count; i++) {
+				if (opt[i].size == tmp &&
+				    memcmp(opt[i].lo, load_option, tmp) == 0) {
+					opt[i].exist = true;
+					break;
 				}
+			}
 
-				if (i == count) {
-					ret = delete_boot_option(i);
-					if (ret != EFI_SUCCESS) {
-						free(load_option);
-						goto out;
-					}
+			/*
+			 * The entire list of variables must be retrieved by
+			 * efi_get_next_variable_name_int() before deleting the invalid
+			 * boot option, just save the index here.
+			 */
+			if (i == count) {
+				p = realloc(delete_index_list, sizeof(u32) *
+					    (list_size + 1));
+				if (!p) {
+					ret = EFI_OUT_OF_RESOURCES;
+					goto out;
 				}
+				delete_index_list = p;
+				delete_index_list[list_size++] = index;
 			}
 		}
 next:
 		free(load_option);
 	}
 
+	/* delete all invalid boot options */
+	for (i = 0; i < list_size; i++) {
+		ret = delete_boot_option(delete_index_list[i]);
+		if (ret != EFI_SUCCESS)
+			goto out;
+	}
+
 out:
+	free(var_name16);
+	free(delete_index_list);
+
 	return ret;
 }
 
diff --git a/cmd/eficonfig_sbkey.c b/cmd/eficonfig_sbkey.c
index 6e0bebf..ed39aab 100644
--- a/cmd/eficonfig_sbkey.c
+++ b/cmd/eficonfig_sbkey.c
@@ -73,6 +73,28 @@
 }
 
 /**
+ * file_is_null_key() - check the file is an authenticated and signed null key
+ *
+ * @auth:	pointer to the file
+ * @size:	file size
+ * @null_key:	pointer to store the result
+ * Return:	status code
+ */
+static efi_status_t file_is_null_key(struct efi_variable_authentication_2 *auth,
+				     efi_uintn_t size, bool *null_key)
+{
+	efi_uintn_t auth_size =
+		sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength;
+
+	if (size < auth_size)
+		return EFI_INVALID_PARAMETER;
+
+	*null_key = (size == auth_size);
+
+	return EFI_SUCCESS;
+}
+
+/**
  * eficonfig_process_enroll_key() - enroll key into signature database
  *
  * @data:	pointer to the data for each entry
@@ -84,6 +106,7 @@
 	char *buf = NULL;
 	efi_uintn_t size;
 	efi_status_t ret;
+	bool null_key = false;
 	struct efi_file_handle *f = NULL;
 	struct efi_device_path *full_dp = NULL;
 	struct eficonfig_select_file_info file_info;
@@ -149,13 +172,24 @@
 		goto out;
 	}
 
+	ret = file_is_null_key((struct efi_variable_authentication_2 *)buf,
+			       size, &null_key);
+	if (ret != EFI_SUCCESS) {
+		eficonfig_print_msg("ERROR! Invalid file format.");
+		goto out;
+	}
+
 	attr = EFI_VARIABLE_NON_VOLATILE |
 	       EFI_VARIABLE_BOOTSERVICE_ACCESS |
 	       EFI_VARIABLE_RUNTIME_ACCESS |
 	       EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
 
-	/* PK can enroll only one certificate */
-	if (u16_strcmp(data, u"PK")) {
+	/*
+	 * PK can enroll only one certificate.
+	 * The signed null key is used to clear KEK, db and dbx.
+	 * EFI_VARIABLE_APPEND_WRITE attribute must not be set in these cases.
+	 */
+	if (u16_strcmp(data, u"PK") && !null_key) {
 		efi_uintn_t db_size = 0;
 
 		/* check the variable exists. If exists, add APPEND_WRITE attribute */
diff --git a/cmd/sound.c b/cmd/sound.c
index 20ac3f7..cef71be 100644
--- a/cmd/sound.c
+++ b/cmd/sound.c
@@ -39,26 +39,39 @@
 	int ret = 0;
 	int msec = 1000;
 	int freq = 400;
-
-	if (argc > 1)
-		msec = dectoul(argv[1], NULL);
-	if (argc > 2)
-		freq = dectoul(argv[2], NULL);
+	bool first = true;
 
 	ret = uclass_first_device_err(UCLASS_SOUND, &dev);
-	if (!ret)
+	if (ret)
+		goto err;
+	--argc;
+	++argv;
+	while (argc || first) {
+		first = false;
+		if (argc && *argv[0] != '-') {
+			msec = dectoul(argv[0], NULL);
+			--argc;
+			++argv;
+		}
+		if (argc && *argv[0] != '-') {
+			freq = dectoul(argv[0], NULL);
+			--argc;
+			++argv;
+		}
 		ret = sound_beep(dev, msec, freq);
-	if (ret) {
-		printf("Sound device failed to play (err=%d)\n", ret);
-		return CMD_RET_FAILURE;
+		if (ret)
+			goto err;
 	}
-
 	return 0;
+
+err:
+	printf("Sound device failed to play (err=%d)\n", ret);
+	return CMD_RET_FAILURE;
 }
 
 static struct cmd_tbl cmd_sound_sub[] = {
 	U_BOOT_CMD_MKENT(init, 0, 1, do_init, "", ""),
-	U_BOOT_CMD_MKENT(play, 2, 1, do_play, "", ""),
+	U_BOOT_CMD_MKENT(play, INT_MAX, 1, do_play, "", ""),
 };
 
 /* process sound command */
@@ -83,8 +96,10 @@
 }
 
 U_BOOT_CMD(
-	sound, 4, 1, do_sound,
+	sound, INT_MAX, 1, do_sound,
 	"sound sub-system",
 	"init - initialise the sound driver\n"
-	"sound play [len [freq]] - play a sound for len ms at freq Hz\n"
+	"sound play [[[-q|-s] len [freq]] ...] - play sounds\n"
+	"  len - duration in ms\n"
+	"  freq - frequency in Hz\n"
 );
diff --git a/doc/usage/cmd/printenv.rst b/doc/usage/cmd/printenv.rst
index 9cb20f6..d4184fd 100644
--- a/doc/usage/cmd/printenv.rst
+++ b/doc/usage/cmd/printenv.rst
@@ -78,7 +78,7 @@
     =>
 
 Configuration
-=============
+-------------
 
 UEFI variables are only supported if CONFIG_CMD_NVEDIT_EFI=y. The value of UEFI
 variables can only be displayed if CONFIG_HEXDUMP=y.
diff --git a/doc/usage/cmd/sound.rst b/doc/usage/cmd/sound.rst
index d3fac24..2cfe9b7 100644
--- a/doc/usage/cmd/sound.rst
+++ b/doc/usage/cmd/sound.rst
@@ -10,12 +10,12 @@
 ::
 
     sound init
-    sound play [len [freq]]
+    sound play [[len freq] ...] [len [freq]]
 
 Description
 -----------
 
-The *sound* command is used to play a beep sound.
+The *sound* command is used to play one or multiple beep sounds.
 
 sound init
     initializes the sound driver.
@@ -30,6 +30,25 @@
 freq
     frequency of the sound in Hz, defaults to 400 Hz
 
+Examples
+--------
+
+Beep at 400 Hz for 1000 ms::
+
+    sound play
+
+Beep at 400 Hz for 600 ms::
+
+    sound play 600
+
+Beep at 500 Hz for 600 ms::
+
+    sound play 600 500
+
+Play melody::
+
+    sound play 500 1047 500 880 500 0 500 1047 500 880 500 0 500 784 500 698 500 784 1000 698
+
 Configuration
 -------------
 
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 0899e29..6991768 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -708,6 +708,8 @@
 int efi_link_dev(efi_handle_t handle, struct udevice *dev);
 int efi_unlink_dev(efi_handle_t handle);
 bool efi_varname_is_load_option(u16 *var_name16, int *index);
+efi_status_t efi_next_variable_name(efi_uintn_t *size, u16 **buf,
+				    efi_guid_t *guid);
 
 /**
  * efi_size_in_pages() - convert size in bytes to size in pages
diff --git a/include/efi_variable.h b/include/efi_variable.h
index 03a3ecb..805e6c5 100644
--- a/include/efi_variable.h
+++ b/include/efi_variable.h
@@ -268,7 +268,8 @@
  * efi_get_next_variable_name_mem() - Runtime common code across efi variable
  *                                    implementations for GetNextVariable()
  *                                    from the cached memory copy
- * @variable_name_size:	size of variable_name buffer in byte
+ *
+ * @variable_name_size:	size of variable_name buffer in bytes
  * @variable_name:	name of uefi variable's name in u16
  * @vendor:		vendor's guid
  *
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
index 788cb9f..1f4ab2b 100644
--- a/lib/efi_loader/efi_helper.c
+++ b/lib/efi_loader/efi_helper.c
@@ -223,3 +223,37 @@
 
 	return false;
 }
+
+/**
+ * efi_next_variable_name() - get next variable name
+ *
+ * This function is a wrapper of efi_get_next_variable_name_int().
+ * If efi_get_next_variable_name_int() returns EFI_BUFFER_TOO_SMALL,
+ * @size and @buf are updated by new buffer size and realloced buffer.
+ *
+ * @size:	pointer to the buffer size
+ * @buf:	pointer to the buffer
+ * @guid:	pointer to the guid
+ * Return:	status code
+ */
+efi_status_t efi_next_variable_name(efi_uintn_t *size, u16 **buf, efi_guid_t *guid)
+{
+	u16 *p;
+	efi_status_t ret;
+	efi_uintn_t buf_size = *size;
+
+	ret = efi_get_next_variable_name_int(&buf_size, *buf, guid);
+	if (ret == EFI_NOT_FOUND)
+		return ret;
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		p = realloc(*buf, buf_size);
+		if (!p)
+			return EFI_OUT_OF_RESOURCES;
+
+		*buf = p;
+		*size = buf_size;
+		ret = efi_get_next_variable_name_int(&buf_size, *buf, guid);
+	}
+
+	return ret;
+}
diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c
index 13909b1..0bac594 100644
--- a/lib/efi_loader/efi_var_mem.c
+++ b/lib/efi_loader/efi_var_mem.c
@@ -315,14 +315,14 @@
 			       u16 *variable_name, efi_guid_t *vendor)
 {
 	struct efi_var_entry *var;
-	efi_uintn_t old_size;
+	efi_uintn_t len, old_size;
 	u16 *pdata;
 
 	if (!variable_name_size || !variable_name || !vendor)
 		return EFI_INVALID_PARAMETER;
 
-	if (u16_strnlen(variable_name, *variable_name_size) ==
-	    *variable_name_size)
+	len = *variable_name_size >> 1;
+	if (u16_strnlen(variable_name, len) == len)
 		return EFI_INVALID_PARAMETER;
 
 	if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
diff --git a/lib/efi_selftest/efi_selftest_variables.c b/lib/efi_selftest/efi_selftest_variables.c
index dc1d5c8..c7a3fdb 100644
--- a/lib/efi_selftest/efi_selftest_variables.c
+++ b/lib/efi_selftest/efi_selftest_variables.c
@@ -141,6 +141,41 @@
 		return EFI_ST_FAILURE;
 	}
 	/* Enumerate variables */
+
+	ret = runtime->get_next_variable_name(NULL, u"efi_st_var1", &guid);
+	if (ret != EFI_INVALID_PARAMETER) {
+		efi_st_error("GetNextVariableName missing parameter check\n");
+		return EFI_ST_FAILURE;
+	}
+
+	len = 24;
+	ret = runtime->get_next_variable_name(&len, NULL, &guid);
+	if (ret != EFI_INVALID_PARAMETER) {
+		efi_st_error("GetNextVariableName missing parameter check\n");
+		return EFI_ST_FAILURE;
+	}
+
+	len = 24;
+	ret = runtime->get_next_variable_name(&len, u"efi_st_var1", NULL);
+	if (ret != EFI_INVALID_PARAMETER) {
+		efi_st_error("GetNextVariableName missing parameter check\n");
+		return EFI_ST_FAILURE;
+	}
+
+	len = 1;
+	ret = runtime->get_next_variable_name(&len, u"", &guid);
+	if (ret != EFI_INVALID_PARAMETER) {
+		efi_st_error("GetNextVariableName missing parameter check\n");
+		return EFI_ST_FAILURE;
+	}
+
+	len = 16;
+	ret = runtime->get_next_variable_name(&len, u"efi_st_var1", &guid);
+	if (ret != EFI_INVALID_PARAMETER) {
+		efi_st_error("GetNextVariableName missing parameter check\n");
+		return EFI_ST_FAILURE;
+	}
+
 	boottime->set_mem(&guid, 16, 0);
 	*varname = 0;
 	flag = 0;
diff --git a/test/unicode_ut.c b/test/unicode_ut.c
index d104bd5..3547aef 100644
--- a/test/unicode_ut.c
+++ b/test/unicode_ut.c
@@ -67,6 +67,17 @@
 }
 UNICODE_TEST(unicode_test_u16_strlen);
 
+static int unicode_test_u16_strnlen(struct unit_test_state *uts)
+{
+	ut_asserteq(0, u16_strnlen(c1, 0));
+	ut_asserteq(4, u16_strnlen(c1, 4));
+	ut_asserteq(6, u16_strnlen(c1, 6));
+	ut_asserteq(6, u16_strnlen(c1, 7));
+
+	return 0;
+}
+UNICODE_TEST(unicode_test_u16_strnlen);
+
 static int unicode_test_u16_strdup(struct unit_test_state *uts)
 {
 	u16 *copy = u16_strdup(c4);