From 62afa2aa588fb0a6ff56acdd268b9f3c557028b8 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 6 May 2020 13:46:42 -0400 Subject: [PATCH 1/3] headers: Move some util.h stuff to compiler.h and add list.h This just splits out some of the stuff that didn't really belong in util.h . Signed-off-by: Peter Jones --- src/compiler.h | 40 ++++++++++++- src/efivar.h | 2 + src/list.h | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ src/safemath.h | 12 ++-- src/util.h | 26 --------- 5 files changed, 200 insertions(+), 34 deletions(-) create mode 100644 src/list.h diff --git a/src/compiler.h b/src/compiler.h index 830b6576..7d4088b9 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -7,6 +7,8 @@ #ifndef COMPILER_H_ #define COMPILER_H_ +#include + /* GCC version checking borrowed from glibc. */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) # define GNUC_PREREQ(maj,min) \ @@ -43,8 +45,42 @@ # define CLANG_PREREQ(maj,min) 0 #endif -#define PASTE(x, y) x ## y -#define PASTE3(x, y, z) x ## y ## z +#define UNUSED __attribute__((__unused__)) +#define HIDDEN __attribute__((__visibility__ ("hidden"))) +#define PUBLIC __attribute__((__visibility__ ("default"))) +#define DESTRUCTOR __attribute__((__destructor__)) +#define CONSTRUCTOR __attribute__((__constructor__)) +#define ALIAS(x) __attribute__((weak, alias (#x))) +#define NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#define PRINTF(...) __attribute__((__format__(printf, __VA_ARGS__))) +#define FLATTEN __attribute__((__flatten__)) +#define PACKED __attribute__((__packed__)) +#if defined(__clang__) +# define VERSION(sym, ver) +#else +# if GNUC_PREREQ(10,0) +# define VERSION(sym, ver) __attribute__ ((symver (# ver))) +# else +# define VERSION(sym, ver) __asm__(".symver " # sym "," # ver) +# endif +#endif +#define NORETURN __attribute__((__noreturn__)) +#define ALIGNED(n) __attribute__((__aligned__(n))) +#define CLEANUP_FUNC(x) __attribute__((__cleanup__(x))) + +#define __CONCAT3(a, b, c) a ## b ## c +#define CONCATENATE(a, b) __CONCAT(a, b) +#define CAT(a, b) __CONCAT(a, b) +#define CAT3(a, b, c) __CONCAT3(a, b, c) +#define STRING(x) __STRING(x) + +#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define __ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) +#define ALIGN(x, a) __ALIGN((x), (a)) +#define ALIGN_DOWN(x, a) __ALIGN((x) - ((a) - 1), (a)) + +#define ALIGNMENT_PADDING(value, align) ((align - (value % align)) % align) +#define ALIGN_UP(value, align) ((value) + ALIGNMENT_PADDING(value, align)) #endif /* !COMPILER_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/src/efivar.h b/src/efivar.h index ca6f2483..2dd90ee2 100644 --- a/src/efivar.h +++ b/src/efivar.h @@ -11,7 +11,9 @@ #include +#include "compiler.h" #include "diag.h" +#include "list.h" #include "util.h" #include "safemath.h" #include "efivar_endian.h" diff --git a/src/list.h b/src/list.h new file mode 100644 index 00000000..651c1d9a --- /dev/null +++ b/src/list.h @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * list.h - simple list primitives + */ + +#ifndef LIST_H_ +#define LIST_H_ + +#include + +#define container_of(ptr, type, member) \ + ({ \ + void *__mptr = (void *)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); \ + }) + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +typedef struct list_head list_t; + +#define LIST_HEAD_INIT(name) \ + { \ + .next = &(name), .prev = &(name) \ + } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) \ + ({ \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + }) + +static inline int +list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void +__list_add(struct list_head *new, struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void +list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void +list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void +__list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void +__list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void +list_del(struct list_head *entry) +{ + __list_del_entry(entry); + entry->next = NULL; + entry->prev = NULL; +} + +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; pos != (head); \ + pos = n, n = pos->prev) + +static inline size_t +list_size(struct list_head *entry) +{ + list_t *pos; + size_t i = 0; + list_for_each(pos, entry) { + i++; + } + return i; +} + +/* + * Sort a list with cmp() + * creates a temporary array on the heap + */ +static inline int +list_sort(struct list_head *head, + int (*cmp)(const void *a, const void *b, void *state), + void *state) +{ + struct list_head **array = NULL, *pos; + size_t nmemb, i = 0; + + nmemb = list_size(head); + + array = calloc(nmemb, sizeof (head)); + if (!array) + return -1; + + list_for_each(pos, head) { + array[i++] = pos; + } + + qsort_r(array, nmemb, sizeof(*array), cmp, state); + + INIT_LIST_HEAD(head); + for (i = 0; i < nmemb; i++) { + INIT_LIST_HEAD(array[i]); + list_add(array[i], head); + } + free(array); + + return 0; +} + + +#endif /* !LIST_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/src/safemath.h b/src/safemath.h index 462c0980..caa5b1cc 100644 --- a/src/safemath.h +++ b/src/safemath.h @@ -64,18 +64,18 @@ #define generic_sint_builtin_(op, x) \ _Generic((x), \ - int: PASTE(__builtin_, op)(x), \ - long: PASTE33(__builtin_, op, l)(x), \ - long long: PASTE3(__builtin_, op, ll)(x) \ + int: CAT(__builtin_, op)(x), \ + long: CAT33(__builtin_, op, l)(x), \ + long long: CAT3(__builtin_, op, ll)(x) \ ) #define FFS(x) generic_sint_builtin_(ffs, x) #define CLRSB(x) generic_sint_builtin_(clrsb, x) #define generic_uint_builtin_(op, x) \ _Generic((x), \ - unsigned int: PASTE(__builtin_, op)(x), \ - unsigned long: PASTE3(__builtin_, op, l)(x), \ - unsigned long long: PASTE3(__builtin_, op, ll)(x) \ + unsigned int: CAT(__builtin_, op)(x), \ + unsigned long: CAT3(__builtin_, op, l)(x), \ + unsigned long long: CAT3(__builtin_, op, ll)(x) \ ) #define CLZ(x) generic_uint_builtin_(clz, x) #define CTZ(x) generic_uint_builtin_(ctz, x) diff --git a/src/util.h b/src/util.h index 43dab45f..fa8db5bf 100644 --- a/src/util.h +++ b/src/util.h @@ -29,32 +29,6 @@ #include "compiler.h" -#define UNUSED __attribute__((__unused__)) -#define HIDDEN __attribute__((__visibility__ ("hidden"))) -#define PUBLIC __attribute__((__visibility__ ("default"))) -#define DESTRUCTOR __attribute__((destructor)) -#define CONSTRUCTOR __attribute__((constructor)) -#define ALIAS(x) __attribute__((weak, alias (#x))) -#define NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#define PRINTF(...) __attribute__((__format__(printf, __VA_ARGS__))) -#define FLATTEN __attribute__((__flatten__)) -#define PACKED __attribute__((__packed__)) -#if GNUC_PREREQ(10,0) -# define VERSION(sym, ver) __attribute__ ((symver (# ver))) -#else -# define VERSION(sym, ver) __asm__(".symver " # sym "," # ver) -#endif - - -#define __branch_check__(x, expect, is_constant) \ - __builtin_expect(!!(x), expect) -#ifndef likely -#define likely(x) (__branch_check__(x, 1, __builtin_constant_p(x))) -#endif -#ifndef unlikely -#define unlikely(x) (__branch_check__(x, 0, __builtin_constant_p(x))) -#endif - static inline int UNUSED read_file(int fd, uint8_t **result, size_t *bufsize) { From 8882f9e17c80aec56ef6a67959cddc497053decd Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 3 Feb 2020 13:24:30 -0500 Subject: [PATCH 2/3] headers: split types and guids out into a different header Again, just moving stuff around to slightly more consumable locations. Signed-off-by: Peter Jones --- src/export.c | 8 -- src/include/efivar/efivar-types.h | 64 +++++++++++ src/include/efivar/efivar.h | 44 +------- src/lib.h | 8 ++ src/libefiboot.abixml | 50 ++++---- src/libefivar.abixml | 182 +++++++++++++++--------------- 6 files changed, 189 insertions(+), 167 deletions(-) create mode 100644 src/include/efivar/efivar-types.h diff --git a/src/export.c b/src/export.c index 87c97de2..f61a8927 100644 --- a/src/export.c +++ b/src/export.c @@ -18,14 +18,6 @@ #define ATTRS_UNSET 0xa5a5a5a5a5a5a5a5 #define ATTRS_MASK 0xffffffff -struct efi_variable { - uint64_t attrs; - efi_guid_t *guid; - unsigned char *name; - uint8_t *data; - size_t data_size; -}; - /* The exported structure is: * struct { * uint32_t magic; diff --git a/src/include/efivar/efivar-types.h b/src/include/efivar/efivar-types.h new file mode 100644 index 00000000..6fca8a49 --- /dev/null +++ b/src/include/efivar/efivar-types.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * Copyright 2012-2020 Red Hat, Inc. + * Copyright 2012-2020 Peter M. Jones + * + * Author(s): Peter Jones + */ +#ifndef EFI_TYPES_H +#define EFI_TYPES_H 1 + +#include + +typedef struct { + uint32_t a; + uint16_t b; + uint16_t c; + uint16_t d; + uint8_t e[6]; +} efi_guid_t __attribute__((__aligned__(1))); + +#if BYTE_ORDER == LITTLE_ENDIAN +#define EFI_GUID(a,b,c,d,e0,e1,e2,e3,e4,e5) \ +((efi_guid_t) {(a), (b), (c), __builtin_bswap16(d), { (e0), (e1), (e2), (e3), (e4), (e5) }}) +#else +#define EFI_GUID(a,b,c,d,e0,e1,e2,e3,e4,e5) \ +((efi_guid_t) {(a), (b), (c), (d), { (e0), (e1), (e2), (e3), (e4), (e5) }}) +#endif + +#define EFI_GLOBAL_GUID EFI_GUID(0x8be4df61,0x93ca,0x11d2,0xaa0d,0x00,0xe0,0x98,0x03,0x2b,0x8c) + +typedef struct { + uint8_t addr[4]; +} efi_ipv4_addr_t; + +typedef struct { + uint8_t addr[16]; +} efi_ipv6_addr_t; + +typedef union { + uint32_t addr[4]; + efi_ipv4_addr_t v4; + efi_ipv6_addr_t v6; +} efi_ip_addr_t; + +typedef struct { + uint8_t addr[32]; +} efi_mac_addr_t; + +typedef unsigned long efi_status_t; +typedef uint16_t efi_char16_t; +typedef unsigned long uintn_t; +typedef long intn_t; + +#define EFI_VARIABLE_NON_VOLATILE ((uint64_t)0x0000000000000001) +#define EFI_VARIABLE_BOOTSERVICE_ACCESS ((uint64_t)0x0000000000000002) +#define EFI_VARIABLE_RUNTIME_ACCESS ((uint64_t)0x0000000000000004) +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD ((uint64_t)0x0000000000000008) +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS ((uint64_t)0x0000000000000010) +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS ((uint64_t)0x0000000000000020) +#define EFI_VARIABLE_APPEND_WRITE ((uint64_t)0x0000000000000040) +#define EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS ((uint64_t)0x0000000000000080) + +#endif /* EFI_TYPES_H */ +// vim:fenc=utf-8:tw=75:noet diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h index cc5dcc56..6b38ce8f 100644 --- a/src/include/efivar/efivar.h +++ b/src/include/efivar/efivar.h @@ -17,54 +17,12 @@ #include #include -typedef struct { - uint32_t a; - uint16_t b; - uint16_t c; - uint16_t d; - uint8_t e[6]; -} efi_guid_t __attribute__((__aligned__(1))); - -typedef struct { - uint8_t addr[4]; -} efi_ipv4_addr_t; - -typedef struct { - uint8_t addr[16]; -} efi_ipv6_addr_t; - -typedef union { - uint32_t addr[4]; - efi_ipv4_addr_t v4; - efi_ipv6_addr_t v6; -} efi_ip_addr_t; - -typedef struct { - uint8_t addr[32]; -} efi_mac_addr_t; +#include #ifndef EFIVAR_BUILD_ENVIRONMENT #include #endif -#if BYTE_ORDER == LITTLE_ENDIAN -#define EFI_GUID(a,b,c,d,e0,e1,e2,e3,e4,e5) \ -((efi_guid_t) {(a), (b), (c), __builtin_bswap16(d), { (e0), (e1), (e2), (e3), (e4), (e5) }}) -#else -#define EFI_GUID(a,b,c,d,e0,e1,e2,e3,e4,e5) \ -((efi_guid_t) {(a), (b), (c), (d), { (e0), (e1), (e2), (e3), (e4), (e5) }}) -#endif - -#define EFI_GLOBAL_GUID EFI_GUID(0x8be4df61,0x93ca,0x11d2,0xaa0d,0x00,0xe0,0x98,0x03,0x2b,0x8c) - -#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 -#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 -#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 -#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008 -#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010 -#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020 -#define EFI_VARIABLE_APPEND_WRITE 0x0000000000000040 - #define EFI_VARIABLE_HAS_AUTH_HEADER 0x0000000100000000 #define EFI_VARIABLE_HAS_SIGNATURE 0x0000000200000000 diff --git a/src/lib.h b/src/lib.h index 26e0f169..21ebc9a8 100644 --- a/src/lib.h +++ b/src/lib.h @@ -12,6 +12,14 @@ #define GUID_FORMAT "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x" +struct efi_variable { + uint64_t attrs; + efi_guid_t *guid; + unsigned char *name; + uint8_t *data; + size_t data_size; +}; + struct efi_var_operations { char name[NAME_MAX]; int (*probe)(void); diff --git a/src/libefiboot.abixml b/src/libefiboot.abixml index 7f82c016..017dea7d 100644 --- a/src/libefiboot.abixml +++ b/src/libefiboot.abixml @@ -54,24 +54,24 @@ - + - + - + - + - + - + - + @@ -670,7 +670,7 @@ - + @@ -679,14 +679,14 @@ - + - + - + @@ -1082,7 +1082,7 @@ - + @@ -1191,7 +1191,7 @@ - + @@ -2193,30 +2193,30 @@ - + - + - - + + - + - - + + - + - + - + - + @@ -2240,7 +2240,7 @@ - + diff --git a/src/libefivar.abixml b/src/libefivar.abixml index 32b84c06..c8da0c15 100644 --- a/src/libefivar.abixml +++ b/src/libefivar.abixml @@ -249,11 +249,11 @@ - + - + @@ -296,12 +296,12 @@ - + - + @@ -329,7 +329,7 @@ - + @@ -1374,48 +1374,48 @@ - + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + @@ -1531,7 +1531,7 @@ - + @@ -1592,36 +1592,36 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -1773,7 +1773,7 @@ - + @@ -1781,10 +1781,10 @@ - + - + @@ -2126,24 +2126,24 @@ - + + - + - + - + - + - + - @@ -2153,78 +2153,78 @@ - - - - + + + + - - - - + + + + - - - - + + + + - + - - - + + + - - - + + + - - + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - + + - + @@ -2233,7 +2233,7 @@ - + @@ -2417,7 +2417,7 @@ - + From 011f3e5212d4bde5f651795435ccf0f77bd2633d Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 3 Feb 2020 13:26:08 -0500 Subject: [PATCH 3/3] Add efi_time_t and time conversion and formatting utilities This adds the hilariously defective efi_time_t type, as well as conversion utilities to and from it, and utilities for formatted output of it. Optionally, for compatibility with older source trees, if your application defines EFIVAR_NO_EFI_TIME_T to any value other than 0, it will *not* do so, though that option may go away in some future efivar version. This allows you to turn off declaration of efi_time_t and related functions in the case where it's declared someplace else, such as in some local code or another library's headers. Signed-off-by: Peter Jones --- src/Makefile | 2 +- src/include/efivar/efivar-time.h | 33 ++++ src/include/efivar/efivar-types.h | 35 ++++ src/include/efivar/efivar.h | 2 + src/libefiboot.abixml | 14 +- src/libefivar.abixml | 121 +++++++++++-- src/libefivar.map.in | 12 ++ src/time.c | 275 ++++++++++++++++++++++++++++++ 8 files changed, 473 insertions(+), 21 deletions(-) create mode 100644 src/include/efivar/efivar-time.h create mode 100644 src/time.c diff --git a/src/Makefile b/src/Makefile index debda257..e3f5039b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,7 +18,7 @@ LIBEFIBOOT_SOURCES = crc32.c creator.c disk.c gpt.c loadopt.c path-helpers.c \ LIBEFIBOOT_OBJECTS = $(patsubst %.c,%.o,$(LIBEFIBOOT_SOURCES)) LIBEFIVAR_SOURCES = crc32.c dp.c dp-acpi.c dp-hw.c dp-media.c dp-message.c \ efivarfs.c error.c export.c guid.c guids.S guid-symbols.c \ - lib.c vars.c + lib.c vars.c time.c LIBEFIVAR_OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(LIBEFIVAR_SOURCES))) EFIVAR_SOURCES = efivar.c EFIVAR_OBJECTS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(EFIVAR_SOURCES))) diff --git a/src/include/efivar/efivar-time.h b/src/include/efivar/efivar-time.h new file mode 100644 index 00000000..284e5b40 --- /dev/null +++ b/src/include/efivar/efivar-time.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * efivar-time.h + * Copyright 2020 Peter Jones + */ + +#if defined(EFIVAR_NO_EFI_TIME_T) && EFIVAR_NO_EFI_TIME_T && \ + !defined(EFIVAR_TIME_H_) +#define EFIVAR_TIME_H_ 1 +#endif + +#ifndef EFIVAR_TIME_H_ +#define EFIVAR_TIME_H_ 1 + +#include + +extern int tm_to_efi_time(const struct tm *const s, efi_time_t *d, bool tzadj); +extern int efi_time_to_tm(const efi_time_t * const s, struct tm *d); + +extern char *efi_asctime(const efi_time_t *const time); +extern char *efi_asctime_r(const efi_time_t *const time, char *buf); +extern efi_time_t *efi_gmtime(const time_t *time); +extern efi_time_t *efi_gmtime_r(const time_t *time, efi_time_t *result); +extern efi_time_t *efi_localtime(const time_t *time); +extern efi_time_t *efi_localtime_r(const time_t *time, efi_time_t *result); +extern time_t efi_mktime(const efi_time_t *const time); + +extern char *efi_strptime(const char *s, const char *format, efi_time_t *time); +extern size_t efi_strftime(char *s, size_t max, const char *format, + const efi_time_t *time); + +#endif /* !EFIVAR_TIME_H_ */ +// vim:fenc=utf-8:tw=75:noet diff --git a/src/include/efivar/efivar-types.h b/src/include/efivar/efivar-types.h index 6fca8a49..1d48943f 100644 --- a/src/include/efivar/efivar-types.h +++ b/src/include/efivar/efivar-types.h @@ -51,6 +51,41 @@ typedef uint16_t efi_char16_t; typedef unsigned long uintn_t; typedef long intn_t; +#if !defined(EFIVAR_NO_EFI_TIME_T) || EFIVAR_NO_EFI_TIME_T +#define EFIVAR_HAVE_EFI_TIME_T 1 + +/* + * This can never be correct, as defined, in the face of leap seconds. + * Because seconds here are defined with a range of [0,59], we can't + * express leap seconds correctly there. Because TimeZone is specified in + * minutes West of UTC, rather than seconds (like struct tm), it can't be + * used to correct when we cross a leap second boundary condition. As a + * result, EFI_TIME can only express UT1, rather than UTC, and there's no + * way when converting to know wether the error has been taken into + * account, nor if it should be. + * + * As I write this, there is a 37 second error. + */ +typedef struct { + uint16_t year; // 1900 - 9999 + uint8_t month; // 1 - 12 + uint8_t day; // 1 - 31 + uint8_t hour; // 0 - 23 + uint8_t minute; // 0 - 59 + uint8_t second; // 0 - 59 // ha ha only serious + uint8_t pad1; // 0 + uint32_t nanosecond; // 0 - 999,999,999 + int16_t timezone; // minutes from UTC or EFI_UNSPECIFIED_TIMEZONE + uint8_t daylight; // bitfield + uint8_t pad2; // 0 +} efi_time_t __attribute__((__aligned__(1))); + +#define EFI_TIME_ADJUST_DAYLIGHT ((uint8_t)0x01) +#define EFI_TIME_IN_DAYLIGHT ((uint8_t)0x02) + +#define EFI_UNSPECIFIED_TIMEZONE ((uint16_t)0x07ff) +#endif /* !defined(EFIVAR_NO_EFI_TIME_T) || EFIVAR_NO_EFI_TIME_T */ + #define EFI_VARIABLE_NON_VOLATILE ((uint64_t)0x0000000000000001) #define EFI_VARIABLE_BOOTSERVICE_ACCESS ((uint64_t)0x0000000000000002) #define EFI_VARIABLE_RUNTIME_ACCESS ((uint64_t)0x0000000000000004) diff --git a/src/include/efivar/efivar.h b/src/include/efivar/efivar.h index 6b38ce8f..7518a323 100644 --- a/src/include/efivar/efivar.h +++ b/src/include/efivar/efivar.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -200,6 +201,7 @@ extern uint32_t efi_get_libefivar_version(void) __attribute__((__visibility__("default"))); #include +#include #endif /* EFIVAR_H */ diff --git a/src/libefiboot.abixml b/src/libefiboot.abixml index 017dea7d..dad69418 100644 --- a/src/libefiboot.abixml +++ b/src/libefiboot.abixml @@ -670,7 +670,7 @@ - + @@ -679,14 +679,14 @@ - + - + - + @@ -1082,7 +1082,7 @@ - + @@ -1191,7 +1191,7 @@ - + @@ -2240,7 +2240,7 @@ - + diff --git a/src/libefivar.abixml b/src/libefivar.abixml index c8da0c15..dddefa4f 100644 --- a/src/libefivar.abixml +++ b/src/libefivar.abixml @@ -249,11 +249,11 @@ - + - + @@ -296,12 +296,12 @@ - + - + @@ -329,7 +329,7 @@ - + @@ -1531,7 +1531,7 @@ - + @@ -1773,7 +1773,7 @@ - + @@ -1781,7 +1781,7 @@ - + @@ -2126,7 +2126,7 @@ - + @@ -2224,7 +2224,7 @@ - + @@ -2233,7 +2233,7 @@ - + @@ -2415,8 +2415,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2429,7 +2524,7 @@ - + diff --git a/src/libefivar.map.in b/src/libefivar.map.in index 94e3370f..2d9d2da0 100644 --- a/src/libefivar.map.in +++ b/src/libefivar.map.in @@ -140,4 +140,16 @@ LIBEFIVAR_1.38 { efi_guid_external_management; efi_variable_alloc; efi_variable_export_dmpstore; + + tm_to_efi_time; + efi_time_to_tm; + efi_asctime; + efi_asctime_r; + efi_gmtime; + efi_gmtime_r; + efi_localtime; + efi_localtime_r; + efi_mktime; + efi_strptime; + efi_strftime; } LIBEFIVAR_1.37; diff --git a/src/time.c b/src/time.c new file mode 100644 index 00000000..d17768f6 --- /dev/null +++ b/src/time.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * time.c - efi_time_t helper functions + * Copyright 2020 Peter Jones + */ + +#include "efivar.h" + +int +efi_time_to_tm(const efi_time_t * const s, struct tm *d) +{ + + if (!s || !d) { + errno = EINVAL; + return -1; + } + + d->tm_year = s->year - 1900; + d->tm_mon = s->month - 1; + d->tm_mday = s->day; + d->tm_hour = s->hour; + d->tm_min = s->minute; + /* + * Just ignore EFI's range problem here and pretend we're in UTC + * not UT1. + */ + d->tm_sec = s->second; + d->tm_isdst = (s->daylight & EFI_TIME_IN_DAYLIGHT) ? 1 : 0; + + return 0; +} + +int +tm_to_efi_time(const struct tm * const s, efi_time_t *d, bool tzadj) +{ + if (!s || !d) { + errno = EINVAL; + return -1; + } + + d->pad2 = 0; + d->daylight = s->tm_isdst ? EFI_TIME_IN_DAYLIGHT : 0; + d->timezone = 0; + d->nanosecond = 0; + d->pad1 = 0; + d->second = s->tm_sec < 60 ? s->tm_sec : 59; + d->minute = s->tm_min; + d->hour = s->tm_hour; + d->day = s->tm_mday; + d->month = s->tm_mon + 1; + d->year = s->tm_year + 1900; + + if (tzadj) { + tzset(); + d->timezone = timezone / 60; + } + + return 0; +} + +static char *otz_; +static char *ntz_; + +static const char * +newtz(int16_t timezone_) +{ + if (!otz_) + otz_ = strdup(secure_getenv("TZ")); + + if (ntz_) { + free(ntz_); + ntz_ = NULL; + } + + if (timezone_ == EFI_UNSPECIFIED_TIMEZONE) { + unsetenv("TZ"); + } else { + char tzsign = timezone_ >= 0 ? '+' : '-'; + int tzabs = tzsign == '+' ? timezone_ : -timezone_; + int16_t tzhours = tzabs / 60; + int16_t tzminutes = tzabs % 60; + int rc; + + /* + * I have no idea what the right thing to do with DST is + * here, so I'm going to ignore it. + */ + rc = asprintf(&ntz_, "UTC%c%"PRId16":%"PRId16":00", + tzsign, tzhours, tzminutes); + if (rc < 1) + return NULL; + + setenv("TZ", ntz_, 1); + } + tzset(); + + return ntz_; +} + +static const char * +oldtz(void) { + if (ntz_) { + free(ntz_); + ntz_ = NULL; + + if (otz_) + setenv("TZ", otz_, 1); + else + unsetenv("TZ"); + } + + tzset(); + + return otz_; +} + +efi_time_t * +efi_gmtime_r(const time_t *time, efi_time_t *result) +{ + struct tm tm = { 0 }; + + if (!time || !result) { + errno = EINVAL; + return NULL; + } + + gmtime_r(time, &tm); + tm_to_efi_time(&tm, result, false); + + return result; +} + +efi_time_t * +efi_gmtime(const time_t *time) +{ + static efi_time_t ret; + + if (!time) { + errno = EINVAL; + return NULL; + } + + efi_gmtime_r(time, &ret); + + return &ret; +} + +efi_time_t * +efi_localtime_r(const time_t *time, efi_time_t *result) +{ + struct tm tm = { 0 }; + + if (!time || !result) { + errno = EINVAL; + return NULL; + } + + localtime_r(time, &tm); + tm_to_efi_time(&tm, result, true); + + return result; +} + +efi_time_t * +efi_localtime(const time_t *time) +{ + static efi_time_t ret; + + if (!time) { + errno = EINVAL; + return NULL; + } + + efi_localtime_r(time, &ret); + + return &ret; +} + +time_t +efi_mktime(const efi_time_t * const time) +{ + struct tm tm = { 0 }; + time_t ret; + + if (!time) { + errno = EINVAL; + return (time_t)-1; + } + + newtz(time->timezone); + + efi_time_to_tm(time, &tm); + ret = mktime(&tm); + + oldtz(); + + return ret; +} + +char * +efi_strptime(const char *s, const char *format, efi_time_t *time) +{ + struct tm tm; + char *end; + + if (!s || !format || !time) { + errno = EINVAL; + return NULL; + } + + memset(&tm, 0, sizeof(tm)); + end = strptime(s, format, &tm); + if (end == NULL) + return NULL; + + if (tm_to_efi_time(&tm, time, true) < 0) + return NULL; + + return end; +} + +char * +efi_asctime_r(const efi_time_t * const time, char *buf) +{ + struct tm tm; + char *ret; + + newtz(time->timezone); + + efi_time_to_tm(time, &tm); + ret = asctime_r(&tm, buf); + + oldtz(); + + return ret; +} + +char * +efi_asctime(const efi_time_t * const time) +{ + struct tm tm; + char *ret; + + newtz(time->timezone); + + efi_time_to_tm(time, &tm); + ret = asctime(&tm); + + oldtz(); + + return ret; +} + +size_t +efi_strftime(char *s, size_t max, const char *format, const efi_time_t *time) +{ + size_t ret = 0; + struct tm tm = { 0 }; + + if (!s || !format || !time) { + errno = EINVAL; + return ret; + } + + newtz(time->timezone); + + efi_time_to_tm(time, &tm); + ret = strftime(s, max, format, &tm); + + oldtz(); + + return ret; +} + +// vim:fenc=utf-8:tw=75:noet