Commit 988fff260e

Jakub Konka <kubkon@jakubkonka.com>
2022-11-29 13:08:17
windows: map CP 40xx registry values to system ID registers
1 parent 152202d
Changed files (1)
lib
std
zig
lib/std/zig/system/windows.zig
@@ -51,23 +51,22 @@ pub fn detectRuntimeVersion() WindowsVersion {
 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
 const max_value_len = 2048;
 
-const RegistryPair = struct {
-    key: []const u8,
-    value: std.os.windows.ULONG,
-};
+fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
+    const ArgsType = @TypeOf(args);
+    const args_type_info = @typeInfo(ArgsType);
+
+    if (args_type_info != .Struct) {
+        @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
+    }
+
+    const fields_info = args_type_info.Struct.fields;
 
-fn getCpuInfoFromRegistry(
-    core: usize,
-    comptime pairs_num: comptime_int,
-    comptime pairs: [pairs_num]RegistryPair,
-    out_buf: *[pairs_num][max_value_len]u8,
-) !void {
     // Originally, I wanted to issue a single call with a more complex table structure such that we
     // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into
     // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would
     // end up pulling only the last CPU core info, overwriting everything else.
     // If anyone can come up with a solution to this, please do!
-    const table_size = 1 + pairs.len;
+    const table_size = 1 + fields_info.len;
     var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
 
     const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
@@ -90,9 +89,9 @@ fn getCpuInfoFromRegistry(
         .DefaultLength = 0,
     };
 
-    inline for (pairs) |pair, i| {
+    inline for (fields_info) |field, i| {
         const ctx: *anyopaque = blk: {
-            switch (pair.value) {
+            switch (@field(args, field.name).value_type) {
                 REG.SZ,
                 REG.EXPAND_SZ,
                 REG.MULTI_SZ,
@@ -121,12 +120,15 @@ fn getCpuInfoFromRegistry(
                 else => unreachable,
             }
         };
-        const key_name = std.unicode.utf8ToUtf16LeStringLiteral(pair.key);
+
+        var key_buf: [max_value_len / 2 + 1]u16 = undefined;
+        const key_len = try std.unicode.utf8ToUtf16Le(&key_buf, @field(args, field.name).key);
+        key_buf[key_len] = 0;
 
         table[i + 1] = .{
             .QueryRoutine = null,
             .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
-            .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
+            .Name = key_buf[0..key_len :0],
             .EntryContext = ctx,
             .DefaultType = REG.NONE,
             .DefaultData = null,
@@ -154,14 +156,15 @@ fn getCpuInfoFromRegistry(
     );
     switch (res) {
         .SUCCESS => {
-            inline for (pairs) |pair, i| switch (pair.value) {
+            inline for (fields_info) |field, i| switch (@field(args, field.name).value_type) {
                 REG.SZ,
                 REG.EXPAND_SZ,
                 REG.MULTI_SZ,
                 => {
+                    var buf = @field(args, field.name).value_buf;
                     const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[i + 1].EntryContext);
-                    const len = try std.unicode.utf16leToUtf8(out_buf[i][0..], entry.Buffer[0 .. entry.Length / 2]);
-                    out_buf[i][len] = 0;
+                    const len = try std.unicode.utf16leToUtf8(buf, entry.Buffer[0 .. entry.Length / 2]);
+                    buf[len] = 0;
                 },
 
                 REG.DWORD,
@@ -169,12 +172,12 @@ fn getCpuInfoFromRegistry(
                 REG.QWORD,
                 => {
                     const entry = @ptrCast([*]align(1) const u8, table[i + 1].EntryContext);
-                    switch (pair.value) {
+                    switch (@field(args, field.name).value_type) {
                         REG.DWORD, REG.DWORD_BIG_ENDIAN => {
-                            mem.copy(u8, out_buf[i][0..4], entry[0..4]);
+                            mem.copy(u8, @field(args, field.name).value_buf[0..4], entry[0..4]);
                         },
                         REG.QWORD => {
-                            mem.copy(u8, out_buf[i][0..8], entry[0..8]);
+                            mem.copy(u8, @field(args, field.name).value_buf[0..8], entry[0..8]);
                         },
                         else => unreachable,
                     }
@@ -197,7 +200,7 @@ fn getCpuCount() usize {
     return std.os.windows.peb().NumberOfProcessors;
 }
 
-const ArmCpuInfoImpl = struct {
+const ArmCpuInfoParser = struct {
     cores: [4]CoreInfo = undefined,
     core_no: usize = 0,
     have_fields: usize = 0,
@@ -205,38 +208,26 @@ const ArmCpuInfoImpl = struct {
     const CoreInfo = @import("arm.zig").CoreInfo;
     const cpu_models = @import("arm.zig").cpu_models;
 
-    const Data = struct {
-        cp_4000: []const u8,
-        identifier: []const u8,
-    };
-
-    fn parseDataHook(self: *ArmCpuInfoImpl, data: Data) !void {
+    fn parseFeaturesFromRegisters(self: *ArmCpuInfoParser, registers: [12]u64) !void {
         const info = &self.cores[self.core_no];
         info.* = .{};
 
-        // CPU part
-        info.part = mem.readIntLittle(u16, data.cp_4000[0..2]) >> 4;
-        self.have_fields += 1;
-
-        // CPU implementer
-        info.implementer = data.cp_4000[3];
-        self.have_fields += 1;
-
-        var tokens = mem.tokenize(u8, data.identifier, " ");
-        while (tokens.next()) |token| {
-            if (mem.eql(u8, "Family", token)) {
-                // CPU architecture
-                const family = tokens.next() orelse continue;
-                info.architecture = try std.fmt.parseInt(u8, family, 10);
-                self.have_fields += 1;
-                break;
-            }
-        } else return;
+        for (registers) |register| {
+            std.log.warn("{x}", .{register});
+        }
+
+        // // CPU part
+        // info.part = mem.readIntLittle(u16, data.cp_4000[0..2]) >> 4;
+        // self.have_fields += 1;
+
+        // // CPU implementer
+        // info.implementer = data.cp_4000[3];
+        // self.have_fields += 1;
 
-        self.addOne();
+        // self.addOne();
     }
 
-    fn addOne(self: *ArmCpuInfoImpl) void {
+    fn addOne(self: *ArmCpuInfoParser) void {
         if (self.have_fields == 3 and self.core_no < self.cores.len) {
             if (self.core_no > 0) {
                 // Deduplicate the core info.
@@ -249,7 +240,7 @@ const ArmCpuInfoImpl = struct {
         }
     }
 
-    fn finalize(self: ArmCpuInfoImpl, arch: Target.Cpu.Arch) ?Target.Cpu {
+    fn finalize(self: ArmCpuInfoParser, arch: Target.Cpu.Arch) ?Target.Cpu {
         if (self.core_no == 0) return null;
 
         const is_64bit = switch (arch) {
@@ -271,36 +262,49 @@ const ArmCpuInfoImpl = struct {
             .features = model.features,
         };
     }
-};
-
-const ArmCpuInfoParser = CpuInfoParser(ArmCpuInfoImpl);
-
-fn CpuInfoParser(comptime impl: anytype) type {
-    return struct {
-        fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
-            var obj: impl = .{};
-            var out_buf: [2][max_value_len]u8 = undefined;
 
-            var i: usize = 0;
-            while (i < getCpuCount()) : (i += 1) {
-                try getCpuInfoFromRegistry(i, 2, .{
-                    .{ .key = "CP 4000", .value = REG.QWORD },
-                    .{ .key = "Identifier", .value = REG.SZ },
-                }, &out_buf);
-
-                const cp_4000 = out_buf[0][0..8];
-                const identifier = mem.sliceTo(out_buf[1][0..], 0);
-
-                try obj.parseDataHook(.{
-                    .cp_4000 = cp_4000,
-                    .identifier = identifier,
-                });
-            }
-
-            return obj.finalize(arch);
+    fn parse(arch: Target.Cpu.Arch) !?Target.Cpu {
+        var obj: ArmCpuInfoParser = .{};
+
+        // Backing datastore
+        var registers: [12]u64 = undefined;
+
+        var i: usize = 0;
+        while (i < getCpuCount()) : (i += 1) {
+            // Registry key to system ID register mapping
+            // CP 4000 -> MIDR_EL1
+            // CP 4020 -> ID_AA64PFR0_EL1
+            // CP 4021 -> ID_AA64PFR1_EL1
+            // CP 4028 -> ID_AA64DFR0_EL1
+            // CP 4029 -> ID_AA64DFR1_EL1
+            // CP 402C -> ID_AA64AFR0_EL1
+            // CP 402D -> ID_AA64AFR1_EL1
+            // CP 4030 -> ID_AA64ISAR0_EL1
+            // CP 4031 -> ID_AA64ISAR1_EL1
+            // CP 4038 -> ID_AA64MMFR0_EL1
+            // CP 4039 -> ID_AA64MMFR1_EL1
+            // CP 403A -> ID_AA64MMFR2_EL1
+            try getCpuInfoFromRegistry(i, .{
+                .{ .key = "CP 4000", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[0]) },
+                .{ .key = "CP 4020", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[1]) },
+                .{ .key = "CP 4021", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[2]) },
+                .{ .key = "CP 4028", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[3]) },
+                .{ .key = "CP 4029", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[4]) },
+                .{ .key = "CP 402C", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[5]) },
+                .{ .key = "CP 402D", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[6]) },
+                .{ .key = "CP 4030", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[7]) },
+                .{ .key = "CP 4031", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[8]) },
+                .{ .key = "CP 4038", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[9]) },
+                .{ .key = "CP 4039", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[10]) },
+                .{ .key = "CP 403A", .value_type = REG.QWORD, .value_buf = @ptrCast(*[8]u8, &registers[11]) },
+            });
+
+            try obj.parseFeaturesFromRegisters(registers);
         }
-    };
-}
+
+        return obj.finalize(arch);
+    }
+};
 
 /// If the fine-grained detection of CPU features via Win registry fails,
 /// 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 {
 
 pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
     const current_arch = builtin.cpu.arch;
-    switch (current_arch) {
-        .aarch64, .aarch64_be, .aarch64_32 => {
-            return ArmCpuInfoParser.parse(current_arch) catch genericCpuAndNativeFeatures(current_arch);
-        },
-        else => return null,
-    }
+    const cpu: ?Target.Cpu = switch (current_arch) {
+        .aarch64, .aarch64_be, .aarch64_32 => ArmCpuInfoParser.parse(current_arch) catch null,
+        else => null,
+    };
+    return cpu orelse genericCpuAndNativeFeatures(current_arch);
 }