Commit 7ea2c7fbcd
Changed files (4)
lib
std
os
zig
system
lib/std/os/windows/kernel32.zig
@@ -10,6 +10,7 @@ const DWORD = windows.DWORD;
const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS;
const HANDLE = windows.HANDLE;
const HMODULE = windows.HMODULE;
+const HKEY = windows.HKEY;
const HRESULT = windows.HRESULT;
const LARGE_INTEGER = windows.LARGE_INTEGER;
const LPCWSTR = windows.LPCWSTR;
@@ -57,6 +58,8 @@ const UCHAR = windows.UCHAR;
const FARPROC = windows.FARPROC;
const INIT_ONCE_FN = windows.INIT_ONCE_FN;
const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION;
+const REGSAM = windows.REGSAM;
+const LSTATUS = windows.LSTATUS;
pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
@@ -412,3 +415,11 @@ pub extern "kernel32" fn SleepConditionVariableSRW(
pub extern "kernel32" fn TryAcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) BOOLEAN;
pub extern "kernel32" fn AcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
pub extern "kernel32" fn ReleaseSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
+
+pub extern "kernel32" fn RegOpenKeyExW(
+ hkey: HKEY,
+ lpSubKey: LPCWSTR,
+ ulOptions: DWORD,
+ samDesired: REGSAM,
+ phkResult: *HANDLE,
+) callconv(WINAPI) LSTATUS;
lib/std/os/windows/ntdll.zig
@@ -22,6 +22,8 @@ const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION;
const SIZE_T = windows.SIZE_T;
const CURDIR = windows.CURDIR;
+const PCWSTR = windows.PCWSTR;
+const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
pub const THREADINFOCLASS = enum(c_int) {
ThreadBasicInformation,
@@ -259,3 +261,11 @@ pub extern "ntdll" fn NtOpenKey(
DesiredAccess: ACCESS_MASK,
ObjectAttributes: OBJECT_ATTRIBUTES,
) callconv(WINAPI) NTSTATUS;
+
+pub extern "ntdll" fn RtlQueryRegistryValues(
+ RelativeTo: ULONG,
+ Path: PCWSTR,
+ QueryTable: [*]RTL_QUERY_REGISTRY_TABLE,
+ Context: ?*anyopaque,
+ Environment: ?*anyopaque,
+) callconv(WINAPI) NTSTATUS;
lib/std/os/windows.zig
@@ -2088,6 +2088,7 @@ pub const LPWSTR = [*:0]WCHAR;
pub const LPCWSTR = [*:0]const WCHAR;
pub const PVOID = *anyopaque;
pub const PWSTR = [*:0]WCHAR;
+pub const PCWSTR = [*:0]const WCHAR;
pub const SIZE_T = usize;
pub const UINT = c_uint;
pub const ULONG_PTR = usize;
@@ -2876,8 +2877,134 @@ pub const ACCESS_MASK = DWORD;
pub const LSTATUS = LONG;
pub const HKEY = HANDLE;
+
pub const HKEY_LOCAL_MACHINE: HKEY = @intToPtr(HKEY, 0x80000002);
+/// Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
+/// KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.
+pub const KEY_ALL_ACCESS = 0xF003F;
+/// Reserved for system use.
+pub const KEY_CREATE_LINK = 0x0020;
+/// Required to create a subkey of a registry key.
+pub const KEY_CREATE_SUB_KEY = 0x0004;
+/// Required to enumerate the subkeys of a registry key.
+pub const KEY_ENUMERATE_SUB_KEYS = 0x0008;
+/// Equivalent to KEY_READ.
+pub const KEY_EXECUTE = 0x20019;
+/// Required to request change notifications for a registry key or for subkeys of a registry key.
+pub const KEY_NOTIFY = 0x0010;
+/// Required to query the values of a registry key.
+pub const KEY_QUERY_VALUE = 0x0001;
+/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
+pub const KEY_READ = 0x20019;
+/// Required to create, delete, or set a registry value.
+pub const KEY_SET_VALUE = 0x0002;
+/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
+/// This flag is ignored by 32-bit Windows.
+pub const KEY_WOW64_32KEY = 0x0200;
+/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
+/// This flag is ignored by 32-bit Windows.
+pub const KEY_WOW64_64KEY = 0x0100;
+/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
+pub const KEY_WRITE = 0x20006;
+
+/// Open symbolic link.
+pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
+
+pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
+ QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
+ Flags: ULONG,
+ Name: ?PWSTR,
+ EntryContext: ?*anyopaque,
+ DefaultType: ULONG,
+ DefaultData: ?*anyopaque,
+ DefaultLength: ULONG,
+};
+
+pub const RTL_QUERY_REGISTRY_ROUTINE = ?std.meta.FnPtr(fn (
+ PWSTR,
+ ULONG,
+ ?*anyopaque,
+ ULONG,
+ ?*anyopaque,
+ ?*anyopaque,
+) callconv(WINAPI) NTSTATUS);
+
+/// Path is a full path
+pub const RTL_REGISTRY_ABSOLUTE = 0;
+/// \Registry\Machine\System\CurrentControlSet\Services
+pub const RTL_REGISTRY_SERVICES = 1;
+/// \Registry\Machine\System\CurrentControlSet\Control
+pub const RTL_REGISTRY_CONTROL = 2;
+/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
+pub const RTL_REGISTRY_WINDOWS_NT = 3;
+/// \Registry\Machine\Hardware\DeviceMap
+pub const RTL_REGISTRY_DEVICEMAP = 4;
+/// \Registry\User\CurrentUser
+pub const RTL_REGISTRY_USER = 5;
+pub const RTL_REGISTRY_MAXIMUM = 6;
+
+/// Low order bits are registry handle
+pub const RTL_REGISTRY_HANDLE = 0x40000000;
+/// Indicates the key node is optional
+pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
+
+/// Name is a subkey and remainder of table or until next subkey are value
+/// names for that subkey to look at.
+pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
+
+/// Reset current key to original key for this and all following table entries.
+pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
+
+/// Fail if no match found for this table entry.
+pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
+
+/// Used to mark a table entry that has no value name, just wants a call out, not
+/// an enumeration of all values.
+pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
+
+/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
+/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
+pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
+
+/// QueryRoutine field ignored. EntryContext field points to location to store value.
+/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
+/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
+/// allocated.
+pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
+
+/// Used to delete value keys after they are queried.
+pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
+
+/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
+/// of the stored registry value matches the type expected by the caller.
+/// If the types do not match, the call fails.
+pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
+
+/// No value type
+pub const REG_NONE = 0;
+/// Unicode nul terminated string
+pub const REG_SZ = 1;
+/// Unicode nul terminated string (with environment variable references)
+pub const REG_EXPAND_SZ = 2;
+/// Free form binary
+pub const REG_BINARY = 3;
+/// 32-bit number
+pub const REG_DWORD = 4;
+/// 32-bit number (same as REG_DWORD)
+pub const REG_DWORD_LITTLE_ENDIAN = 4;
+/// 32-bit number
+pub const REG_DWORD_BIG_ENDIAN = 5;
+/// Symbolic Link (unicode)
+pub const REG_LINK = 6;
+/// Multiple Unicode strings
+pub const REG_MULTI_SZ = 7;
+/// Resource list in the resource map
+pub const REG_RESOURCE_LIST = 8;
+/// Resource list in the hardware description
+pub const REG_FULL_RESOURCE_DESCRIPTOR = 9;
+pub const REG_RESOURCE_REQUIREMENTS_LIST = 10;
+
pub const FILE_NOTIFY_INFORMATION = extern struct {
NextEntryOffset: DWORD,
Action: DWORD,
@@ -4020,187 +4147,3 @@ pub fn IsProcessorFeaturePresent(feature: PF) bool {
if (@enumToInt(feature) >= PROCESSOR_FEATURE_MAX) return false;
return SharedUserData.ProcessorFeatures[@enumToInt(feature)] == 1;
}
-
-pub const KEY_QUERY_VALUE = 0x0001;
-
-/// Open symbolic link.
-pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
-
-inline fn IsPredefKey(hkey: HKEY) bool {
- return @ptrToInt(hkey) & 0xF0000000 == 0x80000000;
-}
-
-inline fn GetPredefKeyIndex(hkey: HKEY) usize {
- return @ptrToInt(hkey) & 0x0FFFFFFF;
-}
-
-inline fn ClosePredefKey(hkey: HKEY) void {
- if (@ptrToInt(hkey) & 0x1 != 0) {
- assert(ntdll.NtClose(hkey) == .SUCCESS);
- }
-}
-
-const MAX_DEFAULT_HANDLES = 6;
-pub const REG_MAX_NAME_SIZE = 256;
-
-pub const RegOpenKeyOpts = struct {
- ulOptions: DWORD = 0,
- samDesired: ACCESS_MASK = KEY_QUERY_VALUE,
-};
-
-/// Pulls existing key from the registry.
-pub fn RegOpenKey(hkey: HKEY, lpSubKey: []const u16, opts: RegOpenKeyOpts) !HKEY {
- if (IsPredefKey(hkey) and lpSubKey.len == 0) {
- return hkey;
- }
-
- const key_handle = try MapDefaultKey(hkey);
- defer ClosePredefKey(key_handle);
-
- var subkey_string: UNICODE_STRING = undefined;
- if (lpSubKey.len == 0 or mem.eql(u16, &[_]u16{'\\'}, lpSubKey)) {
- subkey_string = .{
- .Length = 0,
- .MaximumLength = 0,
- .Buffer = @intToPtr([*]u16, @ptrToInt(&[0]u16{})),
- };
- } else {
- const len_bytes = math.cast(u16, lpSubKey.len * 2) orelse return error.NameTooLong;
- subkey_string = .{
- .Length = len_bytes,
- .MaximumLength = len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(lpSubKey.ptr)),
- };
- }
-
- var attributes: ULONG = OBJ_CASE_INSENSITIVE;
- if (opts.ulOptions & REG_OPTION_OPEN_LINK != 0) {
- attributes |= OBJ_OPENLINK;
- }
-
- var attr = OBJECT_ATTRIBUTES{
- .Length = @sizeOf(OBJECT_ATTRIBUTES),
- .RootDirectory = key_handle,
- .Attributes = attributes,
- .ObjectName = &subkey_string,
- .SecurityDescriptor = null,
- .SecurityQualityOfService = null,
- };
-
- var result: HKEY = undefined;
- const rc = ntdll.NtOpenKey(
- &result,
- opts.samDesired,
- attr,
- );
- switch (rc) {
- .SUCCESS => return result,
- else => return unexpectedStatus(rc),
- }
-}
-
-pub fn RegCloseKey(hkey: HKEY) void {
- if (IsPredefKey(hkey)) return;
- assert(ntdll.NtClose(hkey) == .SUCCESS);
-}
-
-extern var DefaultHandleHKUDisabled: BOOLEAN;
-extern var DefaultHandlesDisabled: BOOLEAN;
-extern var DefaultHandleTable: [MAX_DEFAULT_HANDLES]?HANDLE;
-
-fn MapDefaultKey(key: HKEY) !HANDLE {
- if (!IsPredefKey(key)) return @intToPtr(HANDLE, @ptrToInt(key) & ~@as(usize, 0x1));
-
- const index = GetPredefKeyIndex(key);
- if (index >= MAX_DEFAULT_HANDLES) {
- return error.InvalidParameter;
- }
-
- const def_disabled = if (key == HKEY_LOCAL_MACHINE) DefaultHandleHKUDisabled else DefaultHandlesDisabled;
-
- var handle: HANDLE = undefined;
- var do_open: bool = true;
-
- if (def_disabled != 0) {
- const tmp = DefaultHandleTable[index];
- if (tmp) |h| {
- do_open = false;
- handle = h;
- }
- }
-
- if (do_open) {
- handle = try OpenPredefinedKey(index);
- }
-
- if (def_disabled == 0) {
- handle = @intToPtr(HANDLE, @ptrToInt(handle) | 0x1);
- }
-
- return handle;
-}
-
-fn OpenPredefinedKey(index: usize) !HANDLE {
- switch (index) {
- 0 => {
- // HKEY_CLASSES_ROOT
- return error.Unimplemented;
- },
- 1 => {
- // HKEY_CURRENT_USER
- return error.Unimplemented;
- },
- 2 => {
- // HKEY_LOCAL_MACHINE
- return OpenLocalMachineKey();
- },
- 3 => {
- // HKEY_USERS
- return error.Unimplemented;
- },
- 5 => {
- // HKEY_CURRENT_CONFIG
- return error.Unimplemented;
- },
- 6 => {
- // HKEY_DYN_DATA
- return error.Unimplemented;
- },
- else => {
- return error.InvalidParameter;
- },
- }
-}
-
-fn OpenLocalMachineKey() !HANDLE {
- const path = "\\Registry\\Machine";
- var path_u16: [REG_MAX_NAME_SIZE]u16 = undefined;
- const path_len_u16 = try std.unicode.utf8ToUtf16Le(&path_u16, path);
- const path_len_bytes = @intCast(u16, path_len_u16 * 2);
-
- var key_name = UNICODE_STRING{
- .Length = path_len_bytes,
- .MaximumLength = path_len_bytes,
- .Buffer = @intToPtr([*]u16, @ptrToInt(&path_u16)),
- };
-
- var attr = OBJECT_ATTRIBUTES{
- .Length = @sizeOf(OBJECT_ATTRIBUTES),
- .RootDirectory = null,
- .Attributes = OBJ_CASE_INSENSITIVE,
- .ObjectName = &key_name,
- .SecurityDescriptor = null,
- .SecurityQualityOfService = null,
- };
-
- var result: HKEY = undefined;
- const rc = ntdll.NtOpenKey(
- &result,
- MAXIMUM_ALLOWED,
- attr,
- );
- switch (rc) {
- .SUCCESS => return result,
- else => return unexpectedStatus(rc),
- }
-}
lib/std/zig/system/windows.zig
@@ -43,13 +43,124 @@ pub fn detectRuntimeVersion() WindowsVersion {
return @intToEnum(WindowsVersion, version);
}
+fn detectCpuModelArm64() !*const Target.Cpu.Model {
+ // Pull the CPU identifier from the registry.
+ // Assume max number of cores to be at 128.
+ const max_cpu_count = 128;
+ const cpu_count = getCpuCount();
+
+ if (cpu_count > max_cpu_count) return error.TooManyCpus;
+
+ const table_size = max_cpu_count * 3 + 1;
+ const actual_table_size = cpu_count * 3 + 1;
+ var table: [table_size]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
+
+ // Table sentinel
+ table[actual_table_size - 1] = .{
+ .QueryRoutine = null,
+ .Flags = 0,
+ .Name = null,
+ .EntryContext = null,
+ .DefaultType = 0,
+ .DefaultData = null,
+ .DefaultLength = 0,
+ };
+
+ // Technically, a registry value can be as long as 16k u16s. However, MS recommends storing
+ // values larger than 2048 in a file rather than directly in the registry, and since we
+ // are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
+ // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
+ const max_sz_value = 2048;
+ const key_name = std.unicode.utf8ToUtf16LeStringLiteral("Identifier");
+
+ var i: usize = 0;
+ var index: usize = 0;
+ while (i < cpu_count) : (i += 1) {
+ var buf: [max_sz_value]u16 = undefined;
+ var buf_uni = std.os.windows.UNICODE_STRING{
+ .Length = buf.len * 2,
+ .MaximumLength = buf.len * 2,
+ .Buffer = &buf,
+ };
+
+ var next_cpu_buf: [std.math.log2(max_cpu_count)]u8 = undefined;
+ const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{i});
+
+ var subkey: [std.math.log2(max_cpu_count) / 2]u16 = undefined;
+ const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu);
+ subkey[subkey_len] = 0;
+
+ table[index] = .{
+ .QueryRoutine = null,
+ .Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
+ .Name = subkey[0..subkey_len :0],
+ .EntryContext = null,
+ .DefaultType = std.os.windows.REG_NONE,
+ .DefaultData = null,
+ .DefaultLength = 0,
+ };
+
+ table[index + 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)),
+ .EntryContext = &buf_uni,
+ .DefaultType = std.os.windows.REG_NONE,
+ .DefaultData = null,
+ .DefaultLength = 0,
+ };
+
+ table[index + 2] = .{
+ .QueryRoutine = null,
+ .Flags = std.os.windows.RTL_QUERY_REGISTRY_TOPKEY,
+ .Name = null,
+ .EntryContext = null,
+ .DefaultType = std.os.windows.REG_NONE,
+ .DefaultData = null,
+ .DefaultLength = 0,
+ };
+
+ index += 3;
+ }
+
+ const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
+ const res = std.os.windows.ntdll.RtlQueryRegistryValues(
+ std.os.windows.RTL_REGISTRY_ABSOLUTE,
+ topkey,
+ &table,
+ null,
+ null,
+ );
+ switch (res) {
+ .SUCCESS => {},
+ else => return error.QueryRegistryFailed,
+ }
+
+ // Parse the models from strings
+ i = 0;
+ index = 0;
+ while (i < cpu_count) : (i += 1) {
+ const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[index + 1].EntryContext);
+ index += 3;
+
+ var identifier_buf: [max_sz_value * 2]u8 = undefined;
+ const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]);
+ const identifier = identifier_buf[0..len];
+ _ = identifier;
+ }
+
+ return &Target.aarch64.cpu.microsoft_sq3;
+}
+
fn detectNativeCpuAndFeaturesArm64() Target.Cpu {
const Feature = Target.aarch64.Feature;
+ const model = detectCpuModelArm64() catch Target.Cpu.Model.generic(.aarch64);
+
var cpu = Target.Cpu{
.arch = .aarch64,
- .model = Target.Cpu.Model.generic(.aarch64),
- .features = Target.Cpu.Feature.Set.empty,
+ .model = model,
+ .features = model.features,
};
if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {