authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-28 10:41:39+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-28 17:07:34+01:00
log7fbd2955fae13cdc184dbd648743fb187b3ce33d
tree15459c6f6be6679b2ab454502212822c4e2f9d49
parentd64d7aaac7650d79a6f5a5c5d0db6730b6a11b2c

windows: pull QWORD and SZ identifiers from registry in one syscall

At the same time, do not assume the values necessarily exist, and use defaults as markers for the lack of keys in the registry.

1 files changed, 131 insertions(+), 82 deletions(-)

lib/std/zig/system/windows.zig+131-82
......@@ -93,58 +93,33 @@ const Armv8CpuInfoImpl = struct {
9393 }
9494};
9595
96fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std.os.windows.ULONG) ![]const u8 {
97 const key_name = std.unicode.utf8ToUtf16LeStringLiteral(key);
96// Technically, a registry value can be as long as 1MB. However, MS recommends storing
97// values larger than 2048 bytes in a file rather than directly in the registry, and since we
98// are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
99// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
100const max_value_len = 2048;
101
102const RegistryPair = struct {
103 key: []const u8,
104 value: std.os.windows.ULONG,
105};
98106
107fn getCpuInfoFromRegistry(
108 core: usize,
109 comptime pairs_num: comptime_int,
110 comptime pairs: [pairs_num]RegistryPair,
111 out_buf: *[pairs_num][max_value_len]u8,
112) !void {
99113 // Originally, I wanted to issue a single call with a more complex table structure such that we
100114 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
101115 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
102116 // end up pulling only the last CPU core info, overwriting everything else.
103117 // If anyone can come up with a solution to this, please do!
104 const table_size = 2;
118 const table_size = 1 + pairs.len;
105119 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
106120
107121 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
108122
109 // Technically, a registry value can be as long as 1MB. However, MS recommends storing
110 // values larger than 2048 bytes in a file rather than directly in the registry, and since we
111 // are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
112 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
113 const max_value_len = 2048;
114
115 const ctx: *anyopaque = blk: {
116 switch (value_type) {
117 REG.NONE => unreachable,
118
119 REG.SZ,
120 REG.EXPAND_SZ,
121 REG.MULTI_SZ,
122 => {
123 var buf: [max_value_len / 2]u16 = undefined;
124 var unicode = std.os.windows.UNICODE_STRING{
125 .Length = max_value_len,
126 .MaximumLength = max_value_len,
127 .Buffer = &buf,
128 };
129 break :blk &unicode;
130 },
131
132 REG.DWORD,
133 REG.DWORD_BIG_ENDIAN,
134 => {
135 var buf: [4]u8 = undefined;
136 break :blk &buf;
137 },
138
139 REG.QWORD => {
140 var buf: [8]u8 = undefined;
141 break :blk &buf;
142 },
143
144 else => unreachable,
145 }
146 };
147
148123 const max_cpu_buf = 4;
149124 var next_cpu_buf: [max_cpu_buf]u8 = undefined;
150125 const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{core});
......@@ -163,15 +138,77 @@ fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std
163138 .DefaultLength = 0,
164139 };
165140
166 table[1] = .{
167 .QueryRoutine = null,
168 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
169 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
170 .EntryContext = ctx,
171 .DefaultType = REG.NONE,
172 .DefaultData = null,
173 .DefaultLength = 0,
174 };
141 inline for (pairs) |pair, i| {
142 const ctx: *anyopaque = blk: {
143 switch (pair.value) {
144 REG.SZ,
145 REG.EXPAND_SZ,
146 REG.MULTI_SZ,
147 => {
148 var buf: [max_value_len / 2]u16 = undefined;
149 var unicode = std.os.windows.UNICODE_STRING{
150 .Length = 0,
151 .MaximumLength = max_value_len,
152 .Buffer = &buf,
153 };
154 break :blk &unicode;
155 },
156
157 REG.DWORD,
158 REG.DWORD_BIG_ENDIAN,
159 => {
160 var buf: [4]u8 = undefined;
161 break :blk &buf;
162 },
163
164 REG.QWORD => {
165 var buf: [8]u8 = undefined;
166 break :blk &buf;
167 },
168
169 else => unreachable,
170 }
171 };
172 const default: struct { ptr: *anyopaque, len: u32 } = blk: {
173 switch (pair.value) {
174 REG.SZ,
175 REG.EXPAND_SZ,
176 REG.MULTI_SZ,
177 => {
178 const def = std.unicode.utf8ToUtf16LeStringLiteral("Unknown");
179 var buf: [def.len + 1]u16 = undefined;
180 mem.copy(u16, &buf, def);
181 buf[def.len] = 0;
182 break :blk .{ .ptr = &buf, .len = @intCast(u32, (buf.len + 1) * 2) };
183 },
184
185 REG.DWORD,
186 REG.DWORD_BIG_ENDIAN,
187 => {
188 var buf: [4]u8 = [_]u8{0} ** 4;
189 break :blk .{ .ptr = &buf, .len = 4 };
190 },
191
192 REG.QWORD => {
193 var buf: [8]u8 = [_]u8{0} ** 8;
194 break :blk .{ .ptr = &buf, .len = 8 };
195 },
196
197 else => unreachable,
198 }
199 };
200 const key_name = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);
201
202 table[i + 1] = .{
203 .QueryRoutine = null,
204 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT,
205 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
206 .EntryContext = ctx,
207 .DefaultType = pair.value,
208 .DefaultData = default.ptr,
209 .DefaultLength = default.len,
210 };
211 }
175212
176213 // Table sentinel
177214 table[table_size] = .{
......@@ -192,32 +229,37 @@ fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std
192229 null,
193230 );
194231 switch (res) {
195 .SUCCESS => switch (value_type) {
196 REG.NONE => unreachable,
197
198 REG.SZ,
199 REG.EXPAND_SZ,
200 REG.MULTI_SZ,
201 => {
202 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[1].EntryContext);
203 var identifier_buf: [max_value_len]u8 = undefined;
204 const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]);
205 return identifier_buf[0..len];
206 },
207
208 REG.DWORD,
209 REG.DWORD_BIG_ENDIAN,
210 REG.QWORD,
211 => {
212 const entry = @ptrCast([*]align(1) const u8, table[1].EntryContext);
213 switch (value_type) {
214 REG.DWORD, REG.DWORD_BIG_ENDIAN => return entry[0..4],
215 REG.QWORD => return entry[0..8],
216 else => unreachable,
217 }
218 },
219
220 else => unreachable,
232 .SUCCESS => {
233 inline for (pairs) |pair, i| switch (pair.value) {
234 REG.NONE => unreachable,
235
236 REG.SZ,
237 REG.EXPAND_SZ,
238 REG.MULTI_SZ,
239 => {
240 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);
241 const len = try std.unicode.utf16leToUtf8(out_buf[i][0..], entry.Buffer[0 .. entry.Length / 2]);
242 out_buf[i][len] = 0;
243 },
244
245 REG.DWORD,
246 REG.DWORD_BIG_ENDIAN,
247 REG.QWORD,
248 => {
249 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);
250 switch (pair.value) {
251 REG.DWORD, REG.DWORD_BIG_ENDIAN => {
252 mem.copy(u8, out_buf[i][0..4], entry[0..4]);
253 },
254 REG.QWORD => {
255 mem.copy(u8, out_buf[i][0..8], entry[0..8]);
256 },
257 else => unreachable,
258 }
259 },
260
261 else => unreachable,
262 };
221263 },
222264 else => return std.os.windows.unexpectedStatus(res),
223265 }
......@@ -234,13 +276,20 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model {
234276 // Parse the models from strings
235277 var parser = Armv8CpuInfoImpl{};
236278
279 var out_buf: [3][max_value_len]u8 = undefined;
280
237281 var i: usize = 0;
238282 while (i < cpu_count) : (i += 1) {
239 const identifier = try getCpuInfoFromRegistry(i, "Identifier", REG.SZ);
240 parser.parseOne(identifier);
241
242 const hex = try getCpuInfoFromRegistry(i, "CP 4000", REG.QWORD);
243 std.log.warn("{d} => {x}", .{ i, std.fmt.fmtSliceHexLower(hex) });
283 try getCpuInfoFromRegistry(i, 3, .{
284 .{ .key = "CP 4000", .value = REG.QWORD },
285 .{ .key = "Identifier", .value = REG.SZ },
286 .{ .key = "VendorIdentifier", .value = REG.SZ },
287 }, &out_buf);
288
289 const hex = out_buf[0][0..8];
290 const identifier = mem.sliceTo(out_buf[1][0..], 0);
291 const vendor_identifier = mem.sliceTo(out_buf[2][0..], 0);
292 std.log.warn("{d} => {x}, {s}, {s}", .{ i, std.fmt.fmtSliceHexLower(hex), identifier, vendor_identifier });
244293 }
245294
246295 return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64);