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