Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME

36 views
Skip to first unread message

Lee, Chun-Yi

unread,
Dec 19, 2013, 2:50:02 AM12/19/13
to
This patchset add the timezone support of ACPI TAD and EFI TIME, it
also add codes for adjusting system time base on the timezone value
from EFI TIME services when system boot.

Those patches bring the following changes:

+ Add ACPI driver against ACPI000E ACPI Time and Alarm Device.

+ Add RTC driver of ACPI TAD.

+ Add rtc ioctl functions, RTC_RD_GMTOFF and RTC_SET_GMTOFF, provide
interface to user space for maintain timezone value in BIOS.
The GMTOFF names of ioctl functions match with the tm_gmtoff field
in rtc_time and tm structure in GNU C library.

+ Add rtc ioctl function, RTC_CAPS_READ, provide to user space for
grab the capabilities value to timezone and daylight of interface.

+ Moved duplicate functions to efi.h header for convert EFI Time.

+ When "CMOS RTC not Present" flag set in FADT, block CMOS RTC interface
and switch to EFI time services.

+ Adjusting system time base on timezone from EFI time when system boot.

+ Add rtc-tz.txt document.

Please set CONFIG_RTC_DRV_EFI=m and CONFIG_RTC_ACPI_TAD=m to build RTC ACPI
TAD and EFI TIME drivers.For testing, please use "acpi_no_cmos_rtc=1" to set
CMOST RTC not Present flag in FADT to trigger the mechanism for adjust system
time and block CMOS interface.

About the definition of timezone and the format transformation between ACPI,
EFI and tm_gmtoff of GNU. Please check the rtc-tz.txt document in patch.


Jan Beulich (1):
x86-64/efi: Use EFI to deal with platform wall clock (again)

Lee, Chun-Yi (13):
rtc-efi: fix decrease day twice when computing year days
rtc: block registration of rtc-cmos when CMOS RTC Not Present
ACPI: Add ACPI 5.0 Time and Alarm Device driver
rtc: Add RTC driver of ACPI Time and Alarm Device
rtc-efi: register rtc-efi device when EFI enabled
rtc-efi: add GMTOFF support to rtc_efi
rtc-efi: set uie_unsupported for indicate rtc-efi doesn't support UIE
efi: move functions of access efi time to header file for sharing
rtc: improve and move week day computing function to rtc header
rtc: switch to get/set rtc time to efi functions if CMOS RTC Not
efi: adjust system time base on timezone from EFI time services
Documentation/RTC: add document of ACPI TAD and EFI TIME driver
acpi: add early parameter to set CMOS RTC Not Present bit for testing

Documentation/rtc-tz.txt | 510 +++++++++++++++++++++++++++++++++++++++++++
arch/x86/kernel/acpi/boot.c | 17 ++
arch/x86/kernel/rtc.c | 28 ++-
arch/x86/mm/pageattr.c | 10 +-
arch/x86/platform/efi/efi.c | 89 ++++++--
drivers/acpi/Makefile | 3 +
drivers/acpi/acpi_tad.c | 176 +++++++++++++++
drivers/acpi/bus.c | 3 +
drivers/acpi/internal.h | 5 +
drivers/char/efirtc.c | 98 ---------
drivers/rtc/Kconfig | 12 +-
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-acpitad.c | 283 ++++++++++++++++++++++++
drivers/rtc/rtc-cmos.c | 9 +
drivers/rtc/rtc-dev.c | 4 +
drivers/rtc/rtc-efi.c | 177 +++++++---------
drivers/rtc/rtc-sysfs.c | 8 +
include/asm-generic/rtc.h | 22 ++
include/linux/acpi.h | 31 +++
include/linux/efi.h | 127 +++++++++++-
include/linux/rtc.h | 43 ++++
include/uapi/linux/rtc.h | 5 +
init/main.c | 13 +-
kernel/time.c | 2 +-
24 files changed, 1439 insertions(+), 237 deletions(-)
create mode 100644 Documentation/rtc-tz.txt
create mode 100644 drivers/acpi/acpi_tad.c
create mode 100644 drivers/rtc/rtc-acpitad.c

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Lee, Chun-Yi

unread,
Dec 19, 2013, 2:50:02 AM12/19/13
to
We should not acess CMOS address when CMOS RTC Not Present bit set in
FADT. The ee5872be patch didn't avoid rtc-cmos driver loaded when system support
ACPI PNP PNP0B0* devices.
So this patch block the registion of rtc-cmos driver to avoid
user space access RTC through CMOS interface.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
arch/x86/kernel/rtc.c | 20 ++++++++++++++++----
drivers/rtc/rtc-cmos.c | 9 +++++++++
2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index ca9622a..9b6c568 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -174,16 +174,27 @@ static __init int add_rtc_cmos(void)
{ "PNP0b00", "PNP0b01", "PNP0b02", };
struct pnp_dev *dev;
struct pnp_id *id;
- int i;
+ int i = 0;
+ bool found_pnp;

pnp_for_each_dev(dev) {
for (id = dev->id; id; id = id->next) {
for (i = 0; i < ARRAY_SIZE(ids); i++) {
- if (compare_pnp_id(id, ids[i]) != 0)
- return 0;
+ if (compare_pnp_id(id, ids[i]) != 0) {
+ found_pnp = true;
+ goto found_pnp;
+ }
}
}
}
+
+found_pnp:
+ if (found_pnp) {
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)
+ pr_err(FW_BUG "Found %s device but CMOS RTC Not Present flag set\n", ids[i]);
+ return 0;
+ }
#endif
if (of_have_populated_dt())
return 0;
@@ -193,7 +204,8 @@ static __init int add_rtc_cmos(void)
return -ENODEV;

#ifdef CONFIG_ACPI
- if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
/* This warning can likely go away again in a year or two. */
pr_info("ACPI: not registering RTC platform device\n");
return -ENODEV;
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index f148762..3a84ca9 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -28,6 +28,9 @@
* interrupts disabled, holding the global rtc_lock, to exclude those
* other drivers and utilities on correctly configured systems.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -1144,6 +1147,12 @@ static int __init cmos_init(void)
{
int retval = 0;

+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+ pr_info("ACPI CMOS RTC Not Present detected - not loading\n");
+ return 0;
+ }
+
#ifdef CONFIG_PNP
retval = pnp_register_driver(&cmos_pnp_driver);
if (retval == 0)
--
1.6.4.2

Lee, Chun-Yi

unread,
Dec 19, 2013, 2:50:02 AM12/19/13
to
Compared with the logic in rtc_year_days of efirtc.c, the code in
rtc-efi decreases value of day twice when it computing year days.
That's becase rtc_year_days in rtc-lib.c already decrease day for
return the year days from 0 to 365.
---
drivers/rtc/rtc-efi.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 797aa02..c4c3843 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -35,7 +35,7 @@ static inline int
compute_yday(efi_time_t *eft)
{
/* efi_time_t.month is in the [1-12] so, we need -1 */
- return rtc_year_days(eft->day - 1, eft->month - 1, eft->year);
+ return rtc_year_days(eft->day, eft->month - 1, eft->year);
}
/*
* returns day of the week [0-6] 0=Sunday
--
1.6.4.2

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:01 AM12/19/13
to

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:01 AM12/19/13
to
This patch add the driver of Time and Alarm Device in ACPI 5.0.
Currently it only implemented get/set time functions and grab
the capabilities of device when driver initial.

This driver also register rtc-acpitad platform device for RTC ACPITAD
stub driver using.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
drivers/acpi/Makefile | 3 +
drivers/acpi/acpi_tad.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 3 +
drivers/acpi/internal.h | 5 ++
include/linux/acpi.h | 31 ++++++++
5 files changed, 218 insertions(+), 0 deletions(-)
create mode 100644 drivers/acpi/acpi_tad.c

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 0331f91..d250b15 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,9 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
ifdef CONFIG_ACPI_VIDEO
acpi-y += video_detect.o
endif
+ifdef CONFIG_X86
+acpi-y += acpi_tad.o
+endif

# These are (potentially) separate modules

diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
new file mode 100644
index 0000000..c2200f3
--- /dev/null
+++ b/drivers/acpi/acpi_tad.c
@@ -0,0 +1,176 @@
+/* rtc.c - ACPI 5.0 Time and Alarm Driver
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jl...@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <acpi/acpi_drivers.h>
+
+#include <asm/time.h>
+
+#define ACPI_TIME_ALARM_NAME "Time and Alarm"
+ACPI_MODULE_NAME(ACPI_TIME_ALARM_NAME);
+#define ACPI_TIME_ALARM_CLASS "time_alarm"
+
+static const struct acpi_device_id time_alarm_ids[] = {
+ {"ACPI000E", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, time_alarm_ids);
+
+static struct platform_device rtc_acpitad_dev = {
+ .name = "rtc-acpitad",
+ .id = -1,
+};
+
+static struct acpi_device *acpi_tad_dev;
+static unsigned long long cap;
+
+int acpi_read_time(struct acpi_time *output)
+{
+ unsigned long flags;
+ struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ struct acpi_time *acpit;
+ acpi_status status;
+
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ return -EINVAL;
+
+ if (!output)
+ return -EINVAL;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ status = acpi_evaluate_object(acpi_tad_dev->handle, "_GRT", NULL, &result);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GRT"));
+ return -ENODEV;
+ }
+
+ obj = result.pointer;
+ if (!obj ||
+ obj->type != ACPI_TYPE_BUFFER ||
+ obj->buffer.length > sizeof(struct acpi_time) ||
+ obj->buffer.length < offsetof(struct acpi_time, pad2)) {
+ dev_err(&acpi_tad_dev->dev, ACPI_TIME_ALARM_NAME
+ " Invalid _GRT data\n");
+ return -EINVAL;
+ }
+
+ acpit = (struct acpi_time *) obj->buffer.pointer;
+ if (acpit) {
+ output->year = acpit->year;
+ output->month = acpit->month;
+ output->day = acpit->day;
+ output->hour = acpit->hour;
+ output->minute = acpit->minute;
+ output->second = acpit->second;
+ output->milliseconds = acpit->milliseconds;
+ output->timezone = acpit->timezone;
+ output->daylight = acpit->daylight;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_read_time);
+
+int acpi_set_time(struct acpi_time *acpit)
+{
+ unsigned long flags;
+ struct acpi_object_list input;
+ union acpi_object params[1];
+ unsigned long long output;
+ acpi_status status;
+
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ return -EINVAL;
+
+ if (!acpit)
+ return -EINVAL;
+
+ input.count = 1;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(struct acpi_time);
+ params[0].buffer.pointer = (void *) acpit;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ status = acpi_evaluate_integer(acpi_tad_dev->handle, "_SRT", &input, &output);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRT"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_set_time);
+
+int acpi_tad_get_capability(unsigned long *output)
+{
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ *output = cap;
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_tad_get_capability);
+
+static int acpi_time_alarm_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ if (!device)
+ return -EINVAL;
+
+ acpi_tad_dev = device;
+
+ /* evaluate _GCP */
+ status = acpi_evaluate_integer(device->handle, "_GCP", NULL, &cap);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GCP"));
+ return -ENODEV;
+ }
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ pr_warn(FW_INFO "Get/Set real time features not available.\n");
+
+ if (platform_device_register(&rtc_acpitad_dev) < 0)
+ pr_err("Unable to register rtc-acpitad device\n");
+
+ return 0;
+}
+
+static struct acpi_driver acpi_time_alarm_driver = {
+ .name = "time_and_alarm",
+ .class = ACPI_TIME_ALARM_CLASS,
+ .ids = time_alarm_ids,
+ .ops = {
+ .add = acpi_time_alarm_add,
+ },
+};
+
+int __init acpi_tad_init(void)
+{
+ int result = 0;
+
+ result = acpi_bus_register_driver(&acpi_time_alarm_driver);
+ if (result < 0)
+ return -ENODEV;
+
+ return result;
+}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index bba9b72..3f7a075 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -689,6 +689,9 @@ static int __init acpi_init(void)
pci_mmcfg_late_init();
acpi_scan_init();
acpi_ec_init();
+#ifdef CONFIG_X86
+ acpi_tad_init();
+#endif
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index a29739c..9cfe589 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -134,6 +134,11 @@ void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void);
void acpi_ec_unblock_transactions_early(void);

+/* --------------------------------------------------------------------------
+ Time and Alarm Device
+ -------------------------------------------------------------------------- */
+int acpi_tad_init(void);
+
/*--------------------------------------------------------------------------
Suspend/Resume
-------------------------------------------------------------------------- */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index d9099b1..c8dc104 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -173,6 +173,37 @@ extern int ec_transaction(u8 command,
u8 *rdata, unsigned rdata_len);
extern acpi_handle ec_get_handle(void);

+/*
+ * Time and Alarm device capability flags
+ */
+#define TAD_CAP_ACWAKE (1<<0)
+#define TAD_CAP_DCWAKE (1<<1)
+#define TAD_CAP_GETSETTIME (1<<2)
+#define TAD_CAP_ACCURACY (1<<3)
+
+#define ACPI_TIME_AFFECTED_BY_DAYLIGHT (1<<0)
+#define ACPI_TIME_ADJUSTED_FOR_DAYLIGHT (1<<1)
+#define ACPI_ISDST (ACPI_TIME_AFFECTED_BY_DAYLIGHT|ACPI_TIME_ADJUSTED_FOR_DAYLIGHT)
+#define ACPI_UNSPECIFIED_TIMEZONE 2047
+
+struct acpi_time {
+ u16 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ u8 pad1;
+ u16 milliseconds;
+ s16 timezone;
+ u8 daylight;
+ u8 pad2[3];
+};
+
+extern int acpi_read_time(struct acpi_time *acpit);
+extern int acpi_set_time(struct acpi_time *acpit);
+extern int acpi_tad_get_capability(unsigned long *output);
+
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)

typedef void (*wmi_notify_handler) (u32 value, void *context);

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
Per UEFI 2.3.1 spec, we can use SetTime() to store the timezone value to
BIOS and get it back by GetTime(). It's good for installation system to
gain the default timezone setting from BIOS that was set by
manufacturer.

This patch adds 2 new iotrl: RTC_RD_GMTOFF and RTC_SET_GMTOFF to rtc_efi
support get/set gmt offset that mapping to the GUN's tm_gmtoff extension
(Seconds east of UTC).
Due the timezone definition of UEFI is "Localtime = UTC - TimeZone",
rtc_efi driver will transfer the format between GNU and EFI.

The logic of timezone only affect on x86 architecture and keep the
original EFI_UNSPECIFIED_TIMEZONE value on IA64.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
drivers/rtc/rtc-efi.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 99 insertions(+), 1 deletions(-)

diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index c4c3843..e0e3c7e 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -75,7 +75,10 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
eft->second = wtime->tm_sec;
eft->nanosecond = 0;
eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+#ifdef CONFIG_IA64
+ /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+#endif
}

static void
@@ -108,6 +111,84 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
}
}

+static int efi_read_gmtoff(struct device *dev, long int *arg)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+ s16 timezone;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ timezone = (s16)le16_to_cpu(eft.timezone);
+ *arg = EFI_UNSPECIFIED_TIMEZONE * 60;
+ if (abs(timezone) != EFI_UNSPECIFIED_TIMEZONE &&
+ abs(timezone) <= 1440)
+ *arg = timezone * 60 * -1;
+
+ return 0;
+}
+
+static int efi_set_gmtoff(struct device *dev, long int arg)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+ s16 timezone;
+
+ /* transfer seconds east of UTC to minutes for ACPI */
+ timezone = arg / 60 * -1;
+ if (abs(timezone) > 1440 &&
+ abs(timezone) != EFI_UNSPECIFIED_TIMEZONE)
+ return -EINVAL;
+
+ /* can not use -2047 */
+ if (timezone == EFI_UNSPECIFIED_TIMEZONE * -1)
+ timezone = EFI_UNSPECIFIED_TIMEZONE;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ eft.timezone = (s16)cpu_to_le16(timezone);
+ status = efi.set_time(&eft);
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't set timezone\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int efi_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ long int gmtoff;
+ int err;
+
+ switch (cmd) {
+ case RTC_RD_GMTOFF:
+ err = efi_read_gmtoff(dev, &gmtoff);
+ if (err)
+ return err;
+ return put_user(gmtoff, (unsigned long __user *)arg);
+ case RTC_SET_GMTOFF:
+ return efi_set_gmtoff(dev, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
efi_time_t eft;
@@ -172,6 +253,17 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
{
efi_status_t status;
efi_time_t eft;
+#ifdef CONFIG_X86
+ efi_time_cap_t cap;
+
+ /* read time for grab timezone to avoid overwrite it */
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+#endif

convert_to_efi_time(tm, &eft);

@@ -181,13 +273,16 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
}

static const struct rtc_class_ops efi_rtc_ops = {
+#ifdef CONFIG_X86
+ .ioctl = efi_rtc_ioctl,
+#endif
.read_time = efi_read_time,
.set_time = efi_set_time,
.read_alarm = efi_read_alarm,
.set_alarm = efi_set_alarm,
};

-static int __init efi_rtc_probe(struct platform_device *dev)
+static int efi_rtc_probe(struct platform_device *dev)
{
struct rtc_device *rtc;

@@ -196,6 +291,8 @@ static int __init efi_rtc_probe(struct platform_device *dev)
if (IS_ERR(rtc))
return PTR_ERR(rtc);

+ rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);
+
platform_set_drvdata(dev, rtc);

return 0;
@@ -213,3 +310,4 @@ module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
MODULE_AUTHOR("dann frazier <da...@hp.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EFI RTC driver");
+MODULE_ALIAS("platform:rtc-efi");

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
We should not acess CMOS address when CMOS RTC Not Present bit set in
FADT. The ee5872be patch didn't avoid rtc-cmos driver loaded when system support
ACPI PNP PNP0B0* devices.
So this patch block the registion of rtc-cmos driver to avoid
user space access RTC through CMOS interface.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
+ return 0;
+ }
+
#ifdef CONFIG_PNP
retval = pnp_register_driver(&cmos_pnp_driver);
if (retval == 0)

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
This is a patch for testing that will not go to any git tree.

Add a early kernel parameter to set CMOS RTC Not Present bit in
acpi_gbl_FADT variable for testing. Use acpi_no_cmos_rtc=1 to enable
this bit to block rtc cmos interface.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 6c0b43b..64925dd 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -83,6 +83,8 @@ static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
#warning ACPI uses CMPXCHG, i486 and later hardware
#endif

+static bool acpi_no_cmos_rtc;
+
/* --------------------------------------------------------------------------
Boot-time Configuration
-------------------------------------------------------------------------- */
@@ -1531,6 +1533,13 @@ int __init acpi_boot_init(void)
*/
acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt);

+ /* Dummy NO_CMOS_RTC enable option to fake out RTC CMOS */
+ if (acpi_no_cmos_rtc) {
+ acpi_gbl_FADT.header.revision = 5;
+ acpi_gbl_FADT.boot_flags |= ACPI_FADT_NO_CMOS_RTC;
+ pr_info("acpi: Set NO_CMOS_RTC bit in FADT for testing\n");
+ }
+
/*
* Process the Multiple APIC Description Table (MADT), if present
*/
@@ -1673,3 +1682,11 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
e820_add_region(addr, size, E820_ACPI);
update_e820();
}
+
+/* Dummy NO_CMOS_RTC enable option to fake out RTC CMOS */
+static int __init setup_acpi_no_cmos_rtc(char *arg)
+{
+ acpi_no_cmos_rtc = true;
+ return 0;
+}
+early_param("acpi_no_cmos_rtc", setup_acpi_no_cmos_rtc);

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.

This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
checks efi_enabled flag and efi-rtc driver should enabled.

Cc: Matt Fleming <matt.f...@intel.com>
Cc: H. Peter Anvin <h...@zytor.com>
Cc: Matthew Garrett <matthew...@nebula.com>
Cc: Thomas Gleixner <tg...@linutronix.de>
Cc: Ingo Molnar <mi...@redhat.com>
Cc: Jan Beulich <JBeu...@suse.com>
Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
drivers/rtc/Kconfig | 2 +-
2 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 7a7a692..42d6052 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -42,6 +42,7 @@
#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/bcd.h>
+#include <linux/platform_device.h>

#include <asm/setup.h>
#include <asm/efi.h>
@@ -876,6 +877,22 @@ void __init efi_enter_virtual_mode(void)
0, NULL);
}

+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (efi_enabled(EFI_RUNTIME_SERVICES) &&
+ platform_device_register(&rtc_efi_dev) < 0)
+ pr_err("unable to register rtc-efi device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+arch_initcall(rtc_init);
+
/*
* Convenience functions to obtain memory types and attributes
*/
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 349dbc4..bee13de 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -726,7 +726,7 @@ config RTC_DRV_DA9055

config RTC_DRV_EFI
tristate "EFI RTC"
- depends on IA64
+ depends on EFI
help
If you say yes here you will get support for the EFI
Real Time Clock.

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
This patch add rtc-tz.txt document to explain the RTC driver of
ACPI TAD, EFI TIME. It focus on the timezone field and CMOS RTC Not
Present bit of ACPI 5.0.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
Documentation/rtc-tz.txt | 510 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 510 insertions(+), 0 deletions(-)
create mode 100644 Documentation/rtc-tz.txt

diff --git a/Documentation/rtc-tz.txt b/Documentation/rtc-tz.txt
new file mode 100644
index 0000000..7dfe523
--- /dev/null
+++ b/Documentation/rtc-tz.txt
@@ -0,0 +1,510 @@
+
+ ACPI TAD and EFI TIME Drivers for Timezone and Daylight
+ =======================================================
+
+In ACPI 5.0 ACPI TAD (Time and Alram Device) and EFI, they defined
+Timezone and Daylight field in time struct:
+
+ * ACPI TAD ... INT16 TimeZone
+ -1440 to 1440 or 2047 (unspecified)
+ Time zone field is the number of minutes that
+ the local time lags behind the UTC time.
+ Follow this equation: Localtime = UTC - TimeZone
+ UINT8 Daylight
+ Daylight is a bitmask containing the daylight
+ savings time information for the time:
+ Bit0: 1 = the time is affected by daylight savings time,
+ 0= time is not affected by daylight savings.
+ Bit1: 1= the time has been adjusted for daylight savings time,
+ 0= the time hasn't been adjusted for daylight savings.
+
+ * EFI TIME ... INT16 TimeZone
+ -1440 to 1440 or 2047 (unspecified)
+ Time zone field is the number of minutes that
+ the local time lags behind the UTC time.
+ Follow this equation: Localtime = UTC - TimeZone
+ UINT8 Daylight
+ EFI_TIME_ADJUST_DAYLIGHT 0x01
+ indicates if the time is affected by daylight
+ savings time or not.
+ EFI_TIME_IN_DAYLIGHT 0x02
+ is set, the time has been adjusted for daylight
+ savings time.
+
+Then, the Timzone and daylight definition in GNU struct tm:
+
+ * GNU struct tm ... long int __tm_gmtoff; /* Seconds east of UTC. */
+ int tm_isdst; /* DST. [-1/0/1]*/
+
+For Timzone, the definition match between ACPI TAD and EFI TIME. But it
+different with GNU time struct. GNU's tm_gmtoff is "Seconds east of UTC".
+Simply say, the timzone of ACPI/EFI and GNU are sign difference.
+
+Example 1:
+ 'Asia/Taipei' UTC +8
+ GNU: tm_gmtoff = 28800 seconds.
+ Taiwan is east of UTC, so it's positive number.
+ ACPI or EFI: TimeZone = -480 hours
+ Timezone = UTC - Localtime = UTC - Taipei time
+ So, it's negative number.
+Example 2:
+ 'Americ/Los_Angeles' UTC -8
+ GNU: tm_gmtoff = -28800 seconds.
+ Los Angeles is west of UTC, so it's negative number.
+ ACPI or EFI: TimeZone = 480 hours
+ Timezone = UTC - Localtime = UTC - Taipei time
+ So, it's positive number.
+
+For Daylight, due to GNU's tm_isdst is only define: '1' means current
+time is in daylight savings. '0' means not. '-1' means non-available.
+So, 1b is the only value from ACPI/EFI will transfer to '1' in GNU.
+When ACPI or EFI value is:
+
+ * 11b: Time is affected by daylight and has been adjusted for daylight
+ tm->tm_isdst = 1
+ * 01b: Time is affected by daylight but hasn't been adjusted for daylight
+ tm->tm_isdst = 0
+ * 00b(or 10b): Time is affected by daylight
+ tm->tm_isdst = -1
+
+The above data tranfer work of Timezone and Daylight will handled by RTC driver
+to ACPI TAD and EFI TIME: rtc-acpitad and rtc-efi.
+
+SYSFS INTERFACE
+---------------
+
+The sysfs interface under /sys/class/rtc/rtcN provides access to various
+rtc attributes without requiring the use of ioctls. Here only have one new
+sysfs interface for grab the capabilities:
+
+caps: This interface will return a bitmap of capabilitites to the RTC
+ interface. Currently it indicates the RTC has capability for
+ handle Timezone or Daylight:
+ Bit0: Timezone. Set this bit means this interface has
+ capability to store Timezone
+ Bit1: Daylight: Set this bit means this interface has
+ capability to store Daylight savings time.
+ There already have the following definition in linux/rtc.h:
+ #define RTC_TZ_CAP (1 << 0)
+ #define RTC_DST_CAP (1 << 1)
+
+IOCTL INTERFACE
+---------------
+
+Here create 3 new ioctl functions for read/set timezone and read the
+capabilities of RTC interface.
+
+ * RTC_RD_GMTOFF, RTC_SET_GMTOFF ... Used to read and set timezone value.
+ Due to support the GNU tm_gmtoff format, so the input/output value
+ is "Seconds east of UTC". RTC drivers, rtc-acpitad and rtc-efi, done
+ the data transfer work.
+ The rtc-acpitad and rtc-efi driver also avoid to overwrite the timezone
+ field in their time struct in RTC_SET_TIME.
+
+ * RTC_CAPS_READ ... As the caps sysfs interface, this ioctl provides
+ interface to userspace application for grab capabilities of current
+ RTC interface. Application can early check does this interface support
+ Timezone or Daylight.
+
+CMOS RTC Not Present flag
+-------------------------
+
+ACPI 5.0 spec defines a "CMOS RTC Not Present" flag in IA-PC Boot
+Architecture Flags of FADT. If this bit set, that means OSPM need uses the
+ACPI Time and Alarm device instead. Software should not access RTC through
+CMOS interface.
+
+In Linux kernel, the defaul wallclock functions deal with RTC by CMOS, it
+should move to ACPI TAD or EFI Time servcies. ACPI Time and Alarm device is
+described in DSDT that need wait until DSDT parsed by kernel in subsystem
+initial stage, so it can not used to deal with wallclock when system boot.
+
+Current solution of "CMOS RTC Not Present" is switch to EFI time services.
+On x86_64 EFI machine kernel will deal with RTC by EFI time services. In
+initial process, system time will adjusted base on timezone value from EFI
+time services, it also set persistent_clock_is_local global variable to
+avoid user space adjust system time by userland timezone again.
+
+The above efi warp clock mechanism will triggered on x86_64 EFI machine when
+timezone value is neither 0 nor 2047(UNSPECIFIED), kernel assume the value
+of RTC is local time. On the other hand, system just follow the old logic
+when timezone value from EFI is 0 or 2047, kernel assume the value of RTC is
+UTC time.
+
+About the 2047(EFI_UNSPECIFIED_TIMEZONE) value, it's the default value of
+UEFI BIOS if there didn't have any software set it through EFI interface.
+We can _NOT_ follow EFI spec to interpret the RTC time as a local time if
+timezone value is EFI_UNSPECIFIED_TIMEZONE, that's because Linux stored
+UTC to BIOS on shipped UEFI machines.
+
+
+-------------------- 8< ---------------- 8< -----------------------------
+
+/*
+ * Timezone of ACPI/EFI RTC Driver Test Program
+ *
+ * Compile with:
+ * gcc rtc-tz-test.c -o rtc-tz-test
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jl...@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/rtc.h>
+
+#ifndef RTC_RD_GMTOFF
+#define RTC_RD_GMTOFF _IOR('p', 0x13, long int) /* Read time zone return seconds east of UTC */
+#define RTC_SET_GMTOFF _IOW('p', 0x14, long int) /* Set time zone input seconds east of UTC */
+#endif
+#ifndef RTC_CAPS_READ
+#define RTC_CAPS_READ _IOR('p', 0x15, unsigned int) /* Get capabilities, e.g. TZ, DST */
+#endif
+/* Time Zone and Daylight capabilities */
+#ifndef RTC_TZ_CAP
+#define RTC_TZ_CAP (1 << 0)
+#define RTC_DST_CAP (1 << 1)
+#endif
+
+#define MAX_DEV 5
+#define ADJUST_MIN 60
+#define DEFAULT_TZ 28800 /* GMT offset of Taiwan R.O.C (Seconds east of UTC)*/
+#define ADJUST_TZ -28800 /* GMT offset of Los Angeles (Seconds east of UTC) */
+
+static const char dev_path[] = "/dev/rtc";
+static const char sys_path[] = "/sys/class/rtc/rtc";
+
+static const struct {
+ const char *driver_name;
+ const char *name;
+} names[] = {
+ {"rtc_cmos", "CMOS"},
+ {"rtc-efi", "EFI"},
+ {"rtc-acpitad", "ACPI-TAD"},
+};
+
+struct rtc_dev {
+ char dev_path[10];
+ char sys_path[21];
+ char name[15];
+ char driver_name[15];
+ unsigned int caps;
+};
+
+struct rtc_dev rtc_devs[5];
+
+void search_rtc_dev(void)
+{
+ int i, j, fd, ret;
+ FILE *fin;
+
+ for (i = 0; i <= MAX_DEV; i++)
+ {
+ char path_tmp[30];
+ sprintf(path_tmp, "%s%d", dev_path, i);
+
+ fd = open(path_tmp, O_RDONLY);
+ if (fd != -1) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ memcpy(dev->dev_path, path_tmp, 10);
+ sprintf(dev->sys_path, "%s%d/", sys_path, i);
+
+ sprintf(path_tmp, "%s%s", dev->sys_path, "name");
+ if ((fin = fopen(path_tmp, "r")) != NULL)
+ fscanf(fin, "%s", dev->driver_name);
+ fclose(fin);
+
+ for (j = 0; j < sizeof(names)/sizeof(names[0]); j++) {
+ if (!strcmp(dev->driver_name, names[j].driver_name))
+ memcpy(dev->name, names[j].name, strlen(names[j].name));
+ }
+
+ ioctl(fd, RTC_CAPS_READ, &dev->caps);
+ close(fd);
+ }
+ }
+}
+
+void print_rtc_dev(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ printf("Name: %s(%s)\n", dev->name, dev->driver_name);
+ printf(" Device Path: %s\n", dev->dev_path);
+ printf(" Sysfs Path : %s\n", dev->sys_path);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ printf(" RTC date/time: %d-%d-%d %02d:%02d:%02d\n",
+ rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
+ rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+ printf(" Is Daylight: %d (%s)\n", rtc_tm.tm_isdst,
+ (rtc_tm.tm_isdst)? (rtc_tm.tm_isdst < 0)? "NOT AVAILABLE":"INEFFECT":"NOT IN EFFECT");
+
+ printf(" Capabilities: %d (%s %s)\n", dev->caps,
+ (dev->caps & RTC_TZ_CAP)? "TZ":"",
+ (dev->caps & RTC_DST_CAP)? "DST":"");
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1)
+ printf(" GMTOFF: not support\n");
+ else
+ printf(" GMTOFF: %ld TIMEZONE: %d\n", gmtoff, gmtoff / 60 * -1);
+
+ close(fd);
+ printf("\n");
+}
+
+void print_rtc_devs(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev(&rtc_devs[i]);
+ }
+}
+
+void print_rtc_dev_time(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ printf(" %s: %d-%d-%d %02d:%02d:%02d\n", dev->name,
+ rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
+ rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+
+ close(fd);
+}
+
+void print_rtc_devs_time(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev_time(&rtc_devs[i]);
+ }
+}
+
+void time_increase(struct rtc_dev *dev, int in_min)
+{
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ /* Increase minutes */
+ rtc_tm.tm_min += in_min;
+ if (rtc_tm.tm_min >= 60) {
+ rtc_tm.tm_hour += (rtc_tm.tm_min / 60);
+ rtc_tm.tm_min %= 60;
+ if (rtc_tm.tm_hour >= 24) {
+ rtc_tm.tm_mday += (rtc_tm.tm_hour / 24);
+ rtc_tm.tm_hour %= 24;
+ }
+ /* Yes, it's not perfect, only adjust to mday level for testing */
+ }
+
+ /* Set increased time */
+ ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_SET_TIME ioctl");
+
+ close(fd);
+}
+
+void time_decrease(struct rtc_dev *dev, int in_min)
+{
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ /* Increase minutes */
+ if ((rtc_tm.tm_min - in_min) < 0) {
+ rtc_tm.tm_hour -= (in_min / 60);
+ rtc_tm.tm_min -= in_min % 60;
+ if (rtc_tm.tm_hour < 0) {
+ rtc_tm.tm_mday += (rtc_tm.tm_hour / 24);
+ rtc_tm.tm_hour = (rtc_tm.tm_hour % 24) * -1;
+ }
+ /* Yes, it's not perfect, only adjust to mday level for testing */
+ }
+
+ ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_SET_TIME ioctl");
+
+ close(fd);
+}
+
+void set_rtc_time_test(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ if(strlen(dev->sys_path) &&
+ dev->caps & RTC_TZ_CAP) {
+ printf("Test Target: %s(%s)\n", dev->name, dev->driver_name);
+
+ printf(" Before Increase\n");
+ print_rtc_devs_time();
+ time_increase(dev, ADJUST_MIN);
+ printf(" After Increased %d minutes\n", ADJUST_MIN);
+ print_rtc_devs_time();
+ printf(" Before Decrease\n");
+ print_rtc_devs_time();
+ time_decrease(dev, ADJUST_MIN);
+ printf(" After Decreased %d minutes\n", ADJUST_MIN);
+ print_rtc_devs_time();
+
+ printf("\n\n");
+ }
+ }
+}
+
+void print_rtc_dev_tz(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1)
+ printf(" %s(%s): not support\n", dev->name, dev->driver_name);
+ else
+ printf(" %s(%s): GMTOFF: %ld TIMEZONE: %d\n", dev->name, dev->driver_name, gmtoff, gmtoff / 60 * -1);
+
+ close(fd);
+}
+
+void print_rtc_devs_tz()
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev_tz(&rtc_devs[i]);
+ }
+}
+
+long int change_gmtoff(struct rtc_dev *dev, long int gmtoff_in)
+{
+ long int gmtoff = 122820;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1) {
+ printf("RTC_RD_GMTOFF fail.\n");
+ goto read_err;
+ }
+
+ ret = ioctl(fd, RTC_SET_GMTOFF, gmtoff_in);
+ if (ret == -1)
+ printf("RTC_SET_GMTOFF fail.\n");
+
+read_err:
+ close(fd);
+
+ return gmtoff;
+}
+
+void access_gmtoff_test(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ if(strlen(dev->sys_path) &&
+ dev->caps & RTC_TZ_CAP) {
+ long int orig_tz;
+
+ printf("Test Target: %s(%s)\n", dev->name, dev->driver_name);
+ printf("Set to Default TZ: %ld\n", DEFAULT_TZ);
+ change_gmtoff(dev, DEFAULT_TZ);
+
+ printf(" Before Adjust TZ\n");
+ print_rtc_devs_tz();
+ orig_tz = change_gmtoff(dev, ADJUST_TZ);
+ printf(" After Adjusted TZ\n");
+ print_rtc_devs_tz();
+ orig_tz = change_gmtoff(dev, orig_tz);
+ printf(" Adjusted Back\n");
+ print_rtc_devs_tz();
+
+ printf("\n\n");
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ search_rtc_dev();
+ printf("\nThis testing program will access following ioctl interface:\n");
+ printf(" RTC_RD_TIME/RTC_SET_TIME: Used to read and set RTC value.\n");
+ printf(" RTC_RD_GMTOFF/RTC_SET_GMTOFF: Used to read and set timezone, input/output is \"Seconds east of UTC\".\n");
+ printf(" RTC_CAPS_READ: Read the Timzone and Daylight capabilities of RTC interface.\n");
+
+ printf("\n======== Read Time Testing (RTC_RD_TIME/RTC_CAPS_READ) ========\n\n");
+ print_rtc_devs();
+
+ printf("\n======== Set Time Testing (RTC_SET_TIME/RTC_RD_TIME) ========\n");
+ printf("Only testing the interface supported Timezone.\n");
+ printf("This testing will increase %d minutes of RTC time then decrease it back.\n\n", ADJUST_MIN);
+ set_rtc_time_test();
+
+ printf("\n======== Access TimeZone Testing (RTC_RD_GMTOFF/RTC_SET_GMTOFF) ========\n");
+ printf("Only testing the interface supported Timezone.\n");
+ printf("Timezone of ACPI and UEFI spec: Time zone field is the number of minutes that the local time lags behind the UTC time.\n");
+ printf(" -1440 to 1440 or 2047. Localtime = UTC - TimeZone\n");
+ printf("Timezone in GNU tm struct: Seconds east of UTC.\n");
+ printf("This testing will set time zone to Los Angeles time (-28800 Seconds east of UTC) then set it back.\n\n", ADJUST_MIN);
+ access_gmtoff_test();
+
+ return 0;
+}

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:03 AM12/19/13
to
This patch add the RTC driver of ACPI TAD to provide userspace access
ACPI time through RTC interface.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-acpitad.c | 294 +++++++++++++++++++++++++++++++++++++++++++++
drivers/rtc/rtc-dev.c | 4 +
drivers/rtc/rtc-sysfs.c | 8 ++
include/linux/rtc.h | 5 +
include/uapi/linux/rtc.h | 5 +
7 files changed, 327 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-acpitad.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0077302..349dbc4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -878,6 +878,16 @@ config RTC_DRV_NUC900
If you say yes here you get support for the RTC subsystem of the
NUC910/NUC920 used in embedded systems.

+config RTC_ACPI_TAD
+ tristate "RTC ACPI Time and Alarm Device driver"
+ help
+ This driver exposes ACPI 5.0 Time and Alarm Device as RTC device.
+ Say Y (or M) if you have a computer with ACPI 5.0 firmware that
+ implemented Time and Alarm Device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called rtc_acpitad.
+
comment "on-CPU RTC drivers"

config RTC_DRV_DAVINCI
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 27b4bd8..bca5ab3 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
+obj-$(CONFIG_RTC_ACPI_TAD) += rtc-acpitad.o
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
diff --git a/drivers/rtc/rtc-acpitad.c b/drivers/rtc/rtc-acpitad.c
new file mode 100644
index 0000000..065a033
--- /dev/null
+++ b/drivers/rtc/rtc-acpitad.c
@@ -0,0 +1,294 @@
+/* A RTC driver for ACPI 5.0 Time and Alarm Device
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jl...@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(struct acpi_time *acpit)
+{
+ /* acpi_time.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(acpit->day, acpit->month - 1, acpit->year);
+}
+
+/*
+ * returns day of the week [0-6] 0=Sunday
+ */
+static int
+compute_wday(struct acpi_time *acpit)
+{
+ int y;
+ int ndays = 0;
+
+ if (acpit->year < 1900) {
+ pr_err("ACPI year < 1900, invalid date\n");
+ return -1;
+ }
+
+ for (y = 1900; y < acpit->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(acpit);
+
+ /*
+ * 1=1/1/1900 was a Monday
+ */
+ return (ndays + 1) % 7;
+}
+
+static void
+convert_to_acpi_time(struct rtc_time *tm, struct acpi_time *acpit)
+{
+ acpit->year = tm->tm_year + 1900;
+ acpit->month = tm->tm_mon + 1;
+ acpit->day = tm->tm_mday;
+ acpit->hour = tm->tm_hour;
+ acpit->minute = tm->tm_min;
+ acpit->second = tm->tm_sec;
+ acpit->milliseconds = 0;
+ acpit->daylight = tm->tm_isdst ? ACPI_ISDST : 0;
+}
+
+static void
+convert_from_acpi_time(struct acpi_time *acpit, struct rtc_time *tm)
+{
+ memset(tm, 0, sizeof(*tm));
+ tm->tm_sec = acpit->second;
+ tm->tm_min = acpit->minute;
+ tm->tm_hour = acpit->hour;
+ tm->tm_mday = acpit->day;
+ tm->tm_mon = acpit->month - 1;
+ tm->tm_year = acpit->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ tm->tm_wday = compute_wday(acpit);
+
+ /* day in the year [1-365]*/
+ tm->tm_yday = compute_yday(acpit);
+
+ switch (acpit->daylight & ACPI_ISDST) {
+ case ACPI_ISDST:
+ tm->tm_isdst = 1;
+ break;
+ case ACPI_TIME_AFFECTED_BY_DAYLIGHT:
+ tm->tm_isdst = 0;
+ break;
+ default:
+ tm->tm_isdst = -1;
+ }
+}
+
+static int acpitad_read_gmtoff(struct device *dev, long int *arg)
+{
+ struct acpi_time *acpit;
+ s16 timezone;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ /* transfer minutes to seconds east of UTC for userspace */
+ timezone = (s16)le16_to_cpu(acpit->timezone);
+ *arg = ACPI_UNSPECIFIED_TIMEZONE * 60;
+ if (abs(timezone) != ACPI_UNSPECIFIED_TIMEZONE &&
+ abs(timezone) <= 1440)
+ *arg = timezone * 60 * -1;
+
+error_read:
+ kfree(acpit);
+
+ return ret;
+}
+
+
+static int acpitad_set_gmtoff(struct device *dev, long int arg)
+{
+ struct acpi_time *acpit;
+ s16 timezone;
+ int ret;
+
+ /* transfer seconds east of UTC to minutes for ACPI */
+ timezone = arg / 60 * -1;
+ if (abs(timezone) > 1440 &&
+ abs(timezone) != ACPI_UNSPECIFIED_TIMEZONE)
+ return -EINVAL;
+
+ /* can not use -2047 */
+ if (timezone == ACPI_UNSPECIFIED_TIMEZONE * -1)
+ timezone = ACPI_UNSPECIFIED_TIMEZONE;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ acpit->timezone = (s16)cpu_to_le16(timezone);
+ ret = acpi_set_time(acpit);
+
+error_read:
+ kfree(acpit);
+
+ return ret;
+}
+
+static int acpitad_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ long int gmtoff;
+ int err;
+
+ switch (cmd) {
+ case RTC_RD_GMTOFF:
+ err = acpitad_read_gmtoff(dev, &gmtoff);
+ if (err)
+ return err;
+ return put_user(gmtoff, (unsigned long __user *)arg);
+ case RTC_SET_GMTOFF:
+ return acpitad_set_gmtoff(dev, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int acpitad_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct acpi_time *acpit;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ return ret;
+
+ convert_from_acpi_time(acpit, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int acpitad_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct acpi_time *acpit;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ /* read current timzone to avoid overwrite it by set time */
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ convert_to_acpi_time(tm, acpit);
+
+ ret = acpi_set_time(acpit);
+
+error_read:
+ kfree(acpit);
+ return ret;
+}
+
+static struct rtc_class_ops acpi_rtc_ops = {
+ .ioctl = acpitad_rtc_ioctl,
+ .read_time = acpitad_read_time,
+ .set_time = acpitad_set_time,
+};
+
+static int acpitad_rtc_probe(struct platform_device *dev)
+{
+ unsigned long cap;
+ struct rtc_device *rtc;
+ int ret;
+
+ ret = acpi_tad_get_capability(&cap);
+ if (ret)
+ return ret;
+
+ if (!(cap & TAD_CAP_GETSETTIME)) {
+ acpi_rtc_ops.read_time = NULL;
+ acpi_rtc_ops.set_time = NULL;
+ pr_warn("No get/set time support\n");
+ }
+
+ /* ACPI Alarm at least need AC wake capability */
+ if (!(cap & TAD_CAP_ACWAKE)) {
+ acpi_rtc_ops.read_alarm = NULL;
+ acpi_rtc_ops.set_alarm = NULL;
+ pr_warn("No AC wake support\n");
+ }
+
+ /* register rtc device */
+ rtc = rtc_device_register("rtc-acpitad", &dev->dev, &acpi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->uie_unsupported = 1;
+ rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int acpitad_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver acpitad_rtc_driver = {
+ .driver = {
+ .name = "rtc-acpitad",
+ .owner = THIS_MODULE,
+ },
+ .probe = acpitad_rtc_probe,
+ .remove = acpitad_rtc_remove,
+};
+
+static int __init acpitad_rtc_init(void)
+{
+ return platform_driver_register(&acpitad_rtc_driver);
+}
+
+static void __exit acpitad_rtc_exit(void)
+{
+ platform_driver_unregister(&acpitad_rtc_driver);
+}
+
+module_init(acpitad_rtc_init);
+module_exit(acpitad_rtc_exit);
+
+MODULE_AUTHOR("Lee, Chun-Yi <jl...@suse.com>");
+MODULE_DESCRIPTION("RTC ACPI Time and Alarm Device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-acpitad");
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index d049393..aab70e7 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -398,6 +398,10 @@ static long rtc_dev_ioctl(struct file *file,
err = -EFAULT;
return err;

+ case RTC_CAPS_READ:
+ err = put_user(rtc->caps, (unsigned int __user *)uarg);
+ break;
+
default:
/* Finally try the driver's ioctl interface */
if (ops->ioctl) {
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index babd43b..bdffb8f 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -122,6 +122,13 @@ hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(hctosys);

+static ssize_t
+caps_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", to_rtc_device(dev)->caps);
+}
+static DEVICE_ATTR_RO(caps);
+
static struct attribute *rtc_attrs[] = {
&dev_attr_name.attr,
&dev_attr_date.attr,
@@ -129,6 +136,7 @@ static struct attribute *rtc_attrs[] = {
&dev_attr_since_epoch.attr,
&dev_attr_max_user_freq.attr,
&dev_attr_hctosys.attr,
+ &dev_attr_caps.attr,
NULL,
};
ATTRIBUTE_GROUPS(rtc);
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index c2c2897..e6380ec 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -116,6 +116,11 @@ struct rtc_device
/* Some hardware can't support UIE mode */
int uie_unsupported;

+ /* Time Zone and Daylight capabilities */
+#define RTC_TZ_CAP (1 << 0)
+#define RTC_DST_CAP (1 << 1)
+ int caps;
+
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h
index f8c82e6..5533914 100644
--- a/include/uapi/linux/rtc.h
+++ b/include/uapi/linux/rtc.h
@@ -94,6 +94,11 @@ struct rtc_pll_info {
#define RTC_VL_READ _IOR('p', 0x13, int) /* Voltage low detector */
#define RTC_VL_CLR _IO('p', 0x14) /* Clear voltage low information */

+#define RTC_RD_GMTOFF _IOR('p', 0x15, long int) /* Read time zone return seconds east of UTC */
+#define RTC_SET_GMTOFF _IOW('p', 0x16, long int) /* Set time zone input seconds east of UTC */
+
+#define RTC_CAPS_READ _IOR('p', 0x17, unsigned int) /* Get capabilities, e.g. TZ, DST */
+
/* interrupt flags */
#define RTC_IRQF 0x80 /* Any of the following is active */
#define RTC_PF 0x40 /* Periodic interrupt */

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:03 AM12/19/13
to
EFI time services provide the interface to store timezone to BIOS. The
timezone value from EFI indicates the offset of RTC time in minutes from
UTC.
The formula is: Localtime = UTC - TimeZone.

This patch add a efI_warp_clock() function to initial process for adjust
system time base on timezone value from EFI time services. It will also
set persistent_clock_is_local global variable to avoid user space
adjust timezone again.

This efi warp clock mechanism will triggered on x86_64 EFI machine when
timezone value is neither 0 nor 2047(UNSPECIFIED), kernel assume the
value of RTC is local time. On the other hand, system just follow
the old logic when timezone value from EFI is 0 or 2047, kernel assume
the value of RTC is UTC time.

About the 2047(EFI_UNSPECIFIED_TIMEZONE) value, it's the default value
of UEFI BIOS if there didn't have software set it through EFI interface.
We can _NOT_ follow EFI spec to interpret the RTC time as a local time
if timezone value is EFI_UNSPECIFIED_TIMEZONE, that's because Linux stored
UTC to BIOS on shipped UEFI machines.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
arch/x86/platform/efi/efi.c | 37 +++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 2 ++
init/main.c | 5 +++++
kernel/time.c | 2 +-
4 files changed, 45 insertions(+), 1 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 42d6052..848160e 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -306,6 +306,43 @@ static void efi_get_time(struct timespec *now)
now->tv_nsec = 0;
}

+static int efi_read_timezone(s16 *timezone)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efitime: can't read timezone.\n");
+ return -EINVAL;
+ }
+
+ *timezone = (s16)le16_to_cpu(eft.timezone);
+ return 0;
+}
+
+void __init efi_warp_clock(void)
+{
+ s16 timezone;
+
+ if (!efi_read_timezone(&timezone)) {
+ /* TimeZone value, 2047 or 0 means UTC */
+ if (timezone != 0 && timezone != 2047) {
+ struct timespec adjust;
+
+ persistent_clock_is_local = 1;
+ adjust.tv_sec = timezone * 60;
+ adjust.tv_nsec = 0;
+ timekeeping_inject_offset(&adjust);
+ pr_info("RTC timezone is %d mins behind of UTC.\n", timezone);
+ pr_info("Adjusted system time to UTC.\n");
+ }
+ }
+}
+
/*
* Tell the kernel about the EFI memory map. This might include
* more than the max 128 entries that can fit in the e820 legacy
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 1c78ae7..a8d4f5c 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -715,10 +715,12 @@ extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg);
extern void efi_gettimeofday (struct timespec *ts);
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
#ifdef CONFIG_X86
+extern void efi_warp_clock(void);
extern void efi_late_init(void);
extern void efi_free_boot_services(void);
extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size);
#else
+static inline void efi_warp_clock(void) {}
static inline void efi_late_init(void) {}
static inline void efi_free_boot_services(void) {}

diff --git a/init/main.c b/init/main.c
index 61164ce..9effb1c 100644
--- a/init/main.c
+++ b/init/main.c
@@ -570,6 +570,11 @@ asmlinkage void __init start_kernel(void)
hrtimers_init();
softirq_init();
timekeeping_init();
+#ifdef CONFIG_X86_64
+ /* adjust system time by timezone */
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ efi_warp_clock();
+#endif
time_init();
sched_clock_postinit();
perf_event_init();
diff --git a/kernel/time.c b/kernel/time.c
index 7c7964c..ce18bac 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -176,7 +176,7 @@ int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz)
update_vsyscall_tz();
if (firsttime) {
firsttime = 0;
- if (!tv)
+ if (!tv && !persistent_clock_is_local)
warp_clock();

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:03 AM12/19/13
to
Due to rtc-acpid and efi time used the same logic for computing
week day, so this patch moves code to rtc.h header file.

Additionally using a leap year algorithm to replace the for-loop
block in compute_wday for improve the performance. The first
version of algorithm is from Oliver Neukum.
---
drivers/rtc/rtc-acpitad.c | 13 +------------
include/linux/efi.h | 13 +------------
include/linux/rtc.h | 38 ++++++++++++++++++++++++++++++++++++++
3 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/drivers/rtc/rtc-acpitad.c b/drivers/rtc/rtc-acpitad.c
index 065a033..bdf7ae1 100644
--- a/drivers/rtc/rtc-acpitad.c
+++ b/drivers/rtc/rtc-acpitad.c
@@ -32,23 +32,12 @@ compute_yday(struct acpi_time *acpit)
static int
compute_wday(struct acpi_time *acpit)
{
- int y;
- int ndays = 0;
-
if (acpit->year < 1900) {
pr_err("ACPI year < 1900, invalid date\n");
return -1;
}

- for (y = 1900; y < acpit->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(acpit);
-
- /*
- * 1=1/1/1900 was a Monday
- */
- return (ndays + 1) % 7;
+ return rtc_wday(acpit->day, acpit->month - 1, acpit->year);
}

static void
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 3859f3e..1c78ae7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -177,23 +177,12 @@ compute_yday(efi_time_t *eft)
static inline int
compute_wday(efi_time_t *eft)
{
- int y;
- int ndays = 0;
-
if (eft->year < 1998) {
pr_err("EFI year < 1998, invalid date\n");
return -1;
}

- for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
+ return rtc_wday(eft->day, eft->month - 1, eft->year);
}

static inline void
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index e6380ec..511884f 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -196,6 +196,44 @@ static inline bool is_leap_year(unsigned int year)
return (!(year % 4) && (year % 100)) || !(year % 400);
}

+#define SINCE1900 25 /* valid from 2000 */
+
+static inline int rtc_wday(unsigned int day, unsigned int month, unsigned int year)
+{
+ int ndays, correction;
+ int base;
+
+ if (year < 1900) {
+ pr_err("rtc: year < 1900, invalid date\n");
+ return -1;
+ }
+
+ if (year >= 2000)
+ base = year - 2000;
+ else
+ base = year - 1900;
+
+ correction = 0;
+ if (base >= 0) {
+ correction += base / 4;
+ correction -= base / 100;
+ correction += base / 400;
+ if (year >= 2000)
+ correction += SINCE1900;
+
+ /* later rtc_year_days will add the leap day of current year */
+ correction -= ((is_leap_year(year)) ? 1 : 0);
+ }
+
+ ndays = (year - 1900) * 365 + correction;
+ ndays += rtc_year_days(day, month, year);
+
+ /*
+ * 1=1/1/1900 was a Monday
+ */
+ return (ndays + 1) % 7;
+}
+
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
extern int rtc_hctosys_ret;
#else

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
When CMOS RTC Not Present git set in FADT, system should not access CMOS
interface for time. This patch move get/set rtc time function from CMOS
to EFI runtime on x86_64 machine. And, it also set the BUG_ON check in
rtc_cmos_read/write function to avoid access it.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
arch/x86/kernel/rtc.c | 8 ++++++++
include/asm-generic/rtc.h | 22 ++++++++++++++++++++++
2 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index 9b6c568..a54cd09 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -117,6 +117,10 @@ unsigned char rtc_cmos_read(unsigned char addr)
{
unsigned char val;

+ BUG_ON(acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC &&
+ addr <= RTC_YEAR);
+
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
val = inb(RTC_PORT(1));
@@ -128,6 +132,10 @@ EXPORT_SYMBOL(rtc_cmos_read);

void rtc_cmos_write(unsigned char val, unsigned char addr)
{
+ BUG_ON(acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC &&
+ addr <= RTC_YEAR);
+
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
outb(val, RTC_PORT(1));
diff --git a/include/asm-generic/rtc.h b/include/asm-generic/rtc.h
index fa86f24..4ba8aa6 100644
--- a/include/asm-generic/rtc.h
+++ b/include/asm-generic/rtc.h
@@ -16,6 +16,8 @@
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/delay.h>
+#include <linux/efi.h>
+#include <linux/acpi.h>

#define RTC_PIE 0x40 /* periodic interrupt enable */
#define RTC_AIE 0x20 /* alarm interrupt enable */
@@ -51,6 +53,16 @@ static inline unsigned int __get_rtc_time(struct rtc_time *time)
unsigned int real_year;
#endif

+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+#if defined(CONFIG_EFI) && defined(CONFIG_X86_64)
+ printk_once(KERN_INFO "efi: get rtc time by EFI\n");
+ return efi_read_time(time);
+#else
+ BUG();
+#endif
+ }
+
/*
* read RTC once any update in progress is done. The update
* can take just over 2ms. We wait 20ms. There is no need to
@@ -123,6 +135,16 @@ static inline int __set_rtc_time(struct rtc_time *time)
unsigned int real_yrs, leap_yr;
#endif

+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+#if defined(CONFIG_EFI) && defined(CONFIG_X86_64)
+ printk_once(KERN_INFO "efi: set rtc time by EFI\n");
+ return efi_set_time(time);
+#else
+ BUG();
+#endif
+ }
+
yrs = time->tm_year;
mon = time->tm_mon + 1; /* tm_mon starts at zero */
day = time->tm_mday;

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
This patch set uie_unsupported flag when driver probed because current
rtc-efi driver doesn't support UIE mode. Otherwise RTC_UIE_ON ioctl doesn't
return EINVAL and it causes userspace think the RTC_UIE supported by rtc-efi.

Set uie_unsupported then We can enable CONFIG_RTC_INTF_DEV_UIE_EMUL to
emulate RTC_UIE on rtc-efi.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
drivers/rtc/rtc-efi.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index e0e3c7e..693ea47 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -293,6 +293,8 @@ static int efi_rtc_probe(struct platform_device *dev)

rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);

+ rtc->uie_unsupported = 1;
+
platform_set_drvdata(dev, rtc);

return 0;

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:02 AM12/19/13
to
drivers/acpi/Makefile | 3 +
drivers/acpi/acpi_tad.c | 176 +++++++++++++++
drivers/acpi/bus.c | 3 +
drivers/acpi/internal.h | 5 +
drivers/char/efirtc.c | 98 ---------
drivers/rtc/Kconfig | 12 +-
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-acpitad.c | 283 ++++++++++++++++++++++++
drivers/rtc/rtc-cmos.c | 9 +
drivers/rtc/rtc-dev.c | 4 +
drivers/rtc/rtc-efi.c | 177 +++++++---------
drivers/rtc/rtc-sysfs.c | 8 +
include/asm-generic/rtc.h | 22 ++
include/linux/acpi.h | 31 +++
include/linux/efi.h | 127 +++++++++++-
include/linux/rtc.h | 43 ++++
include/uapi/linux/rtc.h | 5 +
init/main.c | 13 +-
kernel/time.c | 2 +-
24 files changed, 1439 insertions(+), 237 deletions(-)
create mode 100644 Documentation/rtc-tz.txt
create mode 100644 drivers/acpi/acpi_tad.c
create mode 100644 drivers/rtc/rtc-acpitad.c

Lee, Chun-Yi

unread,
Dec 19, 2013, 3:00:01 AM12/19/13
to
There have some functions, e.g. compute_yday, compute_wday, convert efi
time... are duplicated in efirtc, rtc-efi and will also used in rtc.c.
So this patch moved those functions of access efi time to efi.h header
file for sharing.

Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
---
drivers/char/efirtc.c | 98 ------------------------------------
drivers/rtc/rtc-efi.c | 133 ++----------------------------------------------
include/linux/efi.h | 134 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 140 insertions(+), 225 deletions(-)

diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index e39e740..8a30a02 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -41,109 +41,11 @@

#define EFI_RTC_VERSION "0.4"

-#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
-/*
- * EFI Epoch is 1/1/1998
- */
-#define EFI_RTC_EPOCH 1998
-
static DEFINE_SPINLOCK(efi_rtc_lock);

static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);

-#define is_leap(year) \
- ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
-static const unsigned short int __mon_yday[2][13] =
-{
- /* Normal years. */
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- /* Leap years. */
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-};
-
-/*
- * returns day of the year [0-365]
- */
-static inline int
-compute_yday(efi_time_t *eft)
-{
- /* efi_time_t.month is in the [1-12] so, we need -1 */
- return __mon_yday[is_leap(eft->year)][eft->month-1]+ eft->day -1;
-}
-/*
- * returns day of the week [0-6] 0=Sunday
- *
- * Don't try to provide a year that's before 1998, please !
- */
-static int
-compute_wday(efi_time_t *eft)
-{
- int y;
- int ndays = 0;
-
- if ( eft->year < 1998 ) {
- printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
- return -1;
- }
-
- for(y=EFI_RTC_EPOCH; y < eft->year; y++ ) {
- ndays += 365 + (is_leap(y) ? 1 : 0);
- }
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
-}
-
-static void
-convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
-{
-
- eft->year = wtime->tm_year + 1900;
- eft->month = wtime->tm_mon + 1;
- eft->day = wtime->tm_mday;
- eft->hour = wtime->tm_hour;
- eft->minute = wtime->tm_min;
- eft->second = wtime->tm_sec;
- eft->nanosecond = 0;
- eft->daylight = wtime->tm_isdst ? EFI_ISDST: 0;
- eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
-}
-
-static void
-convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
-{
- memset(wtime, 0, sizeof(*wtime));
- wtime->tm_sec = eft->second;
- wtime->tm_min = eft->minute;
- wtime->tm_hour = eft->hour;
- wtime->tm_mday = eft->day;
- wtime->tm_mon = eft->month - 1;
- wtime->tm_year = eft->year - 1900;
-
- /* day of the week [0-6], Sunday=0 */
- wtime->tm_wday = compute_wday(eft);
-
- /* day in the year [1-365]*/
- wtime->tm_yday = compute_yday(eft);
-
-
- switch (eft->daylight & EFI_ISDST) {
- case EFI_ISDST:
- wtime->tm_isdst = 1;
- break;
- case EFI_TIME_ADJUST_DAYLIGHT:
- wtime->tm_isdst = 0;
- break;
- default:
- wtime->tm_isdst = -1;
- }
-}
-
static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 693ea47..4687022 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -22,95 +22,6 @@
#include <linux/rtc.h>
#include <linux/efi.h>

-#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
-/*
- * EFI Epoch is 1/1/1998
- */
-#define EFI_RTC_EPOCH 1998
-
-/*
- * returns day of the year [0-365]
- */
-static inline int
-compute_yday(efi_time_t *eft)
-{
- /* efi_time_t.month is in the [1-12] so, we need -1 */
- return rtc_year_days(eft->day, eft->month - 1, eft->year);
-}
-/*
- * returns day of the week [0-6] 0=Sunday
- *
- * Don't try to provide a year that's before 1998, please !
- */
-static int
-compute_wday(efi_time_t *eft)
-{
- int y;
- int ndays = 0;
-
- if (eft->year < 1998) {
- pr_err("EFI year < 1998, invalid date\n");
- return -1;
- }
-
- for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
-}
-
-static void
-convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
-{
- eft->year = wtime->tm_year + 1900;
- eft->month = wtime->tm_mon + 1;
- eft->day = wtime->tm_mday;
- eft->hour = wtime->tm_hour;
- eft->minute = wtime->tm_min;
- eft->second = wtime->tm_sec;
- eft->nanosecond = 0;
- eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
-#ifdef CONFIG_IA64
- /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
- eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
-#endif
-}
-
-static void
-convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
-{
- memset(wtime, 0, sizeof(*wtime));
- wtime->tm_sec = eft->second;
- wtime->tm_min = eft->minute;
- wtime->tm_hour = eft->hour;
- wtime->tm_mday = eft->day;
- wtime->tm_mon = eft->month - 1;
- wtime->tm_year = eft->year - 1900;
-
- /* day of the week [0-6], Sunday=0 */
- wtime->tm_wday = compute_wday(eft);
-
- /* day in the year [1-365]*/
- wtime->tm_yday = compute_yday(eft);
-
-
- switch (eft->daylight & EFI_ISDST) {
- case EFI_ISDST:
- wtime->tm_isdst = 1;
- break;
- case EFI_TIME_ADJUST_DAYLIGHT:
- wtime->tm_isdst = 0;
- break;
- default:
- wtime->tm_isdst = -1;
- }
-}
-
static int efi_read_gmtoff(struct device *dev, long int *arg)
{
efi_status_t status;
@@ -230,54 +141,22 @@ static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
return status == EFI_SUCCESS ? 0 : -EINVAL;
}

-static int efi_read_time(struct device *dev, struct rtc_time *tm)
+static int efi_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- efi_status_t status;
- efi_time_t eft;
- efi_time_cap_t cap;
-
- status = efi.get_time(&eft, &cap);
-
- if (status != EFI_SUCCESS) {
- /* should never happen */
- dev_err(dev, "can't read time\n");
- return -EINVAL;
- }
-
- convert_from_efi_time(&eft, tm);
-
- return rtc_valid_tm(tm);
+ return efi_read_time(tm);
}

-static int efi_set_time(struct device *dev, struct rtc_time *tm)
+static int efi_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- efi_status_t status;
- efi_time_t eft;
-#ifdef CONFIG_X86
- efi_time_cap_t cap;
-
- /* read time for grab timezone to avoid overwrite it */
- status = efi.get_time(&eft, &cap);
-
- if (status != EFI_SUCCESS) {
- pr_err("efitime: can't read time\n");
- return -EINVAL;
- }
-#endif
-
- convert_to_efi_time(tm, &eft);
-
- status = efi.set_time(&eft);
-
- return status == EFI_SUCCESS ? 0 : -EINVAL;
+ return efi_set_time(tm);
}

static const struct rtc_class_ops efi_rtc_ops = {
#ifdef CONFIG_X86
.ioctl = efi_rtc_ioctl,
#endif
- .read_time = efi_read_time,
- .set_time = efi_set_time,
+ .read_time = efi_rtc_read_time,
+ .set_time = efi_rtc_set_time,
.read_alarm = efi_read_alarm,
.set_alarm = efi_set_alarm,
};
diff --git a/include/linux/efi.h b/include/linux/efi.h
index fc8fa0e..3859f3e 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -132,6 +132,12 @@ typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);
#define EFI_TIME_ADJUST_DAYLIGHT 0x1
#define EFI_TIME_IN_DAYLIGHT 0x2
#define EFI_UNSPECIFIED_TIMEZONE 0x07ff
+#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
+
+/*
+ * EFI Epoch is 1/1/1998
+ */
+#define EFI_RTC_EPOCH 1998

typedef struct {
u16 year;
@@ -154,6 +160,90 @@ typedef struct {
} efi_time_cap_t;

/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(efi_time_t *eft)
+{
+ /* efi_time_t.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(eft->day, eft->month - 1, eft->year);
+}
+
+/*
+ * returns day of the week [0-6] 0=Sunday
+ *
+ * Don't try to provide a year that's before 1998, please !
+ */
+static inline int
+compute_wday(efi_time_t *eft)
+{
+ int y;
+ int ndays = 0;
+
+ if (eft->year < 1998) {
+ pr_err("EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(eft);
+
+ /*
+ * 4=1/1/1998 was a Thursday
+ */
+ return (ndays + 4) % 7;
+}
+
+static inline void
+convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
+{
+ memset(wtime, 0, sizeof(*wtime));
+ wtime->tm_sec = eft->second;
+ wtime->tm_min = eft->minute;
+ wtime->tm_hour = eft->hour;
+ wtime->tm_mday = eft->day;
+ wtime->tm_mon = eft->month - 1;
+ wtime->tm_year = eft->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ wtime->tm_wday = compute_wday(eft);
+
+ /* day in the year [1-365]*/
+ wtime->tm_yday = compute_yday(eft);
+
+ switch (eft->daylight & EFI_ISDST) {
+ case EFI_ISDST:
+ wtime->tm_isdst = 1;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ wtime->tm_isdst = 0;
+ break;
+ default:
+ wtime->tm_isdst = -1;
+ }
+}
+
+static inline void
+convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
+{
+ eft->year = wtime->tm_year + 1900;
+ eft->month = wtime->tm_mon + 1;
+ eft->day = wtime->tm_mday;
+ eft->hour = wtime->tm_hour;
+ eft->minute = wtime->tm_min;
+ eft->second = wtime->tm_sec;
+ eft->nanosecond = 0;
+ eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+#ifdef CONFIG_IA64
+ /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
+ eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+#endif
+}
+
+
+/*
* EFI Boot Services table
*/
typedef struct {
@@ -585,6 +675,50 @@ efi_guid_unparse(efi_guid_t *guid, char *out)
return out;
}

+static inline int
+efi_set_time(struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+#ifdef CONFIG_X86
+ efi_time_cap_t cap;
+
+ /* read time for grab timezone to avoid overwrite it */
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efi: can't read time\n");
+ return -EINVAL;
+ }
+#endif
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static inline int
+efi_read_time(struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efi: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
extern void efi_init (void);
extern void *efi_get_pal_addr (void);
extern void efi_map_pal_code (void);
--
1.6.4.2

Matt Fleming

unread,
Dec 19, 2013, 5:50:01 AM12/19/13
to
On Thu, 19 Dec, at 06:20:16PM, Lee, Chun-Yi wrote:
> From: Jan Beulich <JBeu...@suse.com>
>
> Other than ix86, x86-64 on EFI so far didn't set the
> {g,s}et_wallclock accessors to the EFI routines, thus
> incorrectly using raw RTC accesses instead.
>
> Simply removing the #ifdef around the respective code isn't
> enough, however: While so far early get-time calls were done in
> physical mode, this doesn't work properly for x86-64, as virtual
> addresses would still need to be set up for all runtime regions
> (which wasn't the case on the system I have access to), so
> instead the patch moves the call to efi_enter_virtual_mode()
> ahead (which in turn allows to drop all code related to calling
> efi-get-time in physical mode).
>
> Additionally the earlier calling of efi_set_executable()
> requires the CPA code to cope, i.e. during early boot it must be
> avoided to call cpa_flush_array(), as the first thing this
> function does is a BUG_ON(irqs_disabled()).
>
> Also make the two EFI functions in question here static -
> they're not being referenced elsewhere.
>
> History:
>
> This commit was originally merged as bacef661acdb ("x86-64/efi:
> Use EFI to deal with platform wall clock") but it resulted in some
> ASUS machines no longer booting due to a firmware bug, and so was
> reverted in f026cfa82f62.
>
> Then a pre-emptive fix for the buggy ASUS firmware was merged in
> 03a1c254975e ("x86, efi: 1:1 pagetable mapping for virtual EFI
> calls") but it causes odd bootup problems on x86-64. So this patch
> revoked again by 11520e5e7c1.
>
> Now Borislav Petkov's "EFI runtime services virtual mapping" is
> merged to EFI 'next' branch. So this patch can be reapplied again.
>
> Signed-off-by: Jan Beulich <jbeu...@suse.com>
> Tested-by: Matt Fleming <matt.f...@intel.com>
> Acked-by: Matthew Garrett <m...@redhat.com>
> Cc: Ingo Molnar <mi...@kernel.org>
> Cc: Peter Zijlstra <a.p.zi...@chello.nl>
> Cc: H. Peter Anvin <h...@zytor.com>
> Signed-off-by: Matt Fleming <matt.f...@intel.com> [added commit history]
> Acked-by: Lee, Chun-Yi <jl...@suse.com>
> ---
> arch/x86/mm/pageattr.c | 10 ++++++----
> arch/x86/platform/efi/efi.c | 35 +++++++++++------------------------
> include/linux/efi.h | 2 --
> init/main.c | 8 ++++----
> 4 files changed, 21 insertions(+), 34 deletions(-)

Lee, you can't just simply resend this patch with all the tags - I
haven't tested this version with any recent changes and I'm pretty sure
Matthew isn't going to Ack it.

Do you know if anyone has tested this patch with Borislav's recent
changes?

--
Matt Fleming, Intel Open Source Technology Center

joeyli

unread,
Dec 19, 2013, 8:40:02 AM12/19/13
to
於 四,2013-12-19 於 10:49 +0000,Matt Fleming 提到:
I am very sorry for I didn't remove those tags before send it. I will
remove it when send second version.

>
> Do you know if anyone has tested this patch with Borislav's recent
> changes?
>

I tested Borislav's patch set on a issue BIOS and make sure it works,
but I have no chance to test this patch with it. I will find a time to
test it.


Thanks a lot!
Joey Lee

Matt Fleming

unread,
Dec 19, 2013, 9:10:04 AM12/19/13
to
On Thu, 19 Dec, at 03:51:47PM, Lee, Chun-Yi wrote:
> UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
> supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.
>
> This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
> checks efi_enabled flag and efi-rtc driver should enabled.
>
> Cc: Matt Fleming <matt.f...@intel.com>
> Cc: H. Peter Anvin <h...@zytor.com>
> Cc: Matthew Garrett <matthew...@nebula.com>
> Cc: Thomas Gleixner <tg...@linutronix.de>
> Cc: Ingo Molnar <mi...@redhat.com>
> Cc: Jan Beulich <JBeu...@suse.com>
> Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
> ---
> arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
> drivers/rtc/Kconfig | 2 +-
> 2 files changed, 18 insertions(+), 1 deletions(-)

This patch needs to be justified. Enabling the EFI runtime *Time
functions just because they're available isn't good enough. We need to
know why this patch improves things, what use case does it solve?

The general attitude has been that we want to invoke the runtime
services less, not more, due to the huge variety of runtime
implementation bugs.

--
Matt Fleming, Intel Open Source Technology Center

H. Peter Anvin

unread,
Dec 19, 2013, 9:40:02 AM12/19/13
to
Where did you find a platform with "no CMOS" set and a PNP RTC? I find the expect behavior in that case to be quite ambiguous and it is not at all clear to me that what you have here is the right thing.
Sent from my mobile phone. Please pardon brevity and lack of formatting.

H. Peter Anvin

unread,
Dec 19, 2013, 10:10:02 AM12/19/13
to
On 12/18/2013 11:43 PM, Lee, Chun-Yi wrote:
> This patchset add the timezone support of ACPI TAD and EFI TIME, it
> also add codes for adjusting system time base on the timezone value
> from EFI TIME services when system boot.

EFI time is something we used to support and ripped out, because it
doesn't work well, *at all*.

I am hyper-skeptical to reintroducing them, and *definitely* will veto
reintroducing them on a system which has PNP0B0x devices present.

-hpa

H. Peter Anvin

unread,
Dec 19, 2013, 10:30:02 AM12/19/13
to
On 12/18/2013 11:51 PM, Lee, Chun-Yi wrote:
> This patch add the driver of Time and Alarm Device in ACPI 5.0.
> Currently it only implemented get/set time functions and grab
> the capabilities of device when driver initial.
>
> This driver also register rtc-acpitad platform device for RTC ACPITAD
> stub driver using.
>
> Signed-off-by: Lee, Chun-Yi <jl...@suse.com>

What platform do you have that has TAD support? I am wondering how this
was tested.

-hpa

Alessandro Zummo

unread,
Dec 19, 2013, 11:20:01 AM12/19/13
to
On Thu, 19 Dec 2013 06:59:55 -0800
"H. Peter Anvin" <h...@zytor.com> wrote:

> EFI time is something we used to support and ripped out, because it
> doesn't work well, *at all*.
>
> I am hyper-skeptical to reintroducing them, and *definitely* will veto
> reintroducing them on a system which has PNP0B0x devices present.

Definitely agree.

--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

joeyli

unread,
Dec 19, 2013, 11:00:01 PM12/19/13
to
Hi hpa,

於 四,2013-12-19 於 06:38 -0800,H. Peter Anvin 提到:
> Where did you find a platform with "no CMOS" set and a PNP RTC? I find the expect behavior in that case to be quite ambiguous and it is not at all clear to me that what you have here is the right thing.

Actually there doesn't have the box both with "No CMOS" and PNP device.
I choice to totally block rtc-cmos driver when "No CMOS RTC" because the
definition in ACPI spec:

CMOS RTC Not Present

If set, indicates that the CMOS RTC is either not implemented, or
does not exist at the legacy addresses. OSPM uses the Control
Method Time and Alarm Namespace device instead.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


It suggest us using ACPI TAD interface when this flag present. But, I
agreed your point for this is ambiguous due to ACPI spec didn't clear
define the relationship between PNP0B0x.

Maybe we can do more detail check in cmos_init when "No CMOS RTC" set:
+ check if have ACPI TAD device, then block rtc-cmos
+ check if no ACPI TAD device, but have PNP0B0x, then we use PNP0b0x.

>
> "Lee, Chun-Yi" <joeyli...@gmail.com> wrote:
> >We should not acess CMOS address when CMOS RTC Not Present bit set in
> >FADT. The ee5872be patch didn't avoid rtc-cmos driver loaded when
> >system support
> >ACPI PNP PNP0B0* devices.
> >So this patch block the registion of rtc-cmos driver to avoid
> >user space access RTC through CMOS interface.
> >
> >Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
> >---
> > arch/x86/kernel/rtc.c | 20 ++++++++++++++++----
> > drivers/rtc/rtc-cmos.c | 9 +++++++++
> > 2 files changed, 25 insertions(+), 4 deletions(-)
> >
...
> >--- a/drivers/rtc/rtc-cmos.c
> >+++ b/drivers/rtc/rtc-cmos.c
> >@@ -28,6 +28,9 @@
> > * interrupts disabled, holding the global rtc_lock, to exclude those
> > * other drivers and utilities on correctly configured systems.
> > */
> >+
> >+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> >+
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> > #include <linux/init.h>
> >@@ -1144,6 +1147,12 @@ static int __init cmos_init(void)
> > {
> > int retval = 0;
> >
> >+ if (acpi_gbl_FADT.header.revision >= 5 &&
> >+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
> >+ pr_info("ACPI CMOS RTC Not Present detected - not loading\n");
> >+ return 0;
> >+ }
> >+
> > #ifdef CONFIG_PNP
> > retval = pnp_register_driver(&cmos_pnp_driver);
> > if (retval == 0)
>

Thanks a lot!
Joey Lee


joeyli

unread,
Dec 19, 2013, 11:10:01 PM12/19/13
to
於 四,2013-12-19 於 06:59 -0800,H. Peter Anvin 提到:
> On 12/18/2013 11:43 PM, Lee, Chun-Yi wrote:
> > This patchset add the timezone support of ACPI TAD and EFI TIME, it
> > also add codes for adjusting system time base on the timezone value
> > from EFI TIME services when system boot.
>
> EFI time is something we used to support and ripped out, because it
> doesn't work well, *at all*.
>
> I am hyper-skeptical to reintroducing them, and *definitely* will veto
> reintroducing them on a system which has PNP0B0x devices present.
>
> -hpa
>

Then that means the priority of PNP0B0x is higher then "CMOS RTC Not
Present" flag. ACPI spec doesn't have clear definition on this.

I look forward to Borislav's "EFI runtime mapping" to fix the physical
address accessing issue of EFI time service on x86_64 machines. I tested
his patches on a issue machine and it works for walk around BIOS bug.

Can we use EFI time services on x86_64 after Borislav's patches accepted
to mainline?


Thanks a lot!
Joey Lee

H. Peter Anvin

unread,
Dec 19, 2013, 11:30:01 PM12/19/13
to
On 12/19/2013 07:54 PM, joeyli wrote:
> Hi hpa,
>
> 於 四,2013-12-19 於 06:38 -0800,H. Peter Anvin 提到:
>> Where did you find a platform with "no CMOS" set and a PNP RTC? I find the expect behavior in that case to be quite ambiguous and it is not at all clear to me that what you have here is the right thing.
>
> Actually there doesn't have the box both with "No CMOS" and PNP device.
> I choice to totally block rtc-cmos driver when "No CMOS RTC" because the
> definition in ACPI spec:
>
> CMOS RTC Not Present
>
> If set, indicates that the CMOS RTC is either not implemented, or
> does not exist at the legacy addresses. OSPM uses the Control
> Method Time and Alarm Namespace device instead.
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> It suggest us using ACPI TAD interface when this flag present. But, I
> agreed your point for this is ambiguous due to ACPI spec didn't clear
> define the relationship between PNP0B0x.
>
> Maybe we can do more detail check in cmos_init when "No CMOS RTC" set:
> + check if have ACPI TAD device, then block rtc-cmos
> + check if no ACPI TAD device, but have PNP0B0x, then we use PNP0b0x.
>

I think the only thing we should use that bit for is to inhibit the
last-resort probing of I/O ports 0x70-0x73... if at all.

-hpa

H. Peter Anvin

unread,
Dec 19, 2013, 11:30:02 PM12/19/13
to
On 12/19/2013 08:05 PM, joeyli wrote:
>
> Then that means the priority of PNP0B0x is higher then "CMOS RTC Not
> Present" flag. ACPI spec doesn't have clear definition on this.
>

According to the Microsoft requirements documents, such a platform is
broken and shouldn't exist.

> I look forward to Borislav's "EFI runtime mapping" to fix the physical
> address accessing issue of EFI time service on x86_64 machines. I tested
> his patches on a issue machine and it works for walk around BIOS bug.
>
> Can we use EFI time services on x86_64 after Borislav's patches accepted
> to mainline?
>

No.

-hpa

joeyli

unread,
Dec 19, 2013, 11:30:02 PM12/19/13
to
於 四,2013-12-19 於 14:09 +0000,Matt Fleming 提到:
> On Thu, 19 Dec, at 03:51:47PM, Lee, Chun-Yi wrote:
> > UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
> > supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.
> >
> > This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
> > checks efi_enabled flag and efi-rtc driver should enabled.
> >
> > Cc: Matt Fleming <matt.f...@intel.com>
> > Cc: H. Peter Anvin <h...@zytor.com>
> > Cc: Matthew Garrett <matthew...@nebula.com>
> > Cc: Thomas Gleixner <tg...@linutronix.de>
> > Cc: Ingo Molnar <mi...@redhat.com>
> > Cc: Jan Beulich <JBeu...@suse.com>
> > Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
> > ---
> > arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
> > drivers/rtc/Kconfig | 2 +-
> > 2 files changed, 18 insertions(+), 1 deletions(-)
>
> This patch needs to be justified. Enabling the EFI runtime *Time
> functions just because they're available isn't good enough. We need to
> know why this patch improves things, what use case does it solve?
>

The main purpose of enable efi-rtc driver is providing interface to
userspace access timezone field in EFI time services.

Currently have two BIOS interfaces provided the timezone field accessing
ability, ACPI TAD and EFI. I hope can enable them on x86_64 machines.

> The general attitude has been that we want to invoke the runtime
> services less, not more, due to the huge variety of runtime
> implementation bugs.
>

I agreed, but userspace application should not be too often to access
RTC. Maybe only when system boot and set timezone.


Thanks a lot!
Joey Lee

H. Peter Anvin

unread,
Dec 19, 2013, 11:40:02 PM12/19/13
to
On 12/19/2013 08:24 PM, joeyli wrote:
>
> I agreed, but userspace application should not be too often to access
> RTC. Maybe only when system boot and set timezone.
>

This is, quite frankly, an idiotic argument. Userspace doesn't access
the RTC, the kernel does, and if the underlying implementation is broken
it doesn't matter if it is accessed one time or a thousand. For the
record, in most cases the RTC gets accessed about every 15 minutes to
update the time based on network time.

-hpa

joeyli

unread,
Dec 20, 2013, 12:50:01 AM12/20/13
to
於 四,2013-12-19 於 20:22 -0800,H. Peter Anvin 提到:
> On 12/19/2013 08:05 PM, joeyli wrote:
> >
> > Then that means the priority of PNP0B0x is higher then "CMOS RTC Not
> > Present" flag. ACPI spec doesn't have clear definition on this.
> >
>
> According to the Microsoft requirements documents, such a platform is
> broken and shouldn't exist.

OK~~

>
> > I look forward to Borislav's "EFI runtime mapping" to fix the physical
> > address accessing issue of EFI time service on x86_64 machines. I tested
> > his patches on a issue machine and it works for walk around BIOS bug.
> >
> > Can we use EFI time services on x86_64 after Borislav's patches accepted
> > to mainline?
> >
>
> No.
>
> -hpa

If don't use EFI time, then the first priority is using ACPI TAD if it
present. Due to ACPI TAD is a generic acpi device that's need OS parsing
DSDT table before set system time.

Either move DSDT parser from subsystem initial stage to start_kernel or
move timekeeping initial to after DSDT be parsed. Which one you think is
more possible and risk less? Then I will try that way.


Thanks a lot!
Joey Lee

joeyli

unread,
Dec 20, 2013, 12:50:01 AM12/20/13
to
於 四,2013-12-19 於 07:22 -0800,H. Peter Anvin 提到:
> On 12/18/2013 11:51 PM, Lee, Chun-Yi wrote:
> > This patch add the driver of Time and Alarm Device in ACPI 5.0.
> > Currently it only implemented get/set time functions and grab
> > the capabilities of device when driver initial.
> >
> > This driver also register rtc-acpitad platform device for RTC ACPITAD
> > stub driver using.
> >
> > Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
>
> What platform do you have that has TAD support? I am wondering how this
> was tested.
>
> -hpa
>

It's a testing platform that's only support get/set time functions of
ACPI TAD.


Thanks
Joey Lee

Borislav Petkov

unread,
Dec 20, 2013, 5:40:01 AM12/20/13
to
On Thu, Dec 19, 2013 at 08:30:48PM -0800, H. Peter Anvin wrote:
> On 12/19/2013 08:24 PM, joeyli wrote:
> > I agreed, but userspace application should not be too often to access
> > RTC. Maybe only when system boot and set timezone.
>
> This is, quite frankly, an idiotic argument.

TBH, I've been struggling with the question too - and it might even be a
stupid question - but what is that absolute need to be able to get the
TZ in userspace? Why should I care?

Can we get some use cases for stupid people like me please?

Thanks.

--
Regards/Gruss,
Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

Thomas Renninger

unread,
Dec 20, 2013, 6:00:01 AM12/20/13
to
On Thursday, December 19, 2013 08:22:08 PM H. Peter Anvin wrote:
> On 12/19/2013 08:05 PM, joeyli wrote:
> > Then that means the priority of PNP0B0x is higher then "CMOS RTC Not
> > Present" flag. ACPI spec doesn't have clear definition on this.
>
> According to the Microsoft requirements documents, such a platform is
> broken and shouldn't exist.

Is this a public document?
Probably not but if, a pointer in this thread would help.
Does Microsoft mention ACPI Time and Alarm Device interface in
such a document already?

I expect that future platforms will make more and more use of the
ACPI/EFI specified time functions.

It's probably up to Microsoft requiring this at some point of time,
then this stuff will work reliable, possibly others will not anymore.

Given the fact that there are machines which implement this interface
already and it is likely that more and more will, IMHO a first
implementation of this stuff in the kernel, even there are broken BIOSes
around, makes *a lot of* sense.

I suggest a whitelist, however it looks like:
-> platforms which do not have a PNP0B0x device?
-> dmi whitelist working platforms
-> Extend it later with conditions where we know things work as expected

best also introduce an acpi=enable/disable_tad (or simlar) boot param which
overrides white/blacklisting.
This will allow users to make use of this which may otherwise run into
problems and it will also allow easier testing and feedback
how reliable latest BIOS implementations are.


Thomas

One Thousand Gnomes

unread,
Dec 20, 2013, 6:40:02 AM12/20/13
to
On Thu, 19 Dec 2013 21:32:19 +0800
joeyli <jl...@suse.com> wrote:

> 於 四,2013-12-19 於 10:49 +0000,Matt Fleming 提到:
> > On Thu, 19 Dec, at 06:20:16PM, Lee, Chun-Yi wrote:
> > > From: Jan Beulich <JBeu...@suse.com>
> > >
> > > Other than ix86, x86-64 on EFI so far didn't set the
> > > {g,s}et_wallclock accessors to the EFI routines, thus
> > > incorrectly using raw RTC accesses instead.

Probably wise.

Before anyone reverts that please make sure it also works on EFI32,
because right now we crash and burn on multiple EFI32 tablets querying
the time.

Alan

Matthew Garrett

unread,
Dec 20, 2013, 10:20:01 AM12/20/13
to
On Fri, 2013-12-20 at 11:37 +0100, Borislav Petkov wrote:
> On Thu, Dec 19, 2013 at 08:30:48PM -0800, H. Peter Anvin wrote:
> > On 12/19/2013 08:24 PM, joeyli wrote:
> > > I agreed, but userspace application should not be too often to access
> > > RTC. Maybe only when system boot and set timezone.
> >
> > This is, quite frankly, an idiotic argument.
>
> TBH, I've been struggling with the question too - and it might even be a
> stupid question - but what is that absolute need to be able to get the
> TZ in userspace? Why should I care?
>
> Can we get some use cases for stupid people like me please?

Dual-boot environments will tend to have the RTC in local time, not UTC.
That means that userspace has to reprogram the clock over daylight
savings changes, and to do that it must know whether another OS has
already done so.

--
Matthew Garrett <matthew...@nebula.com>

Matthew Garrett

unread,
Dec 20, 2013, 10:20:01 AM12/20/13
to
On Thu, 2013-12-19 at 20:22 -0800, H. Peter Anvin wrote:
> On 12/19/2013 08:05 PM, joeyli wrote:
> > Can we use EFI time services on x86_64 after Borislav's patches accepted
> > to mainline?
> >
>
> No.

We will want to use them to (at minimum) obtain the clock timezone.
Using them for general RTC access is less attractive.

--
Matthew Garrett <matthew...@nebula.com>

Matthew Garrett

unread,
Dec 20, 2013, 10:20:02 AM12/20/13
to
On Thu, 2013-12-19 at 15:51 +0800, Lee, Chun-Yi wrote:
> This patch adds 2 new iotrl: RTC_RD_GMTOFF and RTC_SET_GMTOFF to
> rtc_efi support get/set gmt offset that mapping to the GUN's tm_gmtoff
> extension (Seconds east of UTC).

Shouldn't this information also be exported via the rtc-sysfs interface?

--
Matthew Garrett <matthew...@nebula.com>

H. Peter Anvin

unread,
Dec 20, 2013, 11:50:01 AM12/20/13
to
The only real use case for the RTC TZ is compatibility with other operating systems that don't keep the time in UTC, at least as far as I can tell. However, that is a fairly big one, especially in VM deployments.

Borislav Petkov <b...@alien8.de> wrote:
>On Thu, Dec 19, 2013 at 08:30:48PM -0800, H. Peter Anvin wrote:
>> On 12/19/2013 08:24 PM, joeyli wrote:
>> > I agreed, but userspace application should not be too often to
>access
>> > RTC. Maybe only when system boot and set timezone.
>>
>> This is, quite frankly, an idiotic argument.
>
>TBH, I've been struggling with the question too - and it might even be
>a
>stupid question - but what is that absolute need to be able to get the
>TZ in userspace? Why should I care?
>
>Can we get some use cases for stupid people like me please?
>
>Thanks.

--
Sent from my mobile phone. Please pardon brevity and lack of formatting.

Matthew Garrett

unread,
Dec 20, 2013, 12:00:01 PM12/20/13
to
On Fri, 2013-12-20 at 08:57 -0800, H. Peter Anvin wrote:
> But we prefer the TAD for that. The case where the EFI runtime is the only source of that info is problematic as they are known to not work at runtime. We could collect it at boot and then never change it, although you end up in definitional issues between EFI and the hw RTC.

Most shipping UEFI hardware has no TAD.

--
Matthew Garrett <matthew...@nebula.com>

H. Peter Anvin

unread,
Dec 20, 2013, 12:00:02 PM12/20/13
to
But we prefer the TAD for that. The case where the EFI runtime is the only source of that info is problematic as they are known to not work at runtime. We could collect it at boot and then never change it, although you end up in definitional issues between EFI and the hw RTC.

Matthew Garrett <matthew...@nebula.com> wrote:
>On Thu, 2013-12-19 at 20:22 -0800, H. Peter Anvin wrote:
>> On 12/19/2013 08:05 PM, joeyli wrote:
>> > Can we use EFI time services on x86_64 after Borislav's patches
>accepted
>> > to mainline?
>> >
>>
>> No.
>
>We will want to use them to (at minimum) obtain the clock timezone.
>Using them for general RTC access is less attractive.

--
Sent from my mobile phone. Please pardon brevity and lack of formatting.

Matthew Garrett

unread,
Dec 20, 2013, 3:40:02 PM12/20/13
to
On Fri, 2013-12-20 at 12:29 -0800, H. Peter Anvin wrote:
> Yes, but the TZ isn't all that critical, either. It certainly doesn't matter at all for a pure Linux system.

No, but it does matter for a great number of deployed Linux systems.
Dealing with the timezone over DST changes has been a perpetual problem,
and if we can make that work then life will be significantly better.

--
Matthew Garrett <matthew...@nebula.com>

H. Peter Anvin

unread,
Dec 20, 2013, 3:40:02 PM12/20/13
to
Yes, but the TZ isn't all that critical, either. It certainly doesn't matter at all for a pure Linux system.

H. Peter Anvin

unread,
Dec 20, 2013, 4:10:02 PM12/20/13
to
Actually, it doesn't have to reprogram the clock ... it just needs to
know if another OS has already done so. All Linux needs to do is to be
able to derive UTC from whatever the RTC is set to and to be able to
keep it consistent.

-hpa

H. Peter Anvin

unread,
Dec 20, 2013, 4:20:01 PM12/20/13
to
On 12/19/2013 09:38 PM, joeyli wrote:
>
> If don't use EFI time, then the first priority is using ACPI TAD if it
> present. Due to ACPI TAD is a generic acpi device that's need OS parsing
> DSDT table before set system time.
>
> Either move DSDT parser from subsystem initial stage to start_kernel or
> move timekeeping initial to after DSDT be parsed. Which one you think is
> more possible and risk less? Then I will try that way.
>

I discussed the DSDT/SSDT parsing issue with Rafael and he claims it
would require a lot of restructuring. Unfortunately ACPI is at this
point done rather late, as I understand. All of this is a big problem.

-hpa

H. Peter Anvin

unread,
Dec 20, 2013, 4:20:02 PM12/20/13
to
On 12/20/2013 07:16 AM, Matthew Garrett wrote:
> On Thu, 2013-12-19 at 20:22 -0800, H. Peter Anvin wrote:
>> On 12/19/2013 08:05 PM, joeyli wrote:
>>> Can we use EFI time services on x86_64 after Borislav's patches accepted
>>> to mainline?
>>>
>>
>> No.
>
> We will want to use them to (at minimum) obtain the clock timezone.
> Using them for general RTC access is less attractive.
>

One option is to use the EFI runtime call to get and save the clock
timezone before we call ExitBootServices() in the EFI stub. This
doesn't obviate the need for proper handling of the TAD, though,
especially since it is likely that future hardware will not have a RTC
in the current form (it is a way more complex device than is needed,
which wouldn't normally be a problem, but the fact that it has to
operate in the Vbat well makes it a major one.)

H. Peter Anvin

unread,
Dec 20, 2013, 4:20:02 PM12/20/13
to
On 12/20/2013 12:32 PM, Matthew Garrett wrote:
> On Fri, 2013-12-20 at 12:29 -0800, H. Peter Anvin wrote:
>> Yes, but the TZ isn't all that critical, either. It certainly doesn't matter at all for a pure Linux system.
>
> No, but it does matter for a great number of deployed Linux systems.
> Dealing with the timezone over DST changes has been a perpetual problem,
> and if we can make that work then life will be significantly better.
>

And as I pointed out, it can matter a lot for VMs, since the provider
doesn't want to provision the VMs differently for different types of guests.

-hpa

H. Peter Anvin

unread,
Dec 20, 2013, 4:30:01 PM12/20/13
to
On 12/20/2013 01:10 PM, H. Peter Anvin wrote:
> On 12/19/2013 09:38 PM, joeyli wrote:
>>
>> If don't use EFI time, then the first priority is using ACPI TAD if it
>> present. Due to ACPI TAD is a generic acpi device that's need OS parsing
>> DSDT table before set system time.
>>
>> Either move DSDT parser from subsystem initial stage to start_kernel or
>> move timekeeping initial to after DSDT be parsed. Which one you think is
>> more possible and risk less? Then I will try that way.
>>
>
> I discussed the DSDT/SSDT parsing issue with Rafael and he claims it
> would require a lot of restructuring. Unfortunately ACPI is at this
> point done rather late, as I understand. All of this is a big problem.
>

The thing is that we probably need this anyway, because that is how one
detects PNP0B0x devices as well. We do need to get away from blindly
poking ports 0x70-73.

Rafael J. Wysocki

unread,
Dec 20, 2013, 4:40:01 PM12/20/13
to
On Friday, December 20, 2013 01:10:26 PM H. Peter Anvin wrote:
> On 12/19/2013 09:38 PM, joeyli wrote:
> >
> > If don't use EFI time, then the first priority is using ACPI TAD if it
> > present. Due to ACPI TAD is a generic acpi device that's need OS parsing
> > DSDT table before set system time.
> >
> > Either move DSDT parser from subsystem initial stage to start_kernel or
> > move timekeeping initial to after DSDT be parsed. Which one you think is
> > more possible and risk less? Then I will try that way.
> >
>
> I discussed the DSDT/SSDT parsing issue with Rafael and he claims it
> would require a lot of restructuring. Unfortunately ACPI is at this
> point done rather late, as I understand. All of this is a big problem.

My understanding, however, is that to use the TAD, we don't actually need to
create a struct acpi_device for it. We just need a handle to the ACPICA
object which can be found using acpi_get_devices() as soon as the namespace
has been extracted from the DSDT and friends. That in turn happens in
acpi_early_init(), which is called from start_kernel() right before
efi_late_init().

Is that early enough?

Rafael

H. Peter Anvin

unread,
Dec 20, 2013, 4:50:02 PM12/20/13
to
On 12/20/2013 01:45 PM, Rafael J. Wysocki wrote:
>
> My understanding, however, is that to use the TAD, we don't actually need to
> create a struct acpi_device for it. We just need a handle to the ACPICA
> object which can be found using acpi_get_devices() as soon as the namespace
> has been extracted from the DSDT and friends. That in turn happens in
> acpi_early_init(), which is called from start_kernel() right before
> efi_late_init().
>
> Is that early enough?
>

Possibly... will have to investigate. I would also ask if the same
applies to the PNP0B0x devices -- all we really need there is the
presence/absence indication and the I/O port base.

-hpa

Matt Fleming

unread,
Dec 20, 2013, 5:00:01 PM12/20/13
to
On Fri, 20 Dec, at 01:43:38PM, H. Peter Anvin wrote:
>
> Possibly... will have to investigate. I would also ask if the same
> applies to the PNP0B0x devices -- all we really need there is the
> presence/absence indication and the I/O port base.

It's worth noting that efi_late_init() exists solely because the ACPI
infrastructure to find the BGRT table isn't available any earlier.

If we could setup ACPI earlier that would be awesome.

--
Matt Fleming, Intel Open Source Technology Center

Rafael J. Wysocki

unread,
Dec 20, 2013, 5:10:01 PM12/20/13
to
On Friday, December 20, 2013 09:50:56 PM Matt Fleming wrote:
> On Fri, 20 Dec, at 01:43:38PM, H. Peter Anvin wrote:
> >
> > Possibly... will have to investigate. I would also ask if the same
> > applies to the PNP0B0x devices -- all we really need there is the
> > presence/absence indication and the I/O port base.
>
> It's worth noting that efi_late_init() exists solely because the ACPI
> infrastructure to find the BGRT table isn't available any earlier.
>
> If we could setup ACPI earlier that would be awesome.

I'm not sure 100%, but I *think* we need to do that with interrupts enabled.
At least after mm_init(), because it relies on things initialized there if I
remember correctly.

From what I can tell at the moment, it might be possible to move it before
efi_enter_virtual_mode() if that would help.

Thanks,
Rafael

H. Peter Anvin

unread,
Dec 20, 2013, 5:20:02 PM12/20/13
to
On 12/20/2013 02:53 AM, Thomas Renninger wrote:
> On Thursday, December 19, 2013 08:22:08 PM H. Peter Anvin wrote:
>> On 12/19/2013 08:05 PM, joeyli wrote:
>>> Then that means the priority of PNP0B0x is higher then "CMOS RTC Not
>>> Present" flag. ACPI spec doesn't have clear definition on this.
>>
>> According to the Microsoft requirements documents, such a platform is
>> broken and shouldn't exist.
>
> Is this a public document?
> Probably not but if, a pointer in this thread would help.
> Does Microsoft mention ACPI Time and Alarm Device interface in
> such a document already?
>

This is the document... I can't remember who sent me the link to it:

http://goo.gl/R7S9Mk

-hpa

joeyli

unread,
Dec 20, 2013, 8:30:01 PM12/20/13
to
於 五,2013-12-20 於 13:04 -0800,H. Peter Anvin 提到:
> On 12/20/2013 07:14 AM, Matthew Garrett wrote:
> > On Fri, 2013-12-20 at 11:37 +0100, Borislav Petkov wrote:
> >> On Thu, Dec 19, 2013 at 08:30:48PM -0800, H. Peter Anvin wrote:
> >>> On 12/19/2013 08:24 PM, joeyli wrote:
> >>>> I agreed, but userspace application should not be too often to access
> >>>> RTC. Maybe only when system boot and set timezone.
> >>>
> >>> This is, quite frankly, an idiotic argument.
> >>
> >> TBH, I've been struggling with the question too - and it might even be a
> >> stupid question - but what is that absolute need to be able to get the
> >> TZ in userspace? Why should I care?
> >>
> >> Can we get some use cases for stupid people like me please?
> >
> > Dual-boot environments will tend to have the RTC in local time, not UTC.
> > That means that userspace has to reprogram the clock over daylight
> > savings changes, and to do that it must know whether another OS has
> > already done so.
> >
>
> Actually, it doesn't have to reprogram the clock ... it just needs to
> know if another OS has already done so. All Linux needs to do is to be
> able to derive UTC from whatever the RTC is set to and to be able to
> keep it consistent.
>
> -hpa
>
>

It's dependent on a right boot initial priority of distribution.
Here have a discussion of adjusting system clock by TZ (from ACPI or
UEFI):
Discussion on BIOS/CMOS/UEFI clock in local time
http://www.spinics.net/lists/util-linux-ng/msg07639.html

and,
from Ted Ts'o in the mail thread
https://lkml.org/lkml/2008/1/8/195


If kernel use the TZ field from ACPI TAD or EFI to adjust system
clock when booting, then it can avoid buggy distributions adjust
system clock AFTER e2fsck is run.

Using ACPI TAD should after DSDT parsing in subsystem initial stage,
so I choice EFI time services before we can move DSDT parser to
start_kernel().


Thanks a lot!
Joey Lee

H. Peter Anvin

unread,
Dec 20, 2013, 9:00:02 PM12/20/13
to
On 12/20/2013 05:24 PM, joeyli wrote:
> 於 五,2013-12-20 於 13:04 -0800,H. Peter Anvin 提到:
>>
>> Actually, it doesn't have to reprogram the clock ... it just needs to
>> know if another OS has already done so. All Linux needs to do is to be
>> able to derive UTC from whatever the RTC is set to and to be able to
>> keep it consistent.
>>
>
> It's dependent on a right boot initial priority of distribution.

-ENOPARSE

> Here have a discussion of adjusting system clock by TZ (from ACPI or
> UEFI):
> Discussion on BIOS/CMOS/UEFI clock in local time
> http://www.spinics.net/lists/util-linux-ng/msg07639.html
>
> and,
> from Ted Ts'o in the mail thread
> https://lkml.org/lkml/2008/1/8/195
>
> If kernel use the TZ field from ACPI TAD or EFI to adjust system
> clock when booting, then it can avoid buggy distributions adjust
> system clock AFTER e2fsck is run.
>
> Using ACPI TAD should after DSDT parsing in subsystem initial stage,
> so I choice EFI time services before we can move DSDT parser to
> start_kernel().
>

Yes, of course. That is irrelevant to needing to reprogram the clock,
though.

My argument is very simple: if we have to rely on EFI, we can get the
offset in the boot stub before ExitBootServices(), and then simply never
change it. That way we still pick up if another operating system has
changed it, and it will still reflect the proper UTC time.

-hpa

joeyli

unread,
Dec 20, 2013, 9:50:01 PM12/20/13
to
於 五,2013-12-20 於 22:45 +0100,Rafael J. Wysocki 提到:
> On Friday, December 20, 2013 01:10:26 PM H. Peter Anvin wrote:
> > On 12/19/2013 09:38 PM, joeyli wrote:
> > >
> > > If don't use EFI time, then the first priority is using ACPI TAD if it
> > > present. Due to ACPI TAD is a generic acpi device that's need OS parsing
> > > DSDT table before set system time.
> > >
> > > Either move DSDT parser from subsystem initial stage to start_kernel or
> > > move timekeeping initial to after DSDT be parsed. Which one you think is
> > > more possible and risk less? Then I will try that way.
> > >
> >
> > I discussed the DSDT/SSDT parsing issue with Rafael and he claims it
> > would require a lot of restructuring. Unfortunately ACPI is at this
> > point done rather late, as I understand. All of this is a big problem.
>
> My understanding, however, is that to use the TAD, we don't actually need to
> create a struct acpi_device for it. We just need a handle to the ACPICA
> object which can be found using acpi_get_devices() as soon as the namespace
> has been extracted from the DSDT and friends. That in turn happens in
> acpi_early_init(), which is called from start_kernel() right before
> efi_late_init().
>
> Is that early enough?
>
> Rafael
>

Thanks for your good suggestion, then I have a direction on using ACPI
TAD earlier.

Joey Lee

joeyli

unread,
Dec 20, 2013, 11:00:02 PM12/20/13
to
於 五,2013-12-20 於 15:11 +0000,Matthew Garrett 提到:
> On Thu, 2013-12-19 at 15:51 +0800, Lee, Chun-Yi wrote:
> > This patch adds 2 new iotrl: RTC_RD_GMTOFF and RTC_SET_GMTOFF to
> > rtc_efi support get/set gmt offset that mapping to the GUN's tm_gmtoff
> > extension (Seconds east of UTC).
>
> Shouldn't this information also be exported via the rtc-sysfs interface?
>

Thanks for your suggestion, I will also export it to rtc-sysfs.

Matt Fleming

unread,
Dec 21, 2013, 7:30:01 AM12/21/13
to
On Fri, 20 Dec, at 11:18:56PM, Rafael J. Wysocki wrote:
>
> I'm not sure 100%, but I *think* we need to do that with interrupts enabled.
> At least after mm_init(), because it relies on things initialized there if I
> remember correctly.
>
> From what I can tell at the moment, it might be possible to move it before
> efi_enter_virtual_mode() if that would help.

Actually yeah, that would be super useful, and I think we'd be able to
get rid of the whole efi_late_init() stuff because we'd no longer need
to hang on to the EFI_BOOT_SERVICES* regions after
SetVirtualAddressMap().

--
Matt Fleming, Intel Open Source Technology Center

joeyli

unread,
Dec 21, 2013, 10:40:02 AM12/21/13
to
於 五,2013-12-20 於 17:51 -0800,H. Peter Anvin 提到:
OK, I will add this part to next version for the UEFI system doesn't
have ACPI TAD.


Thanks for your suggestion!
Joey Lee

H. Peter Anvin

unread,
Dec 23, 2013, 6:30:02 PM12/23/13
to
On 12/20/2013 03:29 AM, One Thousand Gnomes wrote:
> On Thu, 19 Dec 2013 21:32:19 +0800
> joeyli <jl...@suse.com> wrote:
>
>> 於 四,2013-12-19 於 10:49 +0000,Matt Fleming 提到:
>>> On Thu, 19 Dec, at 06:20:16PM, Lee, Chun-Yi wrote:
>>>> From: Jan Beulich <JBeu...@suse.com>
>>>>
>>>> Other than ix86, x86-64 on EFI so far didn't set the
>>>> {g,s}et_wallclock accessors to the EFI routines, thus
>>>> incorrectly using raw RTC accesses instead.
>
> Probably wise.
>
> Before anyone reverts that please make sure it also works on EFI32,
> because right now we crash and burn on multiple EFI32 tablets querying
> the time.
>

Quite -- we in fact did just the opposite (checkin 04bf9ba720fc).

-hpa

H. Peter Anvin

unread,
Dec 31, 2013, 7:50:01 PM12/31/13
to
On 12/19/2013 09:41 PM, joeyli wrote:
>>
>> What platform do you have that has TAD support? I am wondering how this
>> was tested.
>>
>
> It's a testing platform that's only support get/set time functions of
> ACPI TAD.
>

It would be really, really good to get this into Qemu (either SeaBIOS or
OVMF, or ideally both) so we can have anyone test.

Lan Tianyu

unread,
Jan 2, 2014, 3:10:02 AM1/2/14
to
2013/12/19 Lee, Chun-Yi <joeyli...@gmail.com>:
> This patch add the driver of Time and Alarm Device in ACPI 5.0.
> Currently it only implemented get/set time functions and grab
> the capabilities of device when driver initial.
>
> This driver also register rtc-acpitad platform device for RTC ACPITAD
> stub driver using.
>
> Signed-off-by: Lee, Chun-Yi <jl...@suse.com>
> ---
> drivers/acpi/Makefile | 3 +
> drivers/acpi/acpi_tad.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++
> drivers/acpi/bus.c | 3 +
> drivers/acpi/internal.h | 5 ++
> include/linux/acpi.h | 31 ++++++++
> 5 files changed, 218 insertions(+), 0 deletions(-)
> create mode 100644 drivers/acpi/acpi_tad.c
>
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 0331f91..d250b15 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -50,6 +50,9 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
> ifdef CONFIG_ACPI_VIDEO
> acpi-y += video_detect.o
> endif
> +ifdef CONFIG_X86
> +acpi-y += acpi_tad.o
> +endif
>
> # These are (potentially) separate modules
>
> diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
> new file mode 100644
> index 0000000..c2200f3
> --- /dev/null
> +++ b/drivers/acpi/acpi_tad.c
> @@ -0,0 +1,176 @@
> +/* rtc.c - ACPI 5.0 Time and Alarm Driver
> + *
> + * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
> + * Written by Lee, Chun-Yi (jl...@suse.com)
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <acpi/acpi_drivers.h>
> +
> +#include <asm/time.h>
> +
> +#define ACPI_TIME_ALARM_NAME "Time and Alarm"
> +ACPI_MODULE_NAME(ACPI_TIME_ALARM_NAME);
> +#define ACPI_TIME_ALARM_CLASS "time_alarm"
> +
> +static const struct acpi_device_id time_alarm_ids[] = {
> + {"ACPI000E", 0},
> + {"", 0},
> +};
> +MODULE_DEVICE_TABLE(acpi, time_alarm_ids);
> +
> +static struct platform_device rtc_acpitad_dev = {
> + .name = "rtc-acpitad",
> + .id = -1,
> +};
> +
> +static struct acpi_device *acpi_tad_dev;
> +static unsigned long long cap;
> +
> +int acpi_read_time(struct acpi_time *output)
> +{
> + unsigned long flags;
> + struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
> + union acpi_object *obj;
> + struct acpi_time *acpit;
> + acpi_status status;
> +
> + if (!acpi_tad_dev)
> + return -ENODEV;
> +
> + if (!(cap & TAD_CAP_GETSETTIME))
> + return -EINVAL;
> +
> + if (!output)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&rtc_lock, flags);
> + status = acpi_evaluate_object(acpi_tad_dev->handle, "_GRT", NULL, &result);
> + spin_unlock_irqrestore(&rtc_lock, flags);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GRT"));
> + return -ENODEV;
> + }
> +
> + obj = result.pointer;
> + if (!obj ||
> + obj->type != ACPI_TYPE_BUFFER ||
> + obj->buffer.length > sizeof(struct acpi_time) ||
> + obj->buffer.length < offsetof(struct acpi_time, pad2)) {
> + dev_err(&acpi_tad_dev->dev, ACPI_TIME_ALARM_NAME
> + " Invalid _GRT data\n");
> + return -EINVAL;
> + }
> +
> + acpit = (struct acpi_time *) obj->buffer.pointer;
> + if (acpit) {
> + output->year = acpit->year;
> + output->month = acpit->month;
> + output->day = acpit->day;
> + output->hour = acpit->hour;
> + output->minute = acpit->minute;
> + output->second = acpit->second;
> + output->milliseconds = acpit->milliseconds;
> + output->timezone = acpit->timezone;
> + output->daylight = acpit->daylight;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(acpi_read_time);
> +
> +int acpi_set_time(struct acpi_time *acpit)
> +{
> + unsigned long flags;
> + struct acpi_object_list input;
> + union acpi_object params[1];
> + unsigned long long output;
> + acpi_status status;
> +
> + if (!acpi_tad_dev)
> + return -ENODEV;
> +
> + if (!(cap & TAD_CAP_GETSETTIME))
> + return -EINVAL;
> +
> + if (!acpit)
> + return -EINVAL;
> +
> + input.count = 1;
> + input.pointer = params;
> + params[0].type = ACPI_TYPE_BUFFER;
> + params[0].buffer.length = sizeof(struct acpi_time);
> + params[0].buffer.pointer = (void *) acpit;
> +
> + spin_lock_irqsave(&rtc_lock, flags);
> + status = acpi_evaluate_integer(acpi_tad_dev->handle, "_SRT", &input, &output);
> + spin_unlock_irqrestore(&rtc_lock, flags);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRT"));
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(acpi_set_time);
> +
> +int acpi_tad_get_capability(unsigned long *output)
> +{
> + if (!acpi_tad_dev)
> + return -ENODEV;
> +
> + *output = cap;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(acpi_tad_get_capability);
> +
> +static int acpi_time_alarm_add(struct acpi_device *device)
> +{
> + acpi_status status;
> +
> + if (!device)
> + return -EINVAL;
> +
> + acpi_tad_dev = device;
> +
> + /* evaluate _GCP */
> + status = acpi_evaluate_integer(device->handle, "_GCP", NULL, &cap);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GCP"));
> + return -ENODEV;
> + }
> +
> + if (!(cap & TAD_CAP_GETSETTIME))
> + pr_warn(FW_INFO "Get/Set real time features not available.\n");
> +
> + if (platform_device_register(&rtc_acpitad_dev) < 0)
> + pr_err("Unable to register rtc-acpitad device\n");
> +
> + return 0;
> +}
> +
> +static struct acpi_driver acpi_time_alarm_driver = {
> + .name = "time_and_alarm",
> + .class = ACPI_TIME_ALARM_CLASS,
> + .ids = time_alarm_ids,
> + .ops = {
> + .add = acpi_time_alarm_add,
> + },
> +};
> +
> +int __init acpi_tad_init(void)
> +{
> + int result = 0;
> +
> + result = acpi_bus_register_driver(&acpi_time_alarm_driver);
> + if (result < 0)
> + return -ENODEV;
> +
> + return result;
> +}
> diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
> index bba9b72..3f7a075 100644
> --- a/drivers/acpi/bus.c
> +++ b/drivers/acpi/bus.c
> @@ -689,6 +689,9 @@ static int __init acpi_init(void)
> pci_mmcfg_late_init();
> acpi_scan_init();
> acpi_ec_init();
> +#ifdef CONFIG_X86
> + acpi_tad_init();
> +#endif

Why calling acpi_tad_init() directly here rather than using module_initcall?
Is there dependency?


> acpi_debugfs_init();
> acpi_sleep_proc_init();
> acpi_wakeup_device_init();
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index a29739c..9cfe589 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -134,6 +134,11 @@ void acpi_ec_block_transactions(void);
> void acpi_ec_unblock_transactions(void);
> void acpi_ec_unblock_transactions_early(void);
>
> +/* --------------------------------------------------------------------------
> + Time and Alarm Device
> + -------------------------------------------------------------------------- */
> +int acpi_tad_init(void);
> +
> /*--------------------------------------------------------------------------
> Suspend/Resume
> -------------------------------------------------------------------------- */
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index d9099b1..c8dc104 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -173,6 +173,37 @@ extern int ec_transaction(u8 command,
> u8 *rdata, unsigned rdata_len);
> extern acpi_handle ec_get_handle(void);
>
> +/*
> + * Time and Alarm device capability flags
> + */
> +#define TAD_CAP_ACWAKE (1<<0)
> +#define TAD_CAP_DCWAKE (1<<1)
> +#define TAD_CAP_GETSETTIME (1<<2)
> +#define TAD_CAP_ACCURACY (1<<3)
> +
> +#define ACPI_TIME_AFFECTED_BY_DAYLIGHT (1<<0)
> +#define ACPI_TIME_ADJUSTED_FOR_DAYLIGHT (1<<1)
> +#define ACPI_ISDST (ACPI_TIME_AFFECTED_BY_DAYLIGHT|ACPI_TIME_ADJUSTED_FOR_DAYLIGHT)
> +#define ACPI_UNSPECIFIED_TIMEZONE 2047
> +
> +struct acpi_time {
> + u16 year;
> + u8 month;
> + u8 day;
> + u8 hour;
> + u8 minute;
> + u8 second;
> + u8 pad1;
> + u16 milliseconds;
> + s16 timezone;
> + u8 daylight;
> + u8 pad2[3];
> +};
> +
> +extern int acpi_read_time(struct acpi_time *acpit);
> +extern int acpi_set_time(struct acpi_time *acpit);
> +extern int acpi_tad_get_capability(unsigned long *output);
> +
> #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
>
> typedef void (*wmi_notify_handler) (u32 value, void *context);
> --
> 1.6.4.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to majo...@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Best regards
Tianyu Lan

joeyli

unread,
Jan 6, 2014, 4:10:01 AM1/6/14
to
於 二,2013-12-31 於 16:42 -0800,H. Peter Anvin 提到:
> On 12/19/2013 09:41 PM, joeyli wrote:
> >>
> >> What platform do you have that has TAD support? I am wondering how this
> >> was tested.
> >>
> >
> > It's a testing platform that's only support get/set time functions of
> > ACPI TAD.
> >
>
> It would be really, really good to get this into Qemu (either SeaBIOS or
> OVMF, or ideally both) so we can have anyone test.
>
> -hpa
>

I will try to add to OVMF first.


Thanks
Joey Lee

joeyli

unread,
Jan 6, 2014, 4:30:02 AM1/6/14
to
於 四,2014-01-02 於 16:09 +0800,Lan Tianyu 提到:
> > diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
> > index bba9b72..3f7a075 100644
> > --- a/drivers/acpi/bus.c
> > +++ b/drivers/acpi/bus.c
> > @@ -689,6 +689,9 @@ static int __init acpi_init(void)
> > pci_mmcfg_late_init();
> > acpi_scan_init();
> > acpi_ec_init();
> > +#ifdef CONFIG_X86
> > + acpi_tad_init();
> > +#endif
>
> Why calling acpi_tad_init() directly here rather than using
> module_initcall?
> Is there dependency?
>

The rtc-acpitad RTC driver depend on acpi_read/set_time functions in
acpi_tad.

On the other hand, if we adapt to ACPI time device when "CMOS RTC Not
Present" set, then acpi_read/set_time will used to replace CMOS
functions that's called by other drivers. So, I direct call
acpi_tad_init() here.


Thanks a lot!
Joey Lee

H. Peter Anvin

unread,
Jan 7, 2014, 12:40:02 AM1/7/14
to
On 01/06/2014 12:58 AM, joeyli wrote:
> 於 二,2013-12-31 於 16:42 -0800,H. Peter Anvin 提到:
>> On 12/19/2013 09:41 PM, joeyli wrote:
>>>>
>>>> What platform do you have that has TAD support? I am wondering how this
>>>> was tested.
>>>>
>>>
>>> It's a testing platform that's only support get/set time functions of
>>> ACPI TAD.
>>>
>>
>> It would be really, really good to get this into Qemu (either SeaBIOS or
>> OVMF, or ideally both) so we can have anyone test.
>>
>> -hpa
>
> I will try to add to OVMF first.
>

For the record, I posted a patch to Qemu about a year ago to store the
timezone in the CMOS, which might be useful for this implementation. It
was rejected because of no firmware support, so if you implement it for
OVMF we can (update and) push this patch again.

-hpa



joeyli

unread,
Jan 7, 2014, 5:50:02 AM1/7/14
to
於 一,2014-01-06 於 21:37 -0800,H. Peter Anvin 提到:
Thanks for your patch.

Due to accessing CMOS through ASL need enable SMM support in OVMF, I am
looking Laszlo's OVMF S3 patches:

https://www.mail-archive.com/edk2-...@lists.sourceforge.net/msg04969.html

Honestly I am not good on this area, I need to spend more time to
understand it.


Regards

H. Peter Anvin

unread,
Jan 7, 2014, 11:40:01 AM1/7/14
to
On 01/07/2014 02:40 AM, joeyli wrote:
>
> Due to accessing CMOS through ASL need enable SMM support in OVMF,

Why? The CMOS is its own ASL address space, and you need that anyway to
be able to access the RTC proper. If you don't want to use it because
you don't want to export any indication of a legacy RTC you should be
able to just do I/O port references directly in your ASL.

-hpa

joeyli

unread,
Jan 8, 2014, 10:10:02 AM1/8/14
to
於 二,2014-01-07 於 08:35 -0800,H. Peter Anvin 提到:
> On 01/07/2014 02:40 AM, joeyli wrote:
> >
> > Due to accessing CMOS through ASL need enable SMM support in OVMF,
>
> Why? The CMOS is its own ASL address space, and you need that anyway to
> be able to access the RTC proper. If you don't want to use it because
> you don't want to export any indication of a legacy RTC you should be
> able to just do I/O port references directly in your ASL.
>
> -hpa
>
>

ACPICA denied AML access RTC ports.

I tried to access 0x70, 0x71 ports in ASL on a real machine, ACPICA
denied AML access to those ports. I got the following dmesg:

hwvalid-0188 hw_validate_io_request: Denied AML access to port
0x0000000000000071/1


The code in acpica denied it:

linux/drivers/acpi/acpica/hwvalid.c

* This provides ACPICA with the desired port protections and
* Microsoft compatibility.
*
* Description of port entries:
[...]
* RTC: Real-time clock
* CMOS: Extended CMOS
[...]
*/
static const struct acpi_port_info acpi_protected_ports[] = {
[...]
{"RTC", 0x0070, 0x0071, ACPI_OSI_WIN_XP},
{"CMOS", 0x0074, 0x0076, ACPI_OSI_WIN_XP},


Document of Windows XP:
http://www.freelists.org/post/windows_errors/what-error-messages-really-mean-WinXP-IO-Ports-Blocked-from-Bios-AML-on-Windows-XP


If just for ACPI TAD testing, we can remove the port protection check of
RTC ports in hwvalid.c. I have read 0x70/0x71 port success after removed
the checking in acpica/hwvalid.c.

I will try to write RTC port in AML after remove acpica check, maybe
have other unpredictable situation.


Thanks a lot!
Joey Lee

H. Peter Anvin

unread,
Jan 8, 2014, 1:00:02 PM1/8/14
to
Now *THERE* is a good use of the "no RTC bit". In the case that bit is
set we should presumably remove these ports from the block list.

Otherwise we should use the CMOS address space, not the I/O port address
space.

-hpa

joeyli

unread,
Jan 8, 2014, 10:50:02 PM1/8/14
to
於 三,2014-01-08 於 09:56 -0800,H. Peter Anvin 提到:
[...]
> > Document of Windows XP:
> >
> http://www.freelists.org/post/windows_errors/what-error-messages-really-mean-WinXP-IO-Ports-Blocked-from-Bios-AML-on-Windows-XP
> >
> > If just for ACPI TAD testing, we can remove the port protection
> check of
> > RTC ports in hwvalid.c. I have read 0x70/0x71 port success after
> removed
> > the checking in acpica/hwvalid.c.
> >
> > I will try to write RTC port in AML after remove acpica check, maybe
> > have other unpredictable situation.
> >
>
> Now *THERE* is a good use of the "no RTC bit". In the case that bit
> is
> set we should presumably remove these ports from the block list.

Thanks for your suggestion, I will put a testing patch on this.

>
> Otherwise we should use the CMOS address space, not the I/O port
> address
> space.
>
> -hpa

Unfortunately current acpica leaks the SystemCMOS handler:

ACPI Error: Region SystemCMOS (ID=5) has no handler (20131115/exfldio-299)


Regards
Joey Lee

H. Peter Anvin

unread,
Jan 8, 2014, 11:10:02 PM1/8/14
to
On 01/08/2014 07:47 PM, joeyli wrote:
>
> Unfortunately current acpica leaks the SystemCMOS handler:
>
> ACPI Error: Region SystemCMOS (ID=5) has no handler (20131115/exfldio-299)
>

I'm sorry, I can't parse either your statement or the error message...
sounds like there is a bug here, too.

-hpa

Rafael J. Wysocki

unread,
Jan 9, 2014, 7:10:03 AM1/9/14
to
On Thursday, January 09, 2014 11:47:39 AM joeyli wrote:
> 於 三,2014-01-08 於 09:56 -0800,H. Peter Anvin 提到:
> [...]
> > > Document of Windows XP:
> > >
> > http://www.freelists.org/post/windows_errors/what-error-messages-really-mean-WinXP-IO-Ports-Blocked-from-Bios-AML-on-Windows-XP
> > >
> > > If just for ACPI TAD testing, we can remove the port protection
> > check of
> > > RTC ports in hwvalid.c. I have read 0x70/0x71 port success after
> > removed
> > > the checking in acpica/hwvalid.c.
> > >
> > > I will try to write RTC port in AML after remove acpica check, maybe
> > > have other unpredictable situation.
> > >
> >
> > Now *THERE* is a good use of the "no RTC bit". In the case that bit
> > is
> > set we should presumably remove these ports from the block list.
>
> Thanks for your suggestion, I will put a testing patch on this.
>
> >
> > Otherwise we should use the CMOS address space, not the I/O port
> > address
> > space.
> >
> > -hpa
>
> Unfortunately current acpica leaks the SystemCMOS handler:
>
> ACPI Error: Region SystemCMOS (ID=5) has no handler (20131115/exfldio-299)

This is not an ACPICA problem. We have no CMOS address space handler in Linux
and arguably there should be one. So I'd suggest implementing that to start
with or we risk conflicts between kernel code and AML accessing registers in
that space.

Thanks!

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

Rafael J. Wysocki

unread,
Jan 11, 2014, 7:20:01 PM1/11/14
to
On Saturday, December 21, 2013 12:21:48 PM Matt Fleming wrote:
> On Fri, 20 Dec, at 11:18:56PM, Rafael J. Wysocki wrote:
> >
> > I'm not sure 100%, but I *think* we need to do that with interrupts enabled.
> > At least after mm_init(), because it relies on things initialized there if I
> > remember correctly.
> >
> > From what I can tell at the moment, it might be possible to move it before
> > efi_enter_virtual_mode() if that would help.
>
> Actually yeah, that would be super useful, and I think we'd be able to
> get rid of the whole efi_late_init() stuff because we'd no longer need
> to hang on to the EFI_BOOT_SERVICES* regions after
> SetVirtualAddressMap().

OK

I don't see any adverse effects of the patch below on a couple of my test
boxes, but (a) they are Intel-based and (b) they are non-EFI, so it would be
good to give it a go on as many machines as reasonably possible.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rafael.j...@intel.com>
Subject: ACPI / init: Run acpi_early_init() before efi_enter_virtual_mode()

According to Matt Fleming, if acpi_early_init() was executed befpre
efi_enter_virtual_mode(), the EFI initialization could benefit from
it, so make that happen.

Signed-off-by: Rafael J. Wysocki <rafael.j...@intel.com>
---
init/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-pm/init/main.c
===================================================================
--- linux-pm.orig/init/main.c
+++ linux-pm/init/main.c
@@ -615,6 +615,7 @@ asmlinkage void __init start_kernel(void
calibrate_delay();
pidmap_init();
anon_vma_init();
+ acpi_early_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
@@ -641,7 +642,6 @@ asmlinkage void __init start_kernel(void

check_bugs();

- acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();

if (efi_enabled(EFI_RUNTIME_SERVICES)) {

Borislav Petkov

unread,
Jan 12, 2014, 4:10:02 AM1/12/14
to
Looks good both on kvm+OVMF and on my Dell EFI box.

Tested-by: Borislav Petkov <b...@suse.de>

Toshi has a big EFI box though - if he could run it too, that would be
great :-)

Thanks.

--
Regards/Gruss,
Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

Matt Fleming

unread,
Jan 12, 2014, 6:10:02 AM1/12/14
to
On Sun, 12 Jan, at 01:30:23AM, Rafael J. Wysocki wrote:
> On Saturday, December 21, 2013 12:21:48 PM Matt Fleming wrote:
> > On Fri, 20 Dec, at 11:18:56PM, Rafael J. Wysocki wrote:
> > >
> > > I'm not sure 100%, but I *think* we need to do that with interrupts enabled.
> > > At least after mm_init(), because it relies on things initialized there if I
> > > remember correctly.
> > >
> > > From what I can tell at the moment, it might be possible to move it before
> > > efi_enter_virtual_mode() if that would help.
> >
> > Actually yeah, that would be super useful, and I think we'd be able to
> > get rid of the whole efi_late_init() stuff because we'd no longer need
> > to hang on to the EFI_BOOT_SERVICES* regions after
> > SetVirtualAddressMap().
>
> OK
>
> I don't see any adverse effects of the patch below on a couple of my test
> boxes, but (a) they are Intel-based and (b) they are non-EFI, so it would be
> good to give it a go on as many machines as reasonably possible.

Thanks Rafael, I'll give this a spin on my test machines here.

--
Matt Fleming, Intel Open Source Technology Center

Toshi Kani

unread,
Jan 13, 2014, 9:20:02 PM1/13/14
to
Yes, I will test the change.

Thanks,
-Toshi

joeyli

unread,
Jan 13, 2014, 11:20:02 PM1/13/14
to
於 日,2014-01-12 於 01:30 +0100,Rafael J. Wysocki 提到:
This patch works to me on Acer Gateway Z5WT2 UEFI notebook and Intel
UEFI development board.

Does it possible move acpi_early_init() to before timekeeping_init()?
The position is also before efi_enter_virtual_mode() and that will be
useful for parsing ACPI TAD to set system clock:

diff --git a/init/main.c b/init/main.c
index febc511..b6d93c8 100644
--- a/init/main.c
+++ b/init/main.c
@@ -565,6 +565,7 @@ asmlinkage void __init start_kernel(void)
init_timers();
hrtimers_init();
softirq_init();
+ acpi_early_init();
timekeeping_init();
time_init();
sched_clock_postinit();
@@ -641,7 +642,6 @@ asmlinkage void __init start_kernel(void)

check_bugs();

- acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();

if (efi_enabled(EFI_RUNTIME_SERVICES)) {


Thanks a lot!
Joey Lee

Toshi Kani

unread,
Jan 14, 2014, 11:40:03 AM1/14/14
to
Tested-by: Toshi Kani <toshi...@hp.com>

Rafael J. Wysocki

unread,
Jan 14, 2014, 11:40:03 AM1/14/14
to
We can do that if it doesn't cause any problems to happen.

Please resubmit your patch with a changelog and we'll ask everyone to try it.

Thanks!

> The position is also before efi_enter_virtual_mode() and that will be
> useful for parsing ACPI TAD to set system clock:
>
> diff --git a/init/main.c b/init/main.c
> index febc511..b6d93c8 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -565,6 +565,7 @@ asmlinkage void __init start_kernel(void)
> init_timers();
> hrtimers_init();
> softirq_init();
> + acpi_early_init();
> timekeeping_init();
> time_init();
> sched_clock_postinit();
> @@ -641,7 +642,6 @@ asmlinkage void __init start_kernel(void)
>
> check_bugs();
>
> - acpi_early_init(); /* before LAPIC and SMP init */
> sfi_init_late();
>
> if (efi_enabled(EFI_RUNTIME_SERVICES)) {
>
>
> Thanks a lot!
> Joey Lee
>

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

H. Peter Anvin

unread,
Jan 14, 2014, 1:10:04 PM1/14/14
to
On 01/13/2014 08:09 PM, joeyli wrote:
>
> This patch works to me on Acer Gateway Z5WT2 UEFI notebook and Intel
> UEFI development board.
>
> Does it possible move acpi_early_init() to before timekeeping_init()?
> The position is also before efi_enter_virtual_mode() and that will be
> useful for parsing ACPI TAD to set system clock:
>
> diff --git a/init/main.c b/init/main.c
> index febc511..b6d93c8 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -565,6 +565,7 @@ asmlinkage void __init start_kernel(void)
> init_timers();
> hrtimers_init();
> softirq_init();
> + acpi_early_init();
> timekeeping_init();
> time_init();
> sched_clock_postinit();
> @@ -641,7 +642,6 @@ asmlinkage void __init start_kernel(void)
>
> check_bugs();
>
> - acpi_early_init(); /* before LAPIC and SMP init */
> sfi_init_late();
>
> if (efi_enabled(EFI_RUNTIME_SERVICES)) {
>

Hi Toshi,

Could you try this variant, too? If this works as well then we end up
solving two problems in one patch...

-hpa

Toshi Kani

unread,
Jan 14, 2014, 3:40:01 PM1/14/14
to
Hi Peter,

Yes, this version works fine as well.

Tested-by: Toshi Kani <toshi...@hp.com>

Thanks,
-Toshi

joeyli

unread,
Jan 15, 2014, 2:30:01 AM1/15/14
to
於 二,2014-01-14 於 13:32 -0700,Toshi Kani 提到:
> > > + acpi_early_init();
> > > timekeeping_init();
> > > time_init();
> > > sched_clock_postinit();
> > > @@ -641,7 +642,6 @@ asmlinkage void __init start_kernel(void)
> > >
> > > check_bugs();
> > >
> > > - acpi_early_init(); /* before LAPIC and SMP init */
> > > sfi_init_late();
> > >
> > > if (efi_enabled(EFI_RUNTIME_SERVICES)) {
> > >
> >
> > Hi Toshi,
> >
> > Could you try this variant, too? If this works as well then we end
> up
> > solving two problems in one patch...
>
> Hi Peter,
>
> Yes, this version works fine as well.
>
> Tested-by: Toshi Kani <toshi...@hp.com>
>
> Thanks,
> -Toshi

Thanks a lot for your testing.

I will re-send a formal patch with changelog to everybody.

Regards
Joey Lee

Matt Fleming

unread,
Jan 17, 2014, 7:30:02 AM1/17/14
to
On Sun, 12 Jan, at 11:05:46AM, Matt Fleming wrote:
> On Sun, 12 Jan, at 01:30:23AM, Rafael J. Wysocki wrote:
> > On Saturday, December 21, 2013 12:21:48 PM Matt Fleming wrote:
> > > On Fri, 20 Dec, at 11:18:56PM, Rafael J. Wysocki wrote:
> > > >
> > > > I'm not sure 100%, but I *think* we need to do that with interrupts enabled.
> > > > At least after mm_init(), because it relies on things initialized there if I
> > > > remember correctly.
> > > >
> > > > From what I can tell at the moment, it might be possible to move it before
> > > > efi_enter_virtual_mode() if that would help.
> > >
> > > Actually yeah, that would be super useful, and I think we'd be able to
> > > get rid of the whole efi_late_init() stuff because we'd no longer need
> > > to hang on to the EFI_BOOT_SERVICES* regions after
> > > SetVirtualAddressMap().
> >
> > OK
> >
> > I don't see any adverse effects of the patch below on a couple of my test
> > boxes, but (a) they are Intel-based and (b) they are non-EFI, so it would be
> > good to give it a go on as many machines as reasonably possible.
>
> Thanks Rafael, I'll give this a spin on my test machines here.

(Sorry for replying so late)

As various other people have reported, this works fine.
0 new messages