diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index fc42dd381c..4111a82707 100644 --- a/lib/std/os/windows.zig +++ b/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), - } -} diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index 09510a5f91..eeda2f63b6 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/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; diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 4932a6f679..b006a785da 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/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; diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig index 569daf0a30..258b17031b 100644 --- a/lib/std/zig/system/windows.zig +++ b/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)) {