authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-29 13:08:17+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-29 19:24:42+01:00
log988fff260efeb2abfc01a0a565a2c4f1e348a895
treec8c7dbd4d1b06dcf9c8763a425e3467729f874eb
parent152202da777ac90693181226ceb164ef6e30cf89

windows: map CP 40xx registry values to system ID registers


1 files changed, 86 insertions(+), 83 deletions(-)

lib/std/zig/system/windows.zig+86-83
...@@ -51,23 +51,22 @@ pub fn detectRuntimeVersion() WindowsVersion {...@@ -51,23 +51,22 @@ pub fn detectRuntimeVersion() WindowsVersion {
51// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits51// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
52const max_value_len = 2048;52const max_value_len = 2048;
5353
54const RegistryPair = struct {54fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
55 key: []const u8,55 const ArgsType = @TypeOf(args);
56 value: std.os.windows.ULONG,56 const args_type_info = @typeInfo(ArgsType);
57};57
58 if (args_type_info != .Struct) {
59 @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
60 }
61
62 const fields_info = args_type_info.Struct.fields;
5863
59fn getCpuInfoFromRegistry(
60 core: usize,
61 comptime pairs_num: comptime_int,
62 comptime pairs: [pairs_num]RegistryPair,
63 out_buf: *[pairs_num][max_value_len]u8,
64) !void {
65 // Originally, I wanted to issue a single call with a more complex table structure such that we64 // Originally, I wanted to issue a single call with a more complex table structure such that we
66 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into65 // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
67 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would66 // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
68 // end up pulling only the last CPU core info, overwriting everything else.67 // end up pulling only the last CPU core info, overwriting everything else.
69 // If anyone can come up with a solution to this, please do!68 // If anyone can come up with a solution to this, please do!
70 const table_size = 1 + pairs.len;69 const table_size = 1 + fields_info.len;
71 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;70 var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
7271
73 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");72 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
...@@ -90,9 +89,9 @@ fn getCpuInfoFromRegistry(...@@ -90,9 +89,9 @@ fn getCpuInfoFromRegistry(
90 .DefaultLength = 0,89 .DefaultLength = 0,
91 };90 };
9291
93 inline for (pairs) |pair, i| {92 inline for (fields_info) |field, i| {
94 const ctx: *anyopaque = blk: {93 const ctx: *anyopaque = blk: {
95 switch (pair.value) {94 switch (@field(args, field.name).value_type) {
96 REG.SZ,95 REG.SZ,
97 REG.EXPAND_SZ,96 REG.EXPAND_SZ,
98 REG.MULTI_SZ,97 REG.MULTI_SZ,
...@@ -121,12 +120,15 @@ fn getCpuInfoFromRegistry(...@@ -121,12 +120,15 @@ fn getCpuInfoFromRegistry(
121 else => unreachable,120 else => unreachable,
122 }121 }
123 };122 };
124 const key_name = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);123
124 var key_buf: [max_value_len / 2 + 1]u16 = undefined;
125 const key_len = try std.unicode.utf8ToUtf16Le(&key_buf, @field(args, field.name).key);
126 key_buf[key_len] = 0;
125127
126 table[i + 1] = .{128 table[i + 1] = .{
127 .QueryRoutine = null,129 .QueryRoutine = null,
128 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,130 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
129 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),131 .Name = key_buf[0..key_len :0],
130 .EntryContext = ctx,132 .EntryContext = ctx,
131 .DefaultType = REG.NONE,133 .DefaultType = REG.NONE,
132 .DefaultData = null,134 .DefaultData = null,
...@@ -154,14 +156,15 @@ fn getCpuInfoFromRegistry(...@@ -154,14 +156,15 @@ fn getCpuInfoFromRegistry(
154 );156 );
155 switch (res) {157 switch (res) {
156 .SUCCESS => {158 .SUCCESS => {
157 inline for (pairs) |pair, i| switch (pair.value) {159 inline for (fields_info) |field, i| switch (@field(args, field.name).value_type) {
158 REG.SZ,160 REG.SZ,
159 REG.EXPAND_SZ,161 REG.EXPAND_SZ,
160 REG.MULTI_SZ,162 REG.MULTI_SZ,
161 => {163 => {
164 var buf = @field(args, field.name).value_buf;
162 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);165 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);
163 const len = try std.unicode.utf16leToUtf8(out_buf[i][0..], entry.Buffer[0 .. entry.Length / 2]);166 const len = try std.unicode.utf16leToUtf8(buf, entry.Buffer[0 .. entry.Length / 2]);
164 out_buf[i][len] = 0;167 buf[len] = 0;
165 },168 },
166169
167 REG.DWORD,170 REG.DWORD,
...@@ -169,12 +172,12 @@ fn getCpuInfoFromRegistry(...@@ -169,12 +172,12 @@ fn getCpuInfoFromRegistry(
169 REG.QWORD,172 REG.QWORD,
170 => {173 => {
171 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);174 const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);
172 switch (pair.value) {175 switch (@field(args, field.name).value_type) {
173 REG.DWORD, REG.DWORD_BIG_ENDIAN => {176 REG.DWORD, REG.DWORD_BIG_ENDIAN => {
174 mem.copy(u8, out_buf[i][0..4], entry[0..4]);177 mem.copy(u8, @field(args, field.name).value_buf[0..4], entry[0..4]);
175 },178 },
176 REG.QWORD => {179 REG.QWORD => {
177 mem.copy(u8, out_buf[i][0..8], entry[0..8]);180 mem.copy(u8, @field(args, field.name).value_buf[0..8], entry[0..8]);
178 },181 },
179 else => unreachable,182 else => unreachable,
180 }183 }
...@@ -197,7 +200,7 @@ fn getCpuCount() usize {...@@ -197,7 +200,7 @@ fn getCpuCount() usize {
197 return std.os.windows.peb().NumberOfProcessors;200 return std.os.windows.peb().NumberOfProcessors;
198}201}
199202
200const ArmCpuInfoImpl = struct {203const ArmCpuInfoParser = struct {
201 cores: [4]CoreInfo = undefined,204 cores: [4]CoreInfo = undefined,
202 core_no: usize = 0,205 core_no: usize = 0,
203 have_fields: usize = 0,206 have_fields: usize = 0,
...@@ -205,38 +208,26 @@ const ArmCpuInfoImpl = struct {...@@ -205,38 +208,26 @@ const ArmCpuInfoImpl = struct {
205 const CoreInfo = @import("arm.zig").CoreInfo;208 const CoreInfo = @import("arm.zig").CoreInfo;
206 const cpu_models = @import("arm.zig").cpu_models;209 const cpu_models = @import("arm.zig").cpu_models;
207210
208 const Data = struct {211 fn parseFeaturesFromRegisters(self: *ArmCpuInfoParser, registers: [12]u64) !void {
209 cp_4000: []const u8,
210 identifier: []const u8,
211 };
212
213 fn parseDataHook(self: *ArmCpuInfoImpl, data: Data) !void {
214 const info = &self.cores[self.core_no];212 const info = &self.cores[self.core_no];
215 info.* = .{};213 info.* = .{};
216214
217 // CPU part215 for (registers) |register| {
218 info.part = mem.readIntLittle(u16, data.cp_4000[0..2]) >> 4;216 std.log.warn("{x}", .{register});
219 self.have_fields += 1;217 }
220218
221 // CPU implementer219 // // CPU part
222 info.implementer = data.cp_4000[3];220 // info.part = mem.readIntLittle(u16, data.cp_4000[0..2]) >> 4;
223 self.have_fields += 1;221 // self.have_fields += 1;
224222
225 var tokens = mem.tokenize(u8, data.identifier, " ");223 // // CPU implementer
226 while (tokens.next()) |token| {224 // info.implementer = data.cp_4000[3];
227 if (mem.eql(u8, "Family", token)) {225 // self.have_fields += 1;
228 // CPU architecture
229 const family = tokens.next() orelse continue;
230 info.architecture = try std.fmt.parseInt(u8, family, 10);
231 self.have_fields += 1;
232 break;
233 }
234 } else return;
235226
236 self.addOne();227 // self.addOne();
237 }228 }
238229
239 fn addOne(self: *ArmCpuInfoImpl) void {230 fn addOne(self: *ArmCpuInfoParser) void {
240 if (self.have_fields == 3 and self.core_no < self.cores.len) {231 if (self.have_fields == 3 and self.core_no < self.cores.len) {
241 if (self.core_no > 0) {232 if (self.core_no > 0) {
242 // Deduplicate the core info.233 // Deduplicate the core info.
...@@ -249,7 +240,7 @@ const ArmCpuInfoImpl = struct {...@@ -249,7 +240,7 @@ const ArmCpuInfoImpl = struct {
249 }240 }
250 }241 }
251242
252 fn finalize(self: ArmCpuInfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {243 fn finalize(self: ArmCpuInfoParser, arch: Target.Cpu.Arch) ?Target.Cpu {
253 if (self.core_no == 0) return null;244 if (self.core_no == 0) return null;
254245
255 const is_64bit = switch (arch) {246 const is_64bit = switch (arch) {
...@@ -271,36 +262,49 @@ const ArmCpuInfoImpl = struct {...@@ -271,36 +262,49 @@ const ArmCpuInfoImpl = struct {
271 .features = model.features,262 .features = model.features,
272 };263 };
273 }264 }
274};
275
276const ArmCpuInfoParser = CpuInfoParser(ArmCpuInfoImpl);
277
278fn CpuInfoParser(comptime impl: anytype) type {
279 return struct {
280 fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
281 var obj: impl = .{};
282 var out_buf: [2][max_value_len]u8 = undefined;
283265
284 var i: usize = 0;266 fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
285 while (i < getCpuCount()) : (i += 1) {267 var obj: ArmCpuInfoParser = .{};
286 try getCpuInfoFromRegistry(i, 2, .{268
287 .{ .key = "CP 4000", .value = REG.QWORD },269 // Backing datastore
288 .{ .key = "Identifier", .value = REG.SZ },270 var registers: [12]u64 = undefined;
289 }, &out_buf);271
290272 var i: usize = 0;
291 const cp_4000 = out_buf[0][0..8];273 while (i < getCpuCount()) : (i += 1) {
292 const identifier = mem.sliceTo(out_buf[1][0..], 0);274 // Registry key to system ID register mapping
293275 // CP 4000 -> MIDR_EL1
294 try obj.parseDataHook(.{276 // CP 4020 -> ID_AA64PFR0_EL1
295 .cp_4000 = cp_4000,277 // CP 4021 -> ID_AA64PFR1_EL1
296 .identifier = identifier,278 // CP 4028 -> ID_AA64DFR0_EL1
297 });279 // CP 4029 -> ID_AA64DFR1_EL1
298 }280 // CP 402C -> ID_AA64AFR0_EL1
299281 // CP 402D -> ID_AA64AFR1_EL1
300 return obj.finalize(arch);282 // CP 4030 -> ID_AA64ISAR0_EL1
283 // CP 4031 -> ID_AA64ISAR1_EL1
284 // CP 4038 -> ID_AA64MMFR0_EL1
285 // CP 4039 -> ID_AA64MMFR1_EL1
286 // CP 403A -> ID_AA64MMFR2_EL1
287 try getCpuInfoFromRegistry(i, .{
288 .{ .key = "CP 4000", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[0]) },
289 .{ .key = "CP 4020", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[1]) },
290 .{ .key = "CP 4021", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[2]) },
291 .{ .key = "CP 4028", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[3]) },
292 .{ .key = "CP 4029", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[4]) },
293 .{ .key = "CP 402C", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[5]) },
294 .{ .key = "CP 402D", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[6]) },
295 .{ .key = "CP 4030", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[7]) },
296 .{ .key = "CP 4031", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[8]) },
297 .{ .key = "CP 4038", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[9]) },
298 .{ .key = "CP 4039", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[10]) },
299 .{ .key = "CP 403A", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[11]) },
300 });
301
302 try obj.parseFeaturesFromRegisters(registers);
301 }303 }
302 };304
303}305 return obj.finalize(arch);
306 }
307};
304308
305/// If the fine-grained detection of CPU features via Win registry fails,309/// If the fine-grained detection of CPU features via Win registry fails,
306/// we fallback to a generic CPU model but we override the feature set310/// we fallback to a generic CPU model but we override the feature set
...@@ -333,10 +337,9 @@ fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu {...@@ -333,10 +337,9 @@ fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu {
333337
334pub fn detectNativeCpuAndFeatures() ?Target.Cpu {338pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
335 const current_arch = builtin.cpu.arch;339 const current_arch = builtin.cpu.arch;
336 switch (current_arch) {340 const cpu: ?Target.Cpu = switch (current_arch) {
337 .aarch64, .aarch64_be, .aarch64_32 => {341 .aarch64, .aarch64_be, .aarch64_32 => ArmCpuInfoParser.parse(current_arch) catch null,
338 return ArmCpuInfoParser.parse(current_arch) catch genericCpuAndNativeFeatures(current_arch);342 else => null,
339 },343 };
340 else => return null,344 return cpu orelse genericCpuAndNativeFeatures(current_arch);
341 }
342}345}