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 {...@@ -93,58 +93,33 @@ const Armv8CpuInfoImpl = struct {
93 }93 }
94};94};
9595
96fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std.os.windows.ULONG) ![]const u8 {96// Technically, a registry value can be as long as 1MB. However, MS recommends storing
97 const key_name = std.unicode.utf8ToUtf16LeStringLiteral(key);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 {
99 // Originally, I wanted to issue a single call with a more complex table structure such that we113 // Originally, I wanted to issue a single call with a more complex table structure such that we
100 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into114 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
101 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would115 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
102 // end up pulling only the last CPU core info, overwriting everything else.116 // end up pulling only the last CPU core info, overwriting everything else.
103 // If anyone can come up with a solution to this, please do!117 // If anyone can come up with a solution to this, please do!
104 const table_size = 2;118 const table_size = 1 + pairs.len;
105 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;119 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
106120
107 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");121 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
148 const max_cpu_buf = 4;123 const max_cpu_buf = 4;
149 var next_cpu_buf: [max_cpu_buf]u8 = undefined;124 var next_cpu_buf: [max_cpu_buf]u8 = undefined;
150 const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{core});125 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...@@ -163,15 +138,77 @@ fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std
163 .DefaultLength = 0,138 .DefaultLength = 0,
164 };139 };
165140
166 table[1] = .{141 inline for (pairs) |pair, i| {
167 .QueryRoutine = null,142 const ctx: *anyopaque = blk: {
168 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,143 switch (pair.value) {
169 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),144 REG.SZ,
170 .EntryContext = ctx,145 REG.EXPAND_SZ,
171 .DefaultType = REG.NONE,146 REG.MULTI_SZ,
172 .DefaultData = null,147 => {
173 .DefaultLength = 0,148 var buf: [max_value_len / 2]u16 = undefined;
174 };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
176 // Table sentinel213 // Table sentinel
177 table[table_size] = .{214 table[table_size] = .{
...@@ -192,32 +229,37 @@ fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std...@@ -192,32 +229,37 @@ fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8, value_type: std
192 null,229 null,
193 );230 );
194 switch (res) {231 switch (res) {
195 .SUCCESS => switch (value_type) {232 .SUCCESS => {
196 REG.NONE => unreachable,233 inline for (pairs) |pair, i| switch (pair.value) {
197234 REG.NONE => unreachable,
198 REG.SZ,235
199 REG.EXPAND_SZ,236 REG.SZ,
200 REG.MULTI_SZ,237 REG.EXPAND_SZ,
201 => {238 REG.MULTI_SZ,
202 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[1].EntryContext);239 => {
203 var identifier_buf: [max_value_len]u8 = undefined;240 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);
204 const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]);241 const len = try std.unicode.utf16leToUtf8(out_buf[i][0..], entry.Buffer[0 .. entry.Length / 2]);
205 return identifier_buf[0..len];242 out_buf[i][len] = 0;
206 },243 },
207244
208 REG.DWORD,245 REG.DWORD,
209 REG.DWORD_BIG_ENDIAN,246 REG.DWORD_BIG_ENDIAN,
210 REG.QWORD,247 REG.QWORD,
211 => {248 => {
212 const entry = @ptrCast([*]align(1) const u8, table[1].EntryContext);249 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);
213 switch (value_type) {250 switch (pair.value) {
214 REG.DWORD, REG.DWORD_BIG_ENDIAN => return entry[0..4],251 REG.DWORD, REG.DWORD_BIG_ENDIAN => {
215 REG.QWORD => return entry[0..8],252 mem.copy(u8, out_buf[i][0..4], entry[0..4]);
216 else => unreachable,253 },
217 }254 REG.QWORD => {
218 },255 mem.copy(u8, out_buf[i][0..8], entry[0..8]);
219256 },
220 else => unreachable,257 else => unreachable,
258 }
259 },
260
261 else => unreachable,
262 };
221 },263 },
222 else => return std.os.windows.unexpectedStatus(res),264 else => return std.os.windows.unexpectedStatus(res),
223 }265 }
...@@ -234,13 +276,20 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model {...@@ -234,13 +276,20 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model {
234 // Parse the models from strings276 // Parse the models from strings
235 var parser = Armv8CpuInfoImpl{};277 var parser = Armv8CpuInfoImpl{};
236278
279 var out_buf: [3][max_value_len]u8 = undefined;
280
237 var i: usize = 0;281 var i: usize = 0;
238 while (i < cpu_count) : (i += 1) {282 while (i < cpu_count) : (i += 1) {
239 const identifier = try getCpuInfoFromRegistry(i, "Identifier", REG.SZ);283 try getCpuInfoFromRegistry(i, 3, .{
240 parser.parseOne(identifier);284 .{ .key = "CP 4000", .value = REG.QWORD },
241285 .{ .key = "Identifier", .value = REG.SZ },
242 const hex = try getCpuInfoFromRegistry(i, "CP 4000", REG.QWORD);286 .{ .key = "VendorIdentifier", .value = REG.SZ },
243 std.log.warn("{d} => {x}", .{ i, std.fmt.fmtSliceHexLower(hex) });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 });
244 }293 }
245294
246 return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64);295 return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64);