| ... | @@ -92,20 +92,61 @@ const Armv8CpuInfoImpl = struct { | ... | @@ -92,20 +92,61 @@ const Armv8CpuInfoImpl = struct { |
| 92 | } | 92 | } |
| 93 | }; | 93 | }; |
| 94 | | 94 | |
| 95 | fn detectCpuModelArm64() !*const Target.Cpu.Model { | 95 | fn getCpuInfoFromRegistry(core: usize, comptime key: []const u8) ![]const u8 { |
| 96 | // Pull the CPU identifier from the registry. | 96 | // Technically, a registry value can be as long as 16k u16s. However, MS recommends storing |
| 97 | // Assume max number of cores to be at 8. | 97 | // values larger than 2048 in a file rather than directly in the registry, and since we |
| 98 | const max_cpu_count = 8; | 98 | // are only accessing a system hive \Registry\Machine, we stick to MS guidelines. |
| 99 | const cpu_count = getCpuCount(); | 99 | // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits |
| | 100 | const max_sz_value = 2048; |
| | 101 | const key_name = std.unicode.utf8ToUtf16LeStringLiteral(key); |
| | 102 | |
| | 103 | // Originally, I wanted to issue a single call with a more complex table structure such that we |
| | 104 | // would sequentially visit each CPU#d subkey in the registry and pull the value of interest into |
| | 105 | // a buffer, however, NT seems to be expecting a single buffer per each table meaning we would |
| | 106 | // end up pulling only the last CPU core info, overwriting everything else. |
| | 107 | // If anyone can come up with a solution to this, please do! |
| | 108 | const table_size = 2; |
| | 109 | var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined; |
| 100 | | 110 | |
| 101 | if (cpu_count > max_cpu_count) return error.TooManyCpus; | 111 | const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor"); |
| 102 | | 112 | |
| 103 | const table_size = max_cpu_count * 3; | 113 | var buf: [max_sz_value]u16 = undefined; |
| 104 | const actual_table_size = cpu_count * 3; | 114 | var buf_uni = std.os.windows.UNICODE_STRING{ |
| 105 | var table: [table_size + 1]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined; | 115 | .Length = buf.len * 2, |
| | 116 | .MaximumLength = buf.len * 2, |
| | 117 | .Buffer = &buf, |
| | 118 | }; |
| | 119 | |
| | 120 | const max_cpu_buf = 4; |
| | 121 | var next_cpu_buf: [max_cpu_buf]u8 = undefined; |
| | 122 | const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{core}); |
| | 123 | |
| | 124 | var subkey: [max_cpu_buf + 1]u16 = undefined; |
| | 125 | const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu); |
| | 126 | subkey[subkey_len] = 0; |
| | 127 | |
| | 128 | table[0] = .{ |
| | 129 | .QueryRoutine = null, |
| | 130 | .Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED, |
| | 131 | .Name = subkey[0..subkey_len :0], |
| | 132 | .EntryContext = null, |
| | 133 | .DefaultType = std.os.windows.REG_NONE, |
| | 134 | .DefaultData = null, |
| | 135 | .DefaultLength = 0, |
| | 136 | }; |
| | 137 | |
| | 138 | table[1] = .{ |
| | 139 | .QueryRoutine = null, |
| | 140 | .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED, |
| | 141 | .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)), |
| | 142 | .EntryContext = &buf_uni, |
| | 143 | .DefaultType = std.os.windows.REG_NONE, |
| | 144 | .DefaultData = null, |
| | 145 | .DefaultLength = 0, |
| | 146 | }; |
| 106 | | 147 | |
| 107 | // Table sentinel | 148 | // Table sentinel |
| 108 | table[actual_table_size] = .{ | 149 | table[table_size] = .{ |
| 109 | .QueryRoutine = null, | 150 | .QueryRoutine = null, |
| 110 | .Flags = 0, | 151 | .Flags = 0, |
| 111 | .Name = null, | 152 | .Name = null, |
| ... | @@ -115,64 +156,6 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model { | ... | @@ -115,64 +156,6 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model { |
| 115 | .DefaultLength = 0, | 156 | .DefaultLength = 0, |
| 116 | }; | 157 | }; |
| 117 | | 158 | |
| 118 | // Technically, a registry value can be as long as 16k u16s. However, MS recommends storing | | |
| 119 | // values larger than 2048 in a file rather than directly in the registry, and since we | | |
| 120 | // are only accessing a system hive \Registry\Machine, we stick to MS guidelines. | | |
| 121 | // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits | | |
| 122 | const max_sz_value = 2048; | | |
| 123 | const key_name = std.unicode.utf8ToUtf16LeStringLiteral("Identifier"); | | |
| 124 | | | |
| 125 | var i: usize = 0; | | |
| 126 | var index: usize = 0; | | |
| 127 | while (i < cpu_count) : (i += 1) { | | |
| 128 | var buf: [max_sz_value]u16 = undefined; | | |
| 129 | var buf_uni = std.os.windows.UNICODE_STRING{ | | |
| 130 | .Length = buf.len * 2, | | |
| 131 | .MaximumLength = buf.len * 2, | | |
| 132 | .Buffer = &buf, | | |
| 133 | }; | | |
| 134 | | | |
| 135 | var next_cpu_buf: [std.math.log2(max_cpu_count)]u8 = undefined; | | |
| 136 | const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{i}); | | |
| 137 | | | |
| 138 | var subkey: [std.math.log2(max_cpu_count) + 1]u16 = undefined; | | |
| 139 | const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu); | | |
| 140 | subkey[subkey_len] = 0; | | |
| 141 | | | |
| 142 | table[index] = .{ | | |
| 143 | .QueryRoutine = null, | | |
| 144 | .Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED, | | |
| 145 | .Name = subkey[0..subkey_len :0], | | |
| 146 | .EntryContext = null, | | |
| 147 | .DefaultType = std.os.windows.REG_NONE, | | |
| 148 | .DefaultData = null, | | |
| 149 | .DefaultLength = 0, | | |
| 150 | }; | | |
| 151 | | | |
| 152 | table[index + 1] = .{ | | |
| 153 | .QueryRoutine = null, | | |
| 154 | .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED, | | |
| 155 | .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)), | | |
| 156 | .EntryContext = &buf_uni, | | |
| 157 | .DefaultType = std.os.windows.REG_NONE, | | |
| 158 | .DefaultData = null, | | |
| 159 | .DefaultLength = 0, | | |
| 160 | }; | | |
| 161 | | | |
| 162 | table[index + 2] = .{ | | |
| 163 | .QueryRoutine = null, | | |
| 164 | .Flags = std.os.windows.RTL_QUERY_REGISTRY_TOPKEY, | | |
| 165 | .Name = null, | | |
| 166 | .EntryContext = null, | | |
| 167 | .DefaultType = std.os.windows.REG_NONE, | | |
| 168 | .DefaultData = null, | | |
| 169 | .DefaultLength = 0, | | |
| 170 | }; | | |
| 171 | | | |
| 172 | index += 3; | | |
| 173 | } | | |
| 174 | | | |
| 175 | const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor"); | | |
| 176 | const res = std.os.windows.ntdll.RtlQueryRegistryValues( | 159 | const res = std.os.windows.ntdll.RtlQueryRegistryValues( |
| 177 | std.os.windows.RTL_REGISTRY_ABSOLUTE, | 160 | std.os.windows.RTL_REGISTRY_ABSOLUTE, |
| 178 | topkey, | 161 | topkey, |
| ... | @@ -181,22 +164,29 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model { | ... | @@ -181,22 +164,29 @@ fn detectCpuModelArm64() !*const Target.Cpu.Model { |
| 181 | null, | 164 | null, |
| 182 | ); | 165 | ); |
| 183 | switch (res) { | 166 | switch (res) { |
| 184 | .SUCCESS => {}, | 167 | .SUCCESS => { |
| 185 | else => return error.QueryRegistryFailed, | 168 | var identifier_buf: [max_sz_value * 2]u8 = undefined; |
| | 169 | const len = try std.unicode.utf16leToUtf8(&identifier_buf, buf_uni.Buffer[0 .. buf_uni.Length / 2]); |
| | 170 | return identifier_buf[0..len]; |
| | 171 | }, |
| | 172 | else => return std.os.windows.unexpectedStatus(res), |
| 186 | } | 173 | } |
| | 174 | } |
| | 175 | |
| | 176 | fn detectCpuModelArm64() !*const Target.Cpu.Model { |
| | 177 | // Pull the CPU identifier from the registry. |
| | 178 | // Assume max number of cores to be at 8. |
| | 179 | const max_cpu_count = 8; |
| | 180 | const cpu_count = getCpuCount(); |
| | 181 | |
| | 182 | if (cpu_count > max_cpu_count) return error.TooManyCpus; |
| 187 | | 183 | |
| 188 | // Parse the models from strings | 184 | // Parse the models from strings |
| 189 | var parser = Armv8CpuInfoImpl{}; | 185 | var parser = Armv8CpuInfoImpl{}; |
| 190 | | 186 | |
| 191 | i = 0; | 187 | var i: usize = 0; |
| 192 | index = 0; | | |
| 193 | while (i < cpu_count) : (i += 1) { | 188 | while (i < cpu_count) : (i += 1) { |
| 194 | const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[index + 1].EntryContext); | 189 | const identifier = try getCpuInfoFromRegistry(i, "Identifier"); |
| 195 | index += 3; | | |
| 196 | | | |
| 197 | var identifier_buf: [max_sz_value * 2]u8 = undefined; | | |
| 198 | const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]); | | |
| 199 | const identifier = identifier_buf[0..len]; | | |
| 200 | parser.parseOne(identifier); | 190 | parser.parseOne(identifier); |
| 201 | } | 191 | } |
| 202 | | 192 | |