diff mbox

[Resend] uefivarinfo: add the utility to show the NVRam and variable info

Message ID 1370240794-7245-1-git-send-email-ivan.hu@canonical.com
State Rejected
Headers show

Commit Message

Ivan Hu June 3, 2013, 6:26 a.m. UTC
This utility get information by the uefi runtime service QueryVariableInfo
and GetVariable which provides the below infomations:
1. Maximum variable storage size
2. Remaining variable storage size
3. Maximum variable size
4. The variable number gets from the the memory location
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS
EFI_VARIABLE_RUNTIME_ACCESS attribute and used size.

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/Makefile.am                    |    3 +-
 src/uefi/uefivarinfo/uefivarinfo.c |  223 ++++++++++++++++++++++++++++++++++++
 2 files changed, 225 insertions(+), 1 deletion(-)
 create mode 100644 src/uefi/uefivarinfo/uefivarinfo.c

Comments

Colin Ian King June 3, 2013, 10 a.m. UTC | #1
On 03/06/13 07:26, Ivan Hu wrote:
> This utility get information by the uefi runtime service QueryVariableInfo
> and GetVariable which provides the below infomations:
> 1. Maximum variable storage size
> 2. Remaining variable storage size
> 3. Maximum variable size
> 4. The variable number gets from the the memory location
> EFI_VARIABLE_NON_VOLATILE |
> EFI_VARIABLE_BOOTSERVICE_ACCESS
> EFI_VARIABLE_RUNTIME_ACCESS attribute and used size.
> 
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>  src/Makefile.am                    |    3 +-
>  src/uefi/uefivarinfo/uefivarinfo.c |  223 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+), 1 deletion(-)
>  create mode 100644 src/uefi/uefivarinfo/uefivarinfo.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index b57e57c..1f52c47 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -82,7 +82,8 @@ fwts_SOURCES = main.c 				\
>  	uefi/uefirttime/uefirttime.c		\
>  	uefi/uefirtvariable/uefirtvariable.c	\
>  	uefi/uefirtmisc/uefirtmisc.c		\
> -	uefi/securebootcert/securebootcert.c
> +	uefi/securebootcert/securebootcert.c	\
> +	uefi/uefivarinfo/uefivarinfo.c	
^^
line above has trailing whitespace after uefivarinfo.c

>  
>  fwts_LDFLAGS = -ljson -lm
>  
> diff --git a/src/uefi/uefivarinfo/uefivarinfo.c b/src/uefi/uefivarinfo/uefivarinfo.c
> new file mode 100644
> index 0000000..27a94cc
> --- /dev/null
> +++ b/src/uefi/uefivarinfo/uefivarinfo.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright (C) 2013 Canonical
> + *
> + * This program 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 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program 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 this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include <inttypes.h>
> +#include <stdio.h>
> +#include <stddef.h>
> +#include <sys/ioctl.h>
> +#include <fcntl.h>
> +
> +#include "fwts.h"
> +#include "fwts_uefi.h"
> +#include "efi_runtime.h"
> +#include "fwts_efi_module.h"
> +
> +#define MAX_DATA_LENGTH		1024
> +
> +static int fd;
> +
> +static int uefivarinfo_init(fwts_framework *fw)
> +{
> +	if (fwts_firmware_detect() != FWTS_FIRMWARE_UEFI) {
> +		fwts_log_info(fw, "Cannot detect any UEFI firmware. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +
> +	if (fwts_lib_efi_runtime_load_module(fw) != FWTS_OK) {
> +		fwts_log_info(fw, "Cannot load efi_runtime module. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +
> +	fd = open("/dev/efi_runtime", O_RDONLY);
> +	if (fd == -1) {
> +		fwts_log_info(fw, "Cannot open efi_runtime driver. Aborted.");
> +		return FWTS_ABORTED;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int uefivarinfo_deinit(fwts_framework *fw)
> +{
> +	close(fd);
> +	fwts_lib_efi_runtime_unload_module(fw);
> +
> +	return FWTS_OK;
> +}
> +
> +static int do_checkvariables(
> +	fwts_framework *fw,
> +	uint64_t *usedvars,
> +	uint64_t *usedvarssize)
> +{
> +	long ioret;
> +	uint64_t status;
> +
> +	struct efi_getnextvariablename getnextvariablename;
> +	uint64_t variablenamesize = MAX_DATA_LENGTH;
> +	uint16_t variablename[MAX_DATA_LENGTH];
> +	EFI_GUID vendorguid;
> +
> +	uint32_t attributestest;
> +	struct efi_getvariable getvariable;
> +	getvariable.Attributes = &attributestest;
> +	getvariable.status = &status;
> +
> +	getnextvariablename.VariableNameSize = &variablenamesize;
> +	getnextvariablename.VariableName = variablename;
> +	getnextvariablename.VendorGuid = &vendorguid;
> +	getnextvariablename.status = &status;
> +
> +	*usedvars = 0;
> +	*usedvarssize = 0;
> +
> +	/*
> +	 * To start the search, need to pass a Null-terminated string
> +	 * in VariableName
> +	 */
> +	variablename[0] = '\0';
> +	while (true) {
> +		variablenamesize = MAX_DATA_LENGTH;
> +		ioret = ioctl(fd, EFI_RUNTIME_GET_NEXTVARIABLENAME, &getnextvariablename);
> +
> +		if (ioret == -1) {
> +
> +			/* no next variable was found*/
> +			if (*getnextvariablename.status == EFI_NOT_FOUND)
> +				break;
> +
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"UEFIRuntimeGetNextVariableName",
> +				"Failed to get next variable name with UEFI "
> +				"runtime service.");
> +			fwts_uefi_print_status_info(fw, status);
> +			return FWTS_ERROR;
> +		}
> +
> +		(*usedvars)++;
> +
> +		uint8_t data[MAX_DATA_LENGTH];
> +		uint64_t getdatasize = sizeof(data);

the fwts C style is somewhat like the kernel C style, so I prefer
declarations to be made at the start of the block rather than halfway
through if that's OK.

> +
> +		getvariable.VariableName = variablename;
> +		getvariable.VendorGuid = &vendorguid;
> +		getvariable.DataSize = &getdatasize;
> +		getvariable.Data = data;
> +
> +		ioret = ioctl(fd, EFI_RUNTIME_GET_VARIABLE, &getvariable);
> +		if (ioret == -1) {
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"UEFIRuntimeGetVariable",
> +				"Failed to get variable with UEFI "
> +				"runtime service.");
> +			fwts_uefi_print_status_info(fw, status);
> +			return FWTS_ERROR;
> +		}
> +
> +		(*usedvarssize) += getdatasize;
> +
> +	};
> +
> +	return FWTS_OK;
> +
> +}
> +
> +static int do_queryvariableinfo(
> +	uint64_t *status,
> +	uint64_t *maxvarstoragesize,
> +	uint64_t *remvarstoragesize,
> +	uint64_t *maxvariablesize)
> +{
> +	long ioret;
> +	struct efi_queryvariableinfo queryvariableinfo;
> +
> +	queryvariableinfo.Attributes = FWTS_UEFI_VAR_NON_VOLATILE |
> +					FWTS_UEFI_VAR_BOOTSERVICE_ACCESS |
> +					FWTS_UEFI_VAR_RUNTIME_ACCESS;
> +	queryvariableinfo.MaximumVariableStorageSize = maxvarstoragesize;
> +	queryvariableinfo.RemainingVariableStorageSize = remvarstoragesize;
> +	queryvariableinfo.MaximumVariableSize = maxvariablesize;
> +	queryvariableinfo.status = status;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_QUERY_VARIABLEINFO, &queryvariableinfo);
> +
> +	if (ioret == -1)
> +		return FWTS_ERROR;
> +
> +	return FWTS_OK;
> +}
> +
> +static int uefivarinfo_test1(fwts_framework *fw)
> +{
> +	uint64_t status;
> +	uint64_t remvarstoragesize;
> +	uint64_t maxvariablesize;
> +	uint64_t maxvarstoragesize;
> +
> +	uint64_t usedvars;
> +	uint64_t usedvarssize;
> +
> +	if (do_queryvariableinfo(&status, &maxvarstoragesize, &remvarstoragesize, &maxvariablesize) == FWTS_ERROR) {
> +		if (status == EFI_UNSUPPORTED) {
> +			fwts_skipped(fw,
> +				"Not support the QueryVariableInfo UEFI "
> +				"runtime interface: cannot test.");
> +			fwts_advice(fw,
> +				"Firmware also needs to check if the revision "
> +				"of system table is correct or not. Linux "
> +				"kernel returns EFI_UNSUPPORTED as well, if "
> +				"the FirmwareRevision of system table is less "
> +				"than EFI_2_00_SYSTEM_TABLE_REVISION.");
> +			return FWTS_SKIP;
> +		} else {
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"UEFIRuntimeQueryVariableInfo",
> +				"Failed to query variable info with UEFI "
> +				"runtime service.");
> +			fwts_uefi_print_status_info(fw, status);
> +			return FWTS_ERROR;
> +		}
> +	}
> +
> +	fwts_log_info_verbatum(fw, "UEFI NVRAM storage:");
> +	fwts_log_info_verbatum(fw, "  Maximum storage:       %8" PRIu64 " bytes", maxvarstoragesize);
> +	fwts_log_info_verbatum(fw, "  Remaining storage:     %8" PRIu64 " bytes", remvarstoragesize);
> +	fwts_log_info_verbatum(fw, "  Maximum variable size: %8" PRIu64 " bytes", maxvarstoragesize);
> +
> +	if (do_checkvariables(fw, &usedvars, &usedvarssize) != FWTS_ERROR) {
> +		fwts_log_info_verbatum(fw, "Currently used:");
> +		fwts_log_info_verbatum(fw, "  %" PRIu64 " variables, storage used: %" PRIu64 " bytes", usedvars, usedvarssize);
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test uefivarinfo_tests[] = {
> +	{ uefivarinfo_test1, "UEFI varaiable info query." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops uefivarinfo_ops = {
> +	.description = "UEFI varaiable info query.",
> +	.init        = uefivarinfo_init,
> +	.deinit      = uefivarinfo_deinit,
> +	.minor_tests = uefivarinfo_tests
> +};
> +
> +FWTS_REGISTER("uefivarinfo", &uefivarinfo_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS | FWTS_FLAG_ROOT_PRIV);
> 

I tested this out on a Lenovo x220i and I'm getting:

UEFI NVRAM storage:
  Maximum storage:          57324 bytes
  Remaining storage:        38074 bytes
  Maximum variable size:    57324 bytes
FAILED [HIGH] UEFIRuntimeGetVariable: Test 1, Failed to get variable
with UEFI
runtime service.
Return status: EFI_BUFFER_TOO_SMALL. The buffer is not large enough to
hold the
requested data. The required buffer size is returned in the appropriate
parameter when this error occurs.

(formatting broken by my mail client).  Is this expected? Is the 1K
variable size limit the cause of this error?

Colin
diff mbox

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index b57e57c..1f52c47 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,7 +82,8 @@  fwts_SOURCES = main.c 				\
 	uefi/uefirttime/uefirttime.c		\
 	uefi/uefirtvariable/uefirtvariable.c	\
 	uefi/uefirtmisc/uefirtmisc.c		\
-	uefi/securebootcert/securebootcert.c
+	uefi/securebootcert/securebootcert.c	\
+	uefi/uefivarinfo/uefivarinfo.c	
 
 fwts_LDFLAGS = -ljson -lm
 
diff --git a/src/uefi/uefivarinfo/uefivarinfo.c b/src/uefi/uefivarinfo/uefivarinfo.c
new file mode 100644
index 0000000..27a94cc
--- /dev/null
+++ b/src/uefi/uefivarinfo/uefivarinfo.c
@@ -0,0 +1,223 @@ 
+/*
+ * Copyright (C) 2013 Canonical
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include "fwts.h"
+#include "fwts_uefi.h"
+#include "efi_runtime.h"
+#include "fwts_efi_module.h"
+
+#define MAX_DATA_LENGTH		1024
+
+static int fd;
+
+static int uefivarinfo_init(fwts_framework *fw)
+{
+	if (fwts_firmware_detect() != FWTS_FIRMWARE_UEFI) {
+		fwts_log_info(fw, "Cannot detect any UEFI firmware. Aborted.");
+		return FWTS_ABORTED;
+	}
+
+	if (fwts_lib_efi_runtime_load_module(fw) != FWTS_OK) {
+		fwts_log_info(fw, "Cannot load efi_runtime module. Aborted.");
+		return FWTS_ABORTED;
+	}
+
+	fd = open("/dev/efi_runtime", O_RDONLY);
+	if (fd == -1) {
+		fwts_log_info(fw, "Cannot open efi_runtime driver. Aborted.");
+		return FWTS_ABORTED;
+	}
+
+	return FWTS_OK;
+}
+
+static int uefivarinfo_deinit(fwts_framework *fw)
+{
+	close(fd);
+	fwts_lib_efi_runtime_unload_module(fw);
+
+	return FWTS_OK;
+}
+
+static int do_checkvariables(
+	fwts_framework *fw,
+	uint64_t *usedvars,
+	uint64_t *usedvarssize)
+{
+	long ioret;
+	uint64_t status;
+
+	struct efi_getnextvariablename getnextvariablename;
+	uint64_t variablenamesize = MAX_DATA_LENGTH;
+	uint16_t variablename[MAX_DATA_LENGTH];
+	EFI_GUID vendorguid;
+
+	uint32_t attributestest;
+	struct efi_getvariable getvariable;
+	getvariable.Attributes = &attributestest;
+	getvariable.status = &status;
+
+	getnextvariablename.VariableNameSize = &variablenamesize;
+	getnextvariablename.VariableName = variablename;
+	getnextvariablename.VendorGuid = &vendorguid;
+	getnextvariablename.status = &status;
+
+	*usedvars = 0;
+	*usedvarssize = 0;
+
+	/*
+	 * To start the search, need to pass a Null-terminated string
+	 * in VariableName
+	 */
+	variablename[0] = '\0';
+	while (true) {
+		variablenamesize = MAX_DATA_LENGTH;
+		ioret = ioctl(fd, EFI_RUNTIME_GET_NEXTVARIABLENAME, &getnextvariablename);
+
+		if (ioret == -1) {
+
+			/* no next variable was found*/
+			if (*getnextvariablename.status == EFI_NOT_FOUND)
+				break;
+
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"UEFIRuntimeGetNextVariableName",
+				"Failed to get next variable name with UEFI "
+				"runtime service.");
+			fwts_uefi_print_status_info(fw, status);
+			return FWTS_ERROR;
+		}
+
+		(*usedvars)++;
+
+		uint8_t data[MAX_DATA_LENGTH];
+		uint64_t getdatasize = sizeof(data);
+
+		getvariable.VariableName = variablename;
+		getvariable.VendorGuid = &vendorguid;
+		getvariable.DataSize = &getdatasize;
+		getvariable.Data = data;
+
+		ioret = ioctl(fd, EFI_RUNTIME_GET_VARIABLE, &getvariable);
+		if (ioret == -1) {
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"UEFIRuntimeGetVariable",
+				"Failed to get variable with UEFI "
+				"runtime service.");
+			fwts_uefi_print_status_info(fw, status);
+			return FWTS_ERROR;
+		}
+
+		(*usedvarssize) += getdatasize;
+
+	};
+
+	return FWTS_OK;
+
+}
+
+static int do_queryvariableinfo(
+	uint64_t *status,
+	uint64_t *maxvarstoragesize,
+	uint64_t *remvarstoragesize,
+	uint64_t *maxvariablesize)
+{
+	long ioret;
+	struct efi_queryvariableinfo queryvariableinfo;
+
+	queryvariableinfo.Attributes = FWTS_UEFI_VAR_NON_VOLATILE |
+					FWTS_UEFI_VAR_BOOTSERVICE_ACCESS |
+					FWTS_UEFI_VAR_RUNTIME_ACCESS;
+	queryvariableinfo.MaximumVariableStorageSize = maxvarstoragesize;
+	queryvariableinfo.RemainingVariableStorageSize = remvarstoragesize;
+	queryvariableinfo.MaximumVariableSize = maxvariablesize;
+	queryvariableinfo.status = status;
+
+	ioret = ioctl(fd, EFI_RUNTIME_QUERY_VARIABLEINFO, &queryvariableinfo);
+
+	if (ioret == -1)
+		return FWTS_ERROR;
+
+	return FWTS_OK;
+}
+
+static int uefivarinfo_test1(fwts_framework *fw)
+{
+	uint64_t status;
+	uint64_t remvarstoragesize;
+	uint64_t maxvariablesize;
+	uint64_t maxvarstoragesize;
+
+	uint64_t usedvars;
+	uint64_t usedvarssize;
+
+	if (do_queryvariableinfo(&status, &maxvarstoragesize, &remvarstoragesize, &maxvariablesize) == FWTS_ERROR) {
+		if (status == EFI_UNSUPPORTED) {
+			fwts_skipped(fw,
+				"Not support the QueryVariableInfo UEFI "
+				"runtime interface: cannot test.");
+			fwts_advice(fw,
+				"Firmware also needs to check if the revision "
+				"of system table is correct or not. Linux "
+				"kernel returns EFI_UNSUPPORTED as well, if "
+				"the FirmwareRevision of system table is less "
+				"than EFI_2_00_SYSTEM_TABLE_REVISION.");
+			return FWTS_SKIP;
+		} else {
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"UEFIRuntimeQueryVariableInfo",
+				"Failed to query variable info with UEFI "
+				"runtime service.");
+			fwts_uefi_print_status_info(fw, status);
+			return FWTS_ERROR;
+		}
+	}
+
+	fwts_log_info_verbatum(fw, "UEFI NVRAM storage:");
+	fwts_log_info_verbatum(fw, "  Maximum storage:       %8" PRIu64 " bytes", maxvarstoragesize);
+	fwts_log_info_verbatum(fw, "  Remaining storage:     %8" PRIu64 " bytes", remvarstoragesize);
+	fwts_log_info_verbatum(fw, "  Maximum variable size: %8" PRIu64 " bytes", maxvarstoragesize);
+
+	if (do_checkvariables(fw, &usedvars, &usedvarssize) != FWTS_ERROR) {
+		fwts_log_info_verbatum(fw, "Currently used:");
+		fwts_log_info_verbatum(fw, "  %" PRIu64 " variables, storage used: %" PRIu64 " bytes", usedvars, usedvarssize);
+	}
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test uefivarinfo_tests[] = {
+	{ uefivarinfo_test1, "UEFI varaiable info query." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops uefivarinfo_ops = {
+	.description = "UEFI varaiable info query.",
+	.init        = uefivarinfo_init,
+	.deinit      = uefivarinfo_deinit,
+	.minor_tests = uefivarinfo_tests
+};
+
+FWTS_REGISTER("uefivarinfo", &uefivarinfo_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UTILS | FWTS_FLAG_ROOT_PRIV);