问题描述
免责声明:我花了 4 到 5 个小时的时间来寻找答案,在弄清楚之后我决定将其发布在这里供同一地点的人们使用。
OP-TEE 是开发 TA 和 CA 的一个很好的环境,但是,没有直接的方法来获取格式正确的日期时间。也没有 struct tm
。因此,这让我想知道如何在 OP-TEE TA 中获取日期时间格式?
我花了很长时间尝试利用已经支持的 mbedTLS
库,对于新手来说,它们似乎确实支持获取日期时间格式。毕竟,他们确实有 gmtime
应该返回这个值。
然而,不幸的是,gmtime
和相关函数在 ARMv8 上没有针对平台 OP-TEE 的实现。这是一个非常艰难的运气。
那么如何在 OP-TEE TA 中获得 UTC 时间?
解决方法
ARMv8 的所有 OP-TEE 开发都是使用 C 完成的。但是,它缺乏主要的 libc 支持。实际上,它有非常少的库(例如 string.h),这些库是从原始 libc 对应库中删减的版本。
这样,OP-TEE 中提供的 <time.h>
只包含 typedef
的 time_t
,仅此而已。
问题可以分为两部分:
- 如何获得自 1970 年 1 月 1 日 00:00:00 以来的历元?
这是一个有趣的问题,而直接的解决方案是简单地这样做:
TEE_Time tt;
TEE_GetREETime(&tt);
对于许多不想依赖 REE(富执行环境,也称为易受攻击的环境)计数的人来说,此解决方案可能并不令人满意。这对于安全敏感的操作来说可能是有问题的,因为您需要时间来证明其合法性,而且 REE 没有空间修改它以执行特定攻击。
在上述情况下,您必须从硬件时钟获取纪元,这取决于您开发 TA 的硬件板。您甚至可以从定位设备中检索它,这些设备也在 NMEA 语句中返回 UTC 时间。虽然它可能不是 100% 精确到秒,但这可能就足够了。如果您需要非常高的精度,则需要安全地从硬件设备中获取。
无论哪种方式,您都必须自己弄清楚如何获得纪元。此答案侧重于第二部分:获取日期时间。
- 从纪元中获取日期时间。解决步骤 1 后,您需要将其处理为 datetime。为此,您需要 OP-TEE 中不存在的
gmtime
。它没有实现。您需要一个极简的实现来让事情变得简单。
幸运的是,我找到了this answer。哪些链接到 newlib
为 Free BSD 开发的库,这些库是嵌入式系统的理想选择。这就是为什么它在这里很有用!
我能够从他们的实现中将其组合在一起,您可以在此处使用它:
gmtime_r.h:
#include <inttypes.h>
#define SECSPERMIN 60L
#define MINSPERHOUR 60L
#define HOURSPERDAY 24L
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
#define DAYSPERWEEK 7
#define MONSPERYEAR 12
#define YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY 4
#define EPOCH_YEARS_SINCE_LEAP 2
#define EPOCH_YEARS_SINCE_CENTURY 70
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
typedef int64_t time_t;
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct tm* gmtime_r (time_t tim_p,struct tm* res);
gmtime_r.c:
#include "gmtime_r.h"
#define EPOCH_ADJUSTMENT_DAYS 719468L
/* year to which the adjustment was made */
#define ADJUSTED_EPOCH_YEAR 0
/* 1st March of year 0 is Wednesday */
#define ADJUSTED_EPOCH_WDAY 3
/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
#define DAYS_PER_ERA 146097L
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
#define DAYS_PER_CENTURY 36524L
/* there is one leap year every 4 years */
#define DAYS_PER_4_YEARS (3 * 365 + 366)
/* number of days in a non-leap year */
#define DAYS_PER_YEAR 365
/* number of days in January */
#define DAYS_IN_JANUARY 31
/* number of days in non-leap February */
#define DAYS_IN_FEBRUARY 28
/* number of years per era */
#define YEARS_PER_ERA 400
struct tm* gmtime_r (time_t tim_p,struct tm* res)
{
time_t days,rem;
const time_t lcltime = tim_p;
int era,weekday,year;
unsigned erayear,yearday,month,day;
unsigned long eraday;
days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
rem = lcltime % SECSPERDAY;
if (rem < 0)
{
rem += SECSPERDAY;
--days;
}
/* compute hour,min,and sec */
res->tm_hour = (int) (rem / SECSPERHOUR);
rem %= SECSPERHOUR;
res->tm_min = (int) (rem / SECSPERMIN);
res->tm_sec = (int) (rem % SECSPERMIN);
/* compute day of week */
if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
weekday += DAYSPERWEEK;
res->tm_wday = weekday;
/* compute year,day & day of year */
/* for description of this algorithm see
* http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
eraday = days - era * DAYS_PER_ERA; /* [0,146096] */
erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
eraday / (DAYS_PER_ERA - 1)) / 365; /* [0,399] */
yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0,365] */
month = (5 * yearday + 2) / 153; /* [0,11] */
day = yearday - (153 * month + 2) / 5 + 1; /* [1,31] */
month += month < 10 ? 2 : -10;
year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + isleap(erayear);
res->tm_year = year - YEAR_BASE;
res->tm_mon = month;
res->tm_mday = day;
res->tm_isdst = 0;
return (res);
}
您可以将这两个文件都放在 TA 文件夹中,并确保将 gmtime.c
添加到 sub.mk
的源列表中。最后,您可以在 TA 本身中使用它:
TEE_Time tt;
TEE_GetREETime(&tt);
struct tm *lt,temp;
lt = gmtime((time_t)tt.seconds,&temp);
DMSG("%4d-%2d-%2d %2d:%2d:%2d",lt->tm_year + 1900,lt->tm_mon + 1,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec);
这将以正确的格式打印时间和日期。
目前,我还没有移植 strftime
的实现,但很快我也会这样做,这将自动格式化 tm
结构并将 1900
添加到 {{ 1}} 和 tm_year
到 1
。
与此同时,我希望这能找到有需要的人。