| ... | @@ -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-limits | 51 | // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits |
| 52 | const max_value_len = 2048; | 52 | const max_value_len = 2048; |
| 53 | | 53 | |
| 54 | const RegistryPair = struct { | 54 | fn 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; |
| 58 | | 63 | |
| 59 | fn 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 we | 64 | // 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 into | 65 | // 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 would | 66 | // 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; |
| 72 | | 71 | |
| 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 | }; |
| 92 | | 91 | |
| 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; |
| 125 | | 127 | |
| 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 | }, |
| 166 | | 169 | |
| 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 | } |
| 199 | | 202 | |
| 200 | const ArmCpuInfoImpl = struct { | 203 | const 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; |
| 207 | | 210 | |
| 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.* = .{}; |
| 216 | | 214 | |
| 217 | // CPU part | 215 | 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 | } |
| 220 | | 218 | |
| 221 | // CPU implementer | 219 | // // 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; |
| 224 | | 222 | |
| 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; | | |
| 235 | | 226 | |
| 236 | self.addOne(); | 227 | // self.addOne(); |
| 237 | } | 228 | } |
| 238 | | 229 | |
| 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 | } |
| 251 | | 242 | |
| 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; |
| 254 | | 245 | |
| 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 | | | |
| 276 | const ArmCpuInfoParser = CpuInfoParser(ArmCpuInfoImpl); | | |
| 277 | | | |
| 278 | fn 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; | | |
| 283 | | 265 | |
| 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 | |
| 290 | | 272 | 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 |
| 293 | | 275 | // 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 |
| 299 | | 281 | // 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 | }; |
| 304 | | 308 | |
| 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 set | 310 | /// 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 { |
| 333 | | 337 | |
| 334 | pub fn detectNativeCpuAndFeatures() ?Target.Cpu { | 338 | pub 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 | } |