authorgravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2025-03-03 10:07:41+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-03-03 10:07:41+00:00
log6d29ef0bafa3a6d1646658b9bb88c2326f3720a1
tree91d88ca1d295d908baefa4886b0186efe1e1ffe7
parent00a8742bbf71b5175b52ecdf736fb4a53ab8177f
parent3bea47883aae5c7585a27d4b751d656dd723576d
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23046 from linusg/uefi-time

Three time-related fixes for UEFI

2 files changed, 52 insertions(+), 42 deletions(-)

lib/std/os/uefi.zig+19-11
......@@ -113,31 +113,39 @@ pub const Time = extern struct {
113113 /// 0 - 59
114114 second: u8,
115115
116 _pad1: u8,
117
116118 /// 0 - 999999999
117119 nanosecond: u32,
118120
119121 /// The time's offset in minutes from UTC.
120122 /// Allowed values are -1440 to 1440 or unspecified_timezone
121123 timezone: i16,
122 daylight: packed struct {
123 _pad1: u6,
124
124 daylight: packed struct(u8) {
125125 /// If true, the time has been adjusted for daylight savings time.
126126 in_daylight: bool,
127127
128128 /// If true, the time is affected by daylight savings time.
129129 adjust_daylight: bool,
130
131 _: u6,
130132 },
131133
134 _pad2: u8,
135
136 comptime {
137 std.debug.assert(@sizeOf(Time) == 16);
138 }
139
132140 /// Time is to be interpreted as local time
133141 pub const unspecified_timezone: i16 = 0x7ff;
134142
135 fn daysInYear(year: u16, maxMonth: u4) u32 {
136 const leapYear: std.time.epoch.YearLeapKind = if (std.time.epoch.isLeapYear(year)) .leap else .not_leap;
137 var days: u32 = 0;
143 fn daysInYear(year: u16, max_month: u4) u9 {
144 const leap_year: std.time.epoch.YearLeapKind = if (std.time.epoch.isLeapYear(year)) .leap else .not_leap;
145 var days: u9 = 0;
138146 var month: u4 = 0;
139 while (month < maxMonth) : (month += 1) {
140 days += std.time.epoch.getDaysInMonth(leapYear, @enumFromInt(month + 1));
147 while (month < max_month) : (month += 1) {
148 days += std.time.epoch.getDaysInMonth(leap_year, @enumFromInt(month + 1));
141149 }
142150 return days;
143151 }
......@@ -151,9 +159,9 @@ pub const Time = extern struct {
151159 }
152160
153161 days += daysInYear(self.year, @as(u4, @intCast(self.month)) - 1) + self.day;
154 const hours = self.hour + (days * 24);
155 const minutes = self.minute + (hours * 60);
156 const seconds = self.second + (minutes * std.time.s_per_min);
162 const hours: u64 = self.hour + (days * 24);
163 const minutes: u64 = self.minute + (hours * 60);
164 const seconds: u64 = self.second + (minutes * std.time.s_per_min);
157165 return self.nanosecond + (seconds * std.time.ns_per_s);
158166 }
159167};
lib/std/time.zig+33-31
......@@ -135,7 +135,7 @@ pub const Instant = struct {
135135 const clock_id = switch (builtin.os.tag) {
136136 .windows => {
137137 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
138 return Instant{ .timestamp = windows.QueryPerformanceCounter() };
138 return .{ .timestamp = windows.QueryPerformanceCounter() };
139139 },
140140 .wasi => {
141141 var ns: std.os.wasi.timestamp_t = undefined;
......@@ -147,7 +147,7 @@ pub const Instant = struct {
147147 var value: std.os.uefi.Time = undefined;
148148 const status = std.os.uefi.system_table.runtime_services.getTime(&value, null);
149149 if (status != .success) return error.Unsupported;
150 return Instant{ .timestamp = value.toEpoch() };
150 return .{ .timestamp = value.toEpoch() };
151151 },
152152 // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while
153153 // suspended.
......@@ -185,36 +185,38 @@ pub const Instant = struct {
185185 /// This assumes that the `earlier` Instant represents a moment in time before or equal to `self`.
186186 /// This also assumes that the time that has passed between both Instants fits inside a u64 (~585 yrs).
187187 pub fn since(self: Instant, earlier: Instant) u64 {
188 if (builtin.os.tag == .windows) {
189 // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
190 // (a read-only page of info updated and mapped by the kernel to all processes):
191 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
192 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
193 const qpc = self.timestamp - earlier.timestamp;
194 const qpf = windows.QueryPerformanceFrequency();
195
196 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
197 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
198 const common_qpf = 10_000_000;
199 if (qpf == common_qpf) {
200 return qpc * (ns_per_s / common_qpf);
201 }
202
203 // Convert to ns using fixed point.
204 const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf));
205 const result = (@as(u96, qpc) * scale) >> 32;
206 return @as(u64, @truncate(result));
207 }
208
209 // WASI timestamps are directly in nanoseconds
210 if (builtin.os.tag == .wasi) {
211 return self.timestamp - earlier.timestamp;
188 switch (builtin.os.tag) {
189 .windows => {
190 // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
191 // (a read-only page of info updated and mapped by the kernel to all processes):
192 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
193 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
194 const qpc = self.timestamp - earlier.timestamp;
195 const qpf = windows.QueryPerformanceFrequency();
196
197 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
198 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
199 const common_qpf = 10_000_000;
200 if (qpf == common_qpf) {
201 return qpc * (ns_per_s / common_qpf);
202 }
203
204 // Convert to ns using fixed point.
205 const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf));
206 const result = (@as(u96, qpc) * scale) >> 32;
207 return @as(u64, @truncate(result));
208 },
209 .uefi, .wasi => {
210 // UEFI and WASI timestamps are directly in nanoseconds
211 return self.timestamp - earlier.timestamp;
212 },
213 else => {
214 // Convert timespec diff to ns
215 const seconds = @as(u64, @intCast(self.timestamp.sec - earlier.timestamp.sec));
216 const elapsed = (seconds * ns_per_s) + @as(u32, @intCast(self.timestamp.nsec));
217 return elapsed - @as(u32, @intCast(earlier.timestamp.nsec));
218 },
212219 }
213
214 // Convert timespec diff to ns
215 const seconds = @as(u64, @intCast(self.timestamp.sec - earlier.timestamp.sec));
216 const elapsed = (seconds * ns_per_s) + @as(u32, @intCast(self.timestamp.nsec));
217 return elapsed - @as(u32, @intCast(earlier.timestamp.nsec));
218220 }
219221};
220222