Patchwork [2/2,RESEND] uefirttime: add fwts tests for the UEFI get/set time runtime services

login
register
mail settings
Submitter Ivan Hu
Date Oct. 18, 2012, 3:52 p.m.
Message ID <1350575548-28548-1-git-send-email-ivan.hu@canonical.com>
Download mbox | patch
Permalink /patch/192369/
State Accepted
Headers show

Comments

Ivan Hu - Oct. 18, 2012, 3:52 p.m.
Add the set and get time tests of the UEFI runtime service interfaces
which test via efi_runtime driver.

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/Makefile.am                  |    5 +-
 src/lib/include/fwts_uefi.h      |    7 +
 src/uefi/uefirttime/uefirttime.c |  328 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 338 insertions(+), 2 deletions(-)
 create mode 100644 src/uefi/uefirttime/uefirttime.c
Colin King - Oct. 18, 2012, 4:20 p.m.
Thanks Ivan

On 18/10/12 16:52, Ivan Hu wrote:
> Add the set and get time tests of the UEFI runtime service interfaces
> which test via efi_runtime driver.
>
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>   src/Makefile.am                  |    5 +-
>   src/lib/include/fwts_uefi.h      |    7 +
>   src/uefi/uefirttime/uefirttime.c |  328 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 338 insertions(+), 2 deletions(-)
>   create mode 100644 src/uefi/uefirttime/uefirttime.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 6054eb3..b7adc20 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -6,7 +6,7 @@
>   # but libfwts.so depends on libraries produced by acpica/source/compiler.
>   SUBDIRS = acpica/source/compiler lib acpica
>
> -AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -Wall -Werror
> +AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -I$(top_srcdir)/efi_runtime -Wall -Werror
>
>   bin_PROGRAMS = fwts
>   fwts_SOURCES = main.c \
> @@ -66,7 +66,8 @@ fwts_SOURCES = main.c \
>   	kernel/version/version.c \
>   	kernel/oops/oops.c \
>   	uefi/csm/csm.c \
> -	uefi/uefidump/uefidump.c
> +	uefi/uefidump/uefidump.c \
> +	uefi/uefirttime/uefirttime.c
>
>   fwts_LDFLAGS = -ljson -lm
>
> diff --git a/src/lib/include/fwts_uefi.h b/src/lib/include/fwts_uefi.h
> index f45027d..73cd773 100644
> --- a/src/lib/include/fwts_uefi.h
> +++ b/src/lib/include/fwts_uefi.h
> @@ -41,6 +41,13 @@ enum {
>   	FWTS_UEFI_VAR_RUNTIME_ACCESS =		0x00000004
>   };
>
> +enum {
> +	FWTS_UEFI_TIME_ADJUST_DAYLIGHT =	0x01,
> +	FWTS_UEFI_TIME_IN_DAYLIGHT = 		0x02
> +};
> +
> +#define FWTS_UEFI_UNSPECIFIED_TIMEZONE 0x07FF
> +
>   #if 0
>   typedef struct {
>   	char *description;
> diff --git a/src/uefi/uefirttime/uefirttime.c b/src/uefi/uefirttime/uefirttime.c
> new file mode 100644
> index 0000000..686cf2d
> --- /dev/null
> +++ b/src/uefi/uefirttime/uefirttime.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright (C) 2012 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 IS_LEAP(year) \
> +		((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
> +
> +static int fd;
> +static uint32_t dayofmonth[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
> +
> +static bool dayvalid(EFI_TIME *Time)
> +{
> +	if (Time->Day < 1)
> +		return false;
> +
> +	if (Time->Day > dayofmonth[Time->Month - 1])
> +		return false;
> +
> +	/* check month 2 */
> +	if (Time->Month == 2 && (!IS_LEAP(Time->Year) && Time->Day > 28))
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool checktimefields(fwts_framework *fw, EFI_TIME *Time)
> +{
> +	if (Time->Year < 1900 || Time->Year > 9999) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadYear",
> +			"Time returned an invalid year %" PRIu16
> +			", should be between 1900 and 9999.",
> +			Time->Year);
> +		return false;
> +	}
> +
> +	if (Time->Month < 1 || Time->Month > 12) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadMonth",
> +			"Time returned an invalid month %" PRIu8
> +			", should be between 1 and 12.",
> +			Time->Month);
> +		return false;
> +	}
> +
> +	if (!dayvalid(Time)) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadDay",
> +			"Time returned an invalid day %" PRIu8
> +			", should be between 1 and 28-31 depends on month/year.",
> +			Time->Day);
> +		return false;
> +	}
> +
> +	if (Time->Hour > 23) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadHour",
> +			"Time returned an invalid hour %" PRIu8
> +			", should be between 0 and 23.",
> +			Time->Hour);
> +		return false;
> +	}
> +
> +	if (Time->Minute > 59) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadMinute",
> +			"Time returned an invalid minute %" PRIu8
> +			", should be between 0 and 59.",
> +			Time->Minute);
> +		return false;
> +	}
> +
> +	if (Time->Second > 59) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadSecond",
> +			"Time returned an invalid second %" PRIu8
> +			", should be between 0 and 59.",
> +			Time->Second);
> +		return false;
> +	}
> +
> +	if (Time->Nanosecond > 999999999) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadNanosecond",
> +			"Time returned an invalid nanosecond %" PRIu32
> +			", should be between 0 and 999999999.",
> +			Time->Nanosecond);
> +		return false;
> +	}
> +
> +	if (!(Time->TimeZone == FWTS_UEFI_UNSPECIFIED_TIMEZONE ||
> +		(Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadTimezone",
> +			"Time returned an invalid timezone %" PRId16
> +			", should be between -1440 and 1440 or 2047.",
> +			Time->TimeZone);
> +		return false;
> +	}
> +
> +	if (Time->Daylight & (~(FWTS_UEFI_TIME_ADJUST_DAYLIGHT |
> +				FWTS_UEFI_TIME_IN_DAYLIGHT))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadDaylight",
> +			"Time returned an invalid daylight %" PRIu8
> +			", all other bits except UEFI_TIME_IN_DAYLIGHT and "
> +			"UEFI_TIME_ADJUST_DAYLIGHT must be zero.",
> +			Time->Daylight);
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static int uefirttime_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 uefirttime_deinit(fwts_framework *fw)
> +{
> +
> +	close(fd);
> +
> +	return FWTS_OK;
> +}
> +
> +
> +static int uefirttime_test1(fwts_framework *fw)
> +{
> +	long ioret;
> +	struct efi_gettime gettime;
> +	EFI_TIME efi_time;
> +
> +	EFI_TIME_CAPABILITIES efi_time_cap;
> +	uint64_t status;
> +
> +	gettime.Capabilities = &efi_time_cap;
> +	gettime.Time = &efi_time;
> +	gettime.status = &status;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!checktimefields(fw, gettime.Time))
> +		return FWTS_ERROR;
> +
> +	fwts_passed(fw, "UEFI runtime service GetTime interface test passed.");
> +
> +	return FWTS_OK;
> +}
> +
> +static int uefirttime_test2(fwts_framework *fw)
> +{
> +
> +	long ioret;
> +	struct efi_settime settime;
> +	uint64_t status;
> +	struct efi_gettime gettime;
> +
> +	EFI_TIME oldtime;
> +	EFI_TIME newtime;
> +	EFI_TIME time;
> +	EFI_TIME_CAPABILITIES efi_time_cap;
> +
> +	gettime.Capabilities = &efi_time_cap;
> +	gettime.Time = &oldtime;
> +	gettime.status = &status;
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* refer to UEFI SCT 2.3 test items */
> +	/* change year */
> +	time = oldtime;
> +	if (time.Year != 2012)
> +		time.Year = 2012;
> +	else
> +		time.Year = 2016;
> +
> +	/* change month */
> +	if (time.Month != 1)
> +		time.Month = 1;
> +	else
> +		time.Month = 12;
> +
> +	/* Change daylight */
> +	if (time.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)
> +		time.Daylight &= ~FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
> +	else
> +		time.Daylight |= FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
> +
> +	/* Change time zone */
> +	if (time.TimeZone != 0)
> +		time.TimeZone = 0;
> +	else
> +		time.TimeZone = 1;
> +
> +	settime.Time = &time;
> +	settime.status = &status;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
> +			"Failed to set time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	sleep(1);
> +
> +	gettime.Time = &newtime;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Year == 2012) && (newtime.Year == 2016)) &&
> +	    !((oldtime.Year != 2012) && (newtime.Year == 2012))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeYear",
> +			"Failed to set year with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Month == 1) && (newtime.Month == 12)) &&
> +	    !((oldtime.Month != 1) && (newtime.Month == 1))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeMonth",
> +			"Failed to set month with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT) &&
> +	    (!(newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) &&
> +	    !((!(oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)) &&
> +	    (newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeDaylight",
> +			"Failed to set daylight with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.TimeZone == 0) && (newtime.TimeZone == 1)) &&
> +	    !((oldtime.TimeZone != 0) && (newtime.TimeZone == 0))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeTimezone",
> +			"Failed to set timezone with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* restore the previous time. */
> +	settime.Time = &oldtime;
> +	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
> +			"Failed to set time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_passed(fw, "UEFI runtime service SetTime interface test passed.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test uefirttime_tests[] = {
> +	{ uefirttime_test1, "Test UEFI RT service get time interface." },
> +	{ uefirttime_test2, "Test UEFI RT service set time interface." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops uefirttime_ops = {
> +	.description = "UEFI Runtime service time interface tests.",
> +	.init        = uefirttime_init,
> +	.deinit      = uefirttime_deinit,
> +	.minor_tests = uefirttime_tests
> +};
> +
> +FWTS_REGISTER(uefirttime, &uefirttime_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV);
>

Let's go with this. Nice one.

Acked-by: Colin Ian King <colin.king@canonical.com>
Alex Hung - Oct. 24, 2012, 9:18 a.m.
On 10/18/2012 11:52 PM, Ivan Hu wrote:
> Add the set and get time tests of the UEFI runtime service interfaces
> which test via efi_runtime driver.
>
> Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
> ---
>   src/Makefile.am                  |    5 +-
>   src/lib/include/fwts_uefi.h      |    7 +
>   src/uefi/uefirttime/uefirttime.c |  328 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 338 insertions(+), 2 deletions(-)
>   create mode 100644 src/uefi/uefirttime/uefirttime.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 6054eb3..b7adc20 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -6,7 +6,7 @@
>   # but libfwts.so depends on libraries produced by acpica/source/compiler.
>   SUBDIRS = acpica/source/compiler lib acpica
>
> -AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -Wall -Werror
> +AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -I$(top_srcdir)/efi_runtime -Wall -Werror
>
>   bin_PROGRAMS = fwts
>   fwts_SOURCES = main.c \
> @@ -66,7 +66,8 @@ fwts_SOURCES = main.c \
>   	kernel/version/version.c \
>   	kernel/oops/oops.c \
>   	uefi/csm/csm.c \
> -	uefi/uefidump/uefidump.c
> +	uefi/uefidump/uefidump.c \
> +	uefi/uefirttime/uefirttime.c
>
>   fwts_LDFLAGS = -ljson -lm
>
> diff --git a/src/lib/include/fwts_uefi.h b/src/lib/include/fwts_uefi.h
> index f45027d..73cd773 100644
> --- a/src/lib/include/fwts_uefi.h
> +++ b/src/lib/include/fwts_uefi.h
> @@ -41,6 +41,13 @@ enum {
>   	FWTS_UEFI_VAR_RUNTIME_ACCESS =		0x00000004
>   };
>
> +enum {
> +	FWTS_UEFI_TIME_ADJUST_DAYLIGHT =	0x01,
> +	FWTS_UEFI_TIME_IN_DAYLIGHT = 		0x02
> +};
> +
> +#define FWTS_UEFI_UNSPECIFIED_TIMEZONE 0x07FF
> +
>   #if 0
>   typedef struct {
>   	char *description;
> diff --git a/src/uefi/uefirttime/uefirttime.c b/src/uefi/uefirttime/uefirttime.c
> new file mode 100644
> index 0000000..686cf2d
> --- /dev/null
> +++ b/src/uefi/uefirttime/uefirttime.c
> @@ -0,0 +1,328 @@
> +/*
> + * Copyright (C) 2012 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 IS_LEAP(year) \
> +		((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
> +
> +static int fd;
> +static uint32_t dayofmonth[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
> +
> +static bool dayvalid(EFI_TIME *Time)
> +{
> +	if (Time->Day < 1)
> +		return false;
> +
> +	if (Time->Day > dayofmonth[Time->Month - 1])
> +		return false;
> +
> +	/* check month 2 */
> +	if (Time->Month == 2 && (!IS_LEAP(Time->Year) && Time->Day > 28))
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool checktimefields(fwts_framework *fw, EFI_TIME *Time)
> +{
> +	if (Time->Year < 1900 || Time->Year > 9999) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadYear",
> +			"Time returned an invalid year %" PRIu16
> +			", should be between 1900 and 9999.",
> +			Time->Year);
> +		return false;
> +	}
> +
> +	if (Time->Month < 1 || Time->Month > 12) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadMonth",
> +			"Time returned an invalid month %" PRIu8
> +			", should be between 1 and 12.",
> +			Time->Month);
> +		return false;
> +	}
> +
> +	if (!dayvalid(Time)) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadDay",
> +			"Time returned an invalid day %" PRIu8
> +			", should be between 1 and 28-31 depends on month/year.",
> +			Time->Day);
> +		return false;
> +	}
> +
> +	if (Time->Hour > 23) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadHour",
> +			"Time returned an invalid hour %" PRIu8
> +			", should be between 0 and 23.",
> +			Time->Hour);
> +		return false;
> +	}
> +
> +	if (Time->Minute > 59) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadMinute",
> +			"Time returned an invalid minute %" PRIu8
> +			", should be between 0 and 59.",
> +			Time->Minute);
> +		return false;
> +	}
> +
> +	if (Time->Second > 59) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadSecond",
> +			"Time returned an invalid second %" PRIu8
> +			", should be between 0 and 59.",
> +			Time->Second);
> +		return false;
> +	}
> +
> +	if (Time->Nanosecond > 999999999) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadNanosecond",
> +			"Time returned an invalid nanosecond %" PRIu32
> +			", should be between 0 and 999999999.",
> +			Time->Nanosecond);
> +		return false;
> +	}
> +
> +	if (!(Time->TimeZone == FWTS_UEFI_UNSPECIFIED_TIMEZONE ||
> +		(Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadTimezone",
> +			"Time returned an invalid timezone %" PRId16
> +			", should be between -1440 and 1440 or 2047.",
> +			Time->TimeZone);
> +		return false;
> +	}
> +
> +	if (Time->Daylight & (~(FWTS_UEFI_TIME_ADJUST_DAYLIGHT |
> +				FWTS_UEFI_TIME_IN_DAYLIGHT))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"UEFIRuntimeTimeFieldBadDaylight",
> +			"Time returned an invalid daylight %" PRIu8
> +			", all other bits except UEFI_TIME_IN_DAYLIGHT and "
> +			"UEFI_TIME_ADJUST_DAYLIGHT must be zero.",
> +			Time->Daylight);
> +		return false;
> +	}
> +	return true;
> +}
> +
> +static int uefirttime_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 uefirttime_deinit(fwts_framework *fw)
> +{
> +
> +	close(fd);
> +
> +	return FWTS_OK;
> +}
> +
> +
> +static int uefirttime_test1(fwts_framework *fw)
> +{
> +	long ioret;
> +	struct efi_gettime gettime;
> +	EFI_TIME efi_time;
> +
> +	EFI_TIME_CAPABILITIES efi_time_cap;
> +	uint64_t status;
> +
> +	gettime.Capabilities = &efi_time_cap;
> +	gettime.Time = &efi_time;
> +	gettime.status = &status;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!checktimefields(fw, gettime.Time))
> +		return FWTS_ERROR;
> +
> +	fwts_passed(fw, "UEFI runtime service GetTime interface test passed.");
> +
> +	return FWTS_OK;
> +}
> +
> +static int uefirttime_test2(fwts_framework *fw)
> +{
> +
> +	long ioret;
> +	struct efi_settime settime;
> +	uint64_t status;
> +	struct efi_gettime gettime;
> +
> +	EFI_TIME oldtime;
> +	EFI_TIME newtime;
> +	EFI_TIME time;
> +	EFI_TIME_CAPABILITIES efi_time_cap;
> +
> +	gettime.Capabilities = &efi_time_cap;
> +	gettime.Time = &oldtime;
> +	gettime.status = &status;
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* refer to UEFI SCT 2.3 test items */
> +	/* change year */
> +	time = oldtime;
> +	if (time.Year != 2012)
> +		time.Year = 2012;
> +	else
> +		time.Year = 2016;
> +
> +	/* change month */
> +	if (time.Month != 1)
> +		time.Month = 1;
> +	else
> +		time.Month = 12;
> +
> +	/* Change daylight */
> +	if (time.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)
> +		time.Daylight &= ~FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
> +	else
> +		time.Daylight |= FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
> +
> +	/* Change time zone */
> +	if (time.TimeZone != 0)
> +		time.TimeZone = 0;
> +	else
> +		time.TimeZone = 1;
> +
> +	settime.Time = &time;
> +	settime.status = &status;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
> +			"Failed to set time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	sleep(1);
> +
> +	gettime.Time = &newtime;
> +
> +	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
> +
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
> +			"Failed to get time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Year == 2012) && (newtime.Year == 2016)) &&
> +	    !((oldtime.Year != 2012) && (newtime.Year == 2012))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeYear",
> +			"Failed to set year with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Month == 1) && (newtime.Month == 12)) &&
> +	    !((oldtime.Month != 1) && (newtime.Month == 1))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeMonth",
> +			"Failed to set month with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT) &&
> +	    (!(newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) &&
> +	    !((!(oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)) &&
> +	    (newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeDaylight",
> +			"Failed to set daylight with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (!((oldtime.TimeZone == 0) && (newtime.TimeZone == 1)) &&
> +	    !((oldtime.TimeZone != 0) && (newtime.TimeZone == 0))) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeTimezone",
> +			"Failed to set timezone with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* restore the previous time. */
> +	settime.Time = &oldtime;
> +	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
> +	if (ioret == -1) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
> +			"Failed to set time with UEFI runtime service.");
> +		return FWTS_ERROR;
> +	}
> +
> +	fwts_passed(fw, "UEFI runtime service SetTime interface test passed.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test uefirttime_tests[] = {
> +	{ uefirttime_test1, "Test UEFI RT service get time interface." },
> +	{ uefirttime_test2, "Test UEFI RT service set time interface." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops uefirttime_ops = {
> +	.description = "UEFI Runtime service time interface tests.",
> +	.init        = uefirttime_init,
> +	.deinit      = uefirttime_deinit,
> +	.minor_tests = uefirttime_tests
> +};
> +
> +FWTS_REGISTER(uefirttime, &uefirttime_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV);
>
Acked-by: Alex Hung <alex.hung@canonical.com>

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 6054eb3..b7adc20 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@ 
 # but libfwts.so depends on libraries produced by acpica/source/compiler.
 SUBDIRS = acpica/source/compiler lib acpica
  
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -Wall -Werror
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src/acpica/source/include -I$(top_srcdir)/efi_runtime -Wall -Werror
 
 bin_PROGRAMS = fwts
 fwts_SOURCES = main.c \
@@ -66,7 +66,8 @@  fwts_SOURCES = main.c \
 	kernel/version/version.c \
 	kernel/oops/oops.c \
 	uefi/csm/csm.c \
-	uefi/uefidump/uefidump.c
+	uefi/uefidump/uefidump.c \
+	uefi/uefirttime/uefirttime.c
 
 fwts_LDFLAGS = -ljson -lm
 
diff --git a/src/lib/include/fwts_uefi.h b/src/lib/include/fwts_uefi.h
index f45027d..73cd773 100644
--- a/src/lib/include/fwts_uefi.h
+++ b/src/lib/include/fwts_uefi.h
@@ -41,6 +41,13 @@  enum {
 	FWTS_UEFI_VAR_RUNTIME_ACCESS =		0x00000004
 };
 
+enum {
+	FWTS_UEFI_TIME_ADJUST_DAYLIGHT =	0x01,
+	FWTS_UEFI_TIME_IN_DAYLIGHT = 		0x02
+};
+
+#define FWTS_UEFI_UNSPECIFIED_TIMEZONE 0x07FF
+
 #if 0
 typedef struct {
 	char *description;
diff --git a/src/uefi/uefirttime/uefirttime.c b/src/uefi/uefirttime/uefirttime.c
new file mode 100644
index 0000000..686cf2d
--- /dev/null
+++ b/src/uefi/uefirttime/uefirttime.c
@@ -0,0 +1,328 @@ 
+/*
+ * Copyright (C) 2012 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 IS_LEAP(year) \
+		((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+static int fd;
+static uint32_t dayofmonth[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static bool dayvalid(EFI_TIME *Time)
+{
+	if (Time->Day < 1)
+		return false;
+
+	if (Time->Day > dayofmonth[Time->Month - 1])
+		return false;
+
+	/* check month 2 */
+	if (Time->Month == 2 && (!IS_LEAP(Time->Year) && Time->Day > 28))
+		return false;
+
+	return true;
+}
+
+static bool checktimefields(fwts_framework *fw, EFI_TIME *Time)
+{
+	if (Time->Year < 1900 || Time->Year > 9999) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadYear",
+			"Time returned an invalid year %" PRIu16
+			", should be between 1900 and 9999.",
+			Time->Year);
+		return false;
+	}
+
+	if (Time->Month < 1 || Time->Month > 12) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadMonth",
+			"Time returned an invalid month %" PRIu8
+			", should be between 1 and 12.",
+			Time->Month);
+		return false;
+	}
+
+	if (!dayvalid(Time)) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadDay",
+			"Time returned an invalid day %" PRIu8
+			", should be between 1 and 28-31 depends on month/year.",
+			Time->Day);
+		return false;
+	}
+
+	if (Time->Hour > 23) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadHour",
+			"Time returned an invalid hour %" PRIu8
+			", should be between 0 and 23.",
+			Time->Hour);
+		return false;
+	}
+
+	if (Time->Minute > 59) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadMinute",
+			"Time returned an invalid minute %" PRIu8
+			", should be between 0 and 59.",
+			Time->Minute);
+		return false;
+	}
+
+	if (Time->Second > 59) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadSecond",
+			"Time returned an invalid second %" PRIu8
+			", should be between 0 and 59.",
+			Time->Second);
+		return false;
+	}
+
+	if (Time->Nanosecond > 999999999) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadNanosecond",
+			"Time returned an invalid nanosecond %" PRIu32
+			", should be between 0 and 999999999.",
+			Time->Nanosecond);
+		return false;
+	}
+
+	if (!(Time->TimeZone == FWTS_UEFI_UNSPECIFIED_TIMEZONE ||
+		(Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadTimezone",
+			"Time returned an invalid timezone %" PRId16
+			", should be between -1440 and 1440 or 2047.",
+			Time->TimeZone);
+		return false;
+	}
+
+	if (Time->Daylight & (~(FWTS_UEFI_TIME_ADJUST_DAYLIGHT |
+				FWTS_UEFI_TIME_IN_DAYLIGHT))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"UEFIRuntimeTimeFieldBadDaylight",
+			"Time returned an invalid daylight %" PRIu8
+			", all other bits except UEFI_TIME_IN_DAYLIGHT and "
+			"UEFI_TIME_ADJUST_DAYLIGHT must be zero.",
+			Time->Daylight);
+		return false;
+	}
+	return true;
+}
+
+static int uefirttime_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 uefirttime_deinit(fwts_framework *fw)
+{
+
+	close(fd);
+
+	return FWTS_OK;
+}
+
+
+static int uefirttime_test1(fwts_framework *fw)
+{
+	long ioret;
+	struct efi_gettime gettime;
+	EFI_TIME efi_time;
+
+	EFI_TIME_CAPABILITIES efi_time_cap;
+	uint64_t status;
+
+	gettime.Capabilities = &efi_time_cap;
+	gettime.Time = &efi_time;
+	gettime.status = &status;
+
+	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
+
+	if (ioret == -1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
+			"Failed to get time with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	if (!checktimefields(fw, gettime.Time))
+		return FWTS_ERROR;
+
+	fwts_passed(fw, "UEFI runtime service GetTime interface test passed.");
+
+	return FWTS_OK;
+}
+
+static int uefirttime_test2(fwts_framework *fw)
+{
+
+	long ioret;
+	struct efi_settime settime;
+	uint64_t status;
+	struct efi_gettime gettime;
+
+	EFI_TIME oldtime;
+	EFI_TIME newtime;
+	EFI_TIME time;
+	EFI_TIME_CAPABILITIES efi_time_cap;
+
+	gettime.Capabilities = &efi_time_cap;
+	gettime.Time = &oldtime;
+	gettime.status = &status;
+	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
+
+	if (ioret == -1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
+			"Failed to get time with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	/* refer to UEFI SCT 2.3 test items */
+	/* change year */
+	time = oldtime;
+	if (time.Year != 2012)
+		time.Year = 2012;
+	else
+		time.Year = 2016;
+
+	/* change month */
+	if (time.Month != 1)
+		time.Month = 1;
+	else
+		time.Month = 12;
+
+	/* Change daylight */
+	if (time.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)
+		time.Daylight &= ~FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
+	else
+		time.Daylight |= FWTS_UEFI_TIME_ADJUST_DAYLIGHT;
+
+	/* Change time zone */
+	if (time.TimeZone != 0)
+		time.TimeZone = 0;
+	else
+		time.TimeZone = 1;
+
+	settime.Time = &time;
+	settime.status = &status;
+
+	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
+	if (ioret == -1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
+			"Failed to set time with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	sleep(1);
+
+	gettime.Time = &newtime;
+
+	ioret = ioctl(fd, EFI_RUNTIME_GET_TIME, &gettime);
+
+	if (ioret == -1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeGetTime",
+			"Failed to get time with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	if (!((oldtime.Year == 2012) && (newtime.Year == 2016)) &&
+	    !((oldtime.Year != 2012) && (newtime.Year == 2012))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeYear",
+			"Failed to set year with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	if (!((oldtime.Month == 1) && (newtime.Month == 12)) &&
+	    !((oldtime.Month != 1) && (newtime.Month == 1))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeMonth",
+			"Failed to set month with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	if (!((oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT) &&
+	    (!(newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) &&
+	    !((!(oldtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT)) &&
+	    (newtime.Daylight & FWTS_UEFI_TIME_ADJUST_DAYLIGHT))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeDaylight",
+			"Failed to set daylight with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	if (!((oldtime.TimeZone == 0) && (newtime.TimeZone == 1)) &&
+	    !((oldtime.TimeZone != 0) && (newtime.TimeZone == 0))) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTimeTimezone",
+			"Failed to set timezone with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	/* restore the previous time. */
+	settime.Time = &oldtime;
+	ioret = ioctl(fd, EFI_RUNTIME_SET_TIME, &settime);
+	if (ioret == -1) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UEFIRuntimeSetTime",
+			"Failed to set time with UEFI runtime service.");
+		return FWTS_ERROR;
+	}
+
+	fwts_passed(fw, "UEFI runtime service SetTime interface test passed.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test uefirttime_tests[] = {
+	{ uefirttime_test1, "Test UEFI RT service get time interface." },
+	{ uefirttime_test2, "Test UEFI RT service set time interface." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops uefirttime_ops = {
+	.description = "UEFI Runtime service time interface tests.",
+	.init        = uefirttime_init,
+	.deinit      = uefirttime_deinit,
+	.minor_tests = uefirttime_tests
+};
+
+FWTS_REGISTER(uefirttime, &uefirttime_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_UNSAFE | FWTS_FLAG_ROOT_PRIV);