| ... | @@ -93,58 +93,33 @@ const Armv8CpuInfoImpl = struct { | ... | @@ -93,58 +93,33 @@ const Armv8CpuInfoImpl = struct { |
| 93 | } | 93 | } |
| 94 | }; | 94 | }; |
| 95 | | 95 | |
| 96 | fn 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 |
| | 100 | const max_value_len = 2048; |
| | 101 | |
| | 102 | const RegistryPair = struct { |
| | 103 | key: []const u8, |
| | 104 | value: std.os.windows.ULONG, |
| | 105 | }; |
| 98 | | 106 | |
| | 107 | fn 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 we | 113 | // 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 into | 114 | // 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 would | 115 | // 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; |
| 106 | | 120 | |
| 107 | const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor"); | 121 | const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor"); |
| 108 | | 122 | |
| 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 | }; |
| 165 | | 140 | |
| 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 | } |
| 175 | | 212 | |
| 176 | // Table sentinel | 213 | // 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) { |
| 197 | | 234 | 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 | }, |
| 207 | | 244 | |
| 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]); |
| 219 | | 256 | }, |
| 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 strings | 276 | // Parse the models from strings |
| 235 | var parser = Armv8CpuInfoImpl{}; | 277 | var parser = Armv8CpuInfoImpl{}; |
| 236 | | 278 | |
| | 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 }, |
| 241 | | 285 | .{ .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 | } |
| 245 | | 294 | |
| 246 | return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64); | 295 | return parser.finalize() orelse Target.Cpu.Model.generic(.aarch64); |