diff --git a/AppSandbox.sln b/AppSandbox.sln index 10b7aa4..5d00fd9 100644 --- a/AppSandbox.sln +++ b/AppSandbox.sln @@ -9,12 +9,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppSandbox", "AppSandbox.vc {1A2B3C4D-5E6F-7890-ABCD-111222333444} = {1A2B3C4D-5E6F-7890-ABCD-111222333444} {D4E5F6A7-B8C9-0123-4567-89ABCDEF0123} = {D4E5F6A7-B8C9-0123-4567-89ABCDEF0123} {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22} = {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22} + {B689C521-58F9-47E4-92FC-E110A39D51C8} = {B689C521-58F9-47E4-92FC-E110A39D51C8} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "agent", "tools\agent\agent.vcxproj", "{D4E5F6A7-B8C9-0123-4567-89ABCDEF0123}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppSandbox-NVIDIA-VK-GL-shim", "tools\nvidia\AppSandbox-NVIDIA-VK-GL-shim.vcxproj", "{31D821AC-8E4A-4CC7-AE30-FB0E5F431F22}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppSandbox-NVIDIA-DLSS-shim", "tools\nvidia\AppSandbox-NVIDIA-DLSS-shim.vcxproj", "{B689C521-58F9-47E4-92FC-E110A39D51C8}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iso-patch", "tools\iso-patch\iso-patch.vcxproj", "{A1B2C3D4-5678-9ABC-DEF0-123456789ABC}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppSandboxVDD", "tools\vdd\AppSandboxVDD.vcxproj", "{E1F2A3B4-C5D6-7890-ABCD-EF1234567890}" @@ -46,6 +49,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppSandboxPackage", "tools\ {D4E5F6A7-B8C9-0123-4567-89ABCDEF0123} = {D4E5F6A7-B8C9-0123-4567-89ABCDEF0123} {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22} = {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22} {A1B2C3D4-5678-9ABC-DEF0-123456789ABC} = {A1B2C3D4-5678-9ABC-DEF0-123456789ABC} + {B689C521-58F9-47E4-92FC-E110A39D51C8} = {B689C521-58F9-47E4-92FC-E110A39D51C8} {E1F2A3B4-C5D6-7890-ABCD-EF1234567890} = {E1F2A3B4-C5D6-7890-ABCD-EF1234567890} {F2E3D4C5-B6A7-8901-CDEF-234567890ABC} = {F2E3D4C5-B6A7-8901-CDEF-234567890ABC} {A1B2C3D4-5678-9ABC-DEF0-AABBCCDDEEFF} = {A1B2C3D4-5678-9ABC-DEF0-AABBCCDDEEFF} @@ -67,6 +71,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Debug|ARM64.Build.0 = Debug|ARM64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Debug|x64.ActiveCfg = Debug|x64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Debug|x64.Build.0 = Debug|x64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Release|ARM64.ActiveCfg = Release|ARM64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Release|ARM64.Build.0 = Release|ARM64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Release|x64.ActiveCfg = Release|x64 + {B689C521-58F9-47E4-92FC-E110A39D51C8}.Release|x64.Build.0 = Release|x64 {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22}.Debug|ARM64.ActiveCfg = Debug|ARM64 {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22}.Debug|ARM64.Build.0 = Debug|ARM64 {31D821AC-8E4A-4CC7-AE30-FB0E5F431F22}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/src/backend_win/asb_core.c b/src/backend_win/asb_core.c index 2f77e1a..5395c01 100644 --- a/src/backend_win/asb_core.c +++ b/src/backend_win/asb_core.c @@ -1052,7 +1052,7 @@ static DWORD WINAPI start_vm_thread(LPVOID param) else if (_wcsicmp(args->config.os_type, L"Windows") == 0) { gpu_append_amd_gl_vk_driver_shares(&g_gpu_list, &args->config.gpu_shares); gpu_append_nvidia_drs_share(&g_gpu_list, &args->config.gpu_shares); - gpu_append_nvidia_gl_vk_shim_share(&g_gpu_list, &args->config.gpu_shares); + gpu_append_nvidia_graphics_shim_share(&g_gpu_list, &args->config.gpu_shares); prepare_gl_layers_share(&args->config.gpu_shares); } } @@ -3210,7 +3210,7 @@ ASB_API HRESULT asb_vm_create(const AsbVmConfig *config) else if (_wcsicmp(cfg.os_type, L"Windows") == 0) { gpu_append_amd_gl_vk_driver_shares(&g_gpu_list, &cfg.gpu_shares); gpu_append_nvidia_drs_share(&g_gpu_list, &cfg.gpu_shares); - gpu_append_nvidia_gl_vk_shim_share(&g_gpu_list, &cfg.gpu_shares); + gpu_append_nvidia_graphics_shim_share(&g_gpu_list, &cfg.gpu_shares); prepare_gl_layers_share(&cfg.gpu_shares); } } diff --git a/src/backend_win/gpu_enum.c b/src/backend_win/gpu_enum.c index 9dea586..af88f54 100644 --- a/src/backend_win/gpu_enum.c +++ b/src/backend_win/gpu_enum.c @@ -600,9 +600,13 @@ BOOL gpu_append_nvidia_drs_share(const GpuList *gpu_list, GpuDriverShareList *li return TRUE; } -BOOL gpu_append_nvidia_gl_vk_shim_share(const GpuList *gpu_list, GpuDriverShareList *list) +BOOL gpu_append_nvidia_graphics_shim_share(const GpuList *gpu_list, GpuDriverShareList *list) { #if defined(_M_X64) + static const wchar_t *const additional[] = { + L"AppSandbox-NVIDIA-VK-GL-shim32.dll", + L"AppSandbox-NVIDIA-DLSS-shim.dll" + }; wchar_t exe[MAX_PATH], path[MAX_PATH], file[MAX_PATH], *slash; GpuDriverShare *share; DWORD length, attributes; @@ -643,10 +647,14 @@ BOOL gpu_append_nvidia_gl_vk_shim_share(const GpuList *gpu_list, GpuDriverShareL wcscpy_s(share->host_path, MAX_PATH, path); wcscpy_s(share->guest_path, MAX_PATH, L"C:\\Windows\\AppSandbox\\nvidia"); wcscpy_s(share->file_filter, 4096, L"AppSandbox-NVIDIA-VK-GL-shim.dll"); - swprintf_s(file, MAX_PATH, L"%s\\AppSandbox-NVIDIA-VK-GL-shim32.dll", path); - attributes = GetFileAttributesW(file); - if (attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY)) - wcscat_s(share->file_filter, 4096, L";AppSandbox-NVIDIA-VK-GL-shim32.dll"); + for (i = 0; i < ARRAYSIZE(additional); i++) { + swprintf_s(file, MAX_PATH, L"%s\\%s", path, additional[i]); + attributes = GetFileAttributesW(file); + if (attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY)) { + wcscat_s(share->file_filter, 4096, L";"); + wcscat_s(share->file_filter, 4096, additional[i]); + } + } list->count++; return TRUE; #else diff --git a/src/backend_win/gpu_enum.h b/src/backend_win/gpu_enum.h index 3da78ae..d096369 100644 --- a/src/backend_win/gpu_enum.h +++ b/src/backend_win/gpu_enum.h @@ -53,7 +53,7 @@ BOOL gpu_get_driver_shares(GpuList *gpu_list, GpuDriverShareList *out); /* Windows guests only; callers gate on guest OS and GPU mode. */ BOOL gpu_append_amd_gl_vk_driver_shares(const GpuList *gpu_list, GpuDriverShareList *list); BOOL gpu_append_nvidia_drs_share(const GpuList *gpu_list, GpuDriverShareList *list); -BOOL gpu_append_nvidia_gl_vk_shim_share(const GpuList *gpu_list, GpuDriverShareList *list); +BOOL gpu_append_nvidia_graphics_shim_share(const GpuList *gpu_list, GpuDriverShareList *list); /* Append the host's lxss\lib directory as a synthetic Plan9 share entry (name "AppSandbox.HostLxssLib") to a share list. Linux guests need diff --git a/tools/agent/agent.c b/tools/agent/agent.c index 6272a7e..024286b 100644 --- a/tools/agent/agent.c +++ b/tools/agent/agent.c @@ -613,7 +613,8 @@ static DWORD WINAPI gpu_copy_thread(LPVOID param) rc = P9_ERR_IO; } else if (strcmp(si->share_name, "AppSandbox.Nvidia") == 0) { const P9CopyOptions options = { - FALSE, NULL, "AppSandbox-NVIDIA-VK-GL-shim.dll;AppSandbox-NVIDIA-VK-GL-shim32.dll" + FALSE, NULL, "AppSandbox-NVIDIA-VK-GL-shim.dll;AppSandbox-NVIDIA-VK-GL-shim32.dll;" + "AppSandbox-NVIDIA-DLSS-shim.dll" }; rc = p9_copy_share_ex(50001, si->share_name, dest_wide, si->filter[0] ? si->filter : NULL, @@ -657,6 +658,8 @@ static DWORD WINAPI gpu_copy_thread(LPVOID param) if (failed_shares == 0 && (gl_dir[0] || native_dir[0] || gpu_prefers_system_opengl())) gl_provision(gl_dir, native_dir); + if (failed_shares == 0 && !nvidia_dlss_provision(native_dir)) + failed_shares++; /* Send final result to host */ if (failed_shares == 0) { diff --git a/tools/agent/gl_vk_provision.c b/tools/agent/gl_vk_provision.c index 90d8bf1..eb09762 100644 --- a/tools/agent/gl_vk_provision.c +++ b/tools/agent/gl_vk_provision.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #pragma warning(pop) #include #include @@ -290,7 +292,7 @@ static BOOL grant_system_file_control(const wchar_t *path) } #if defined(_M_X64) -static BOOL validate_opengl_dll(const wchar_t *path, WORD machine, BOOL native) +static BOOL validate_dll_export(const wchar_t *path, WORD machine, const char *required_export) { HMODULE module = LoadLibraryExW(path, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE); BYTE *base; @@ -298,6 +300,7 @@ static BOOL validate_opengl_dll(const wchar_t *path, WORD machine, BOOL native) IMAGE_DATA_DIRECTORY directory; IMAGE_EXPORT_DIRECTORY *exports; DWORD *names, i, size; + size_t export_length = required_export ? strlen(required_export) + 1 : 0; BOOL found = FALSE; if (!module) return FALSE; @@ -314,7 +317,7 @@ static BOOL validate_opengl_dll(const wchar_t *path, WORD machine, BOOL native) size = nt32->OptionalHeader.SizeOfImage; directory = nt32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; } else goto done; - if (!native) { found = TRUE; goto done; } + if (!required_export) { found = TRUE; goto done; } if (!directory.VirtualAddress || size < sizeof(*exports) || directory.VirtualAddress > size - sizeof(*exports)) goto done; exports = (IMAGE_EXPORT_DIRECTORY *)(base + directory.VirtualAddress); @@ -322,8 +325,8 @@ static BOOL validate_opengl_dll(const wchar_t *path, WORD machine, BOOL native) exports->AddressOfNames > size - exports->NumberOfNames * sizeof(DWORD)) goto done; names = (DWORD *)(base + exports->AddressOfNames); for (i = 0; i < exports->NumberOfNames; i++) { - if (names[i] <= size - sizeof("appsandbox_nvidia") && - !memcmp(base + names[i], "appsandbox_nvidia", sizeof("appsandbox_nvidia"))) { + if (export_length <= size && names[i] <= size - export_length && + !memcmp(base + names[i], required_export, export_length)) { found = TRUE; break; } @@ -333,8 +336,13 @@ done: return found; } -static UINT find_nvidia_vulkan_manifests(wchar_t paths[32][MAX_PATH], const wchar_t *sys, - const wchar_t *manifest) +static BOOL validate_opengl_dll(const wchar_t *path, WORD machine, BOOL native) +{ + return validate_dll_export(path, machine, native ? "appsandbox_nvidia" : NULL); +} + +static UINT find_nvidia_driver_files(wchar_t paths[32][MAX_PATH], const wchar_t *sys, + const wchar_t *filename) { typedef NTSTATUS (APIENTRY *EnumAdaptersFn)(const D3DKMT_ENUMADAPTERS2 *); typedef NTSTATUS (APIENTRY *QueryAdapterFn)(const D3DKMT_QUERYADAPTERINFO *); @@ -401,11 +409,11 @@ static UINT find_nvidia_vulkan_manifests(wchar_t paths[32][MAX_PATH], const wcha if (!slash || _wcsicmp(slash + 1, L"nvoglv64.dll") || GetFileAttributesW(driver) == INVALID_FILE_ATTRIBUTES) goto next; *slash = 0; - if (!_wcsicmp(manifest, L"nv-vk32.json")) { + if (!_wcsicmp(filename, L"nv-vk32.json")) { swprintf_s(path, MAX_PATH, L"%s\\nvoglv32.dll", driver); if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) goto next; } - swprintf_s(path, MAX_PATH, L"%s\\%s", driver, manifest); + swprintf_s(path, MAX_PATH, L"%s\\%s", driver, filename); if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) goto next; for (j = 0; j < count; j++) if (!_wcsicmp(paths[j], path)) break; @@ -426,6 +434,167 @@ done: return count; } +typedef struct { + DWORD tag; + USHORT data_length, reserved; + USHORT substitute_offset, substitute_length, print_offset, print_length; + wchar_t paths[(MAX_PATH + 4) * 2]; +} NvidiaNgxJunction; + +static HANDLE create_ngx_junction_directory(const wchar_t *path, BOOL *created) +{ + typedef NTSTATUS (NTAPI *CreateFileFn)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, + PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG); + CreateFileFn create; + HANDLE directory = INVALID_HANDLE_VALUE, token; + TOKEN_PRIVILEGES privileges = { 1 }, previous = { 0 }; + DWORD previous_size = sizeof(previous), error; + wchar_t native[MAX_PATH + 4]; + UNICODE_STRING name; + OBJECT_ATTRIBUTES attributes = { sizeof(attributes) }; + IO_STATUS_BLOCK status; + + *created = CreateDirectoryW(path, NULL); + if (*created) { + directory = CreateFileW(path, GENERIC_WRITE | DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); + if (directory != INVALID_HANDLE_VALUE) return directory; + } + error = GetLastError(); + if (error != ERROR_ACCESS_DENIED) return INVALID_HANDLE_VALUE; + create = (CreateFileFn)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtCreateFile"); + if (!create || swprintf_s(native, ARRAYSIZE(native), L"\\??\\%s", path) < 0 || + !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &token)) return INVALID_HANDLE_VALUE; + if (!LookupPrivilegeValueW(NULL, SE_RESTORE_NAME, &privileges.Privileges[0].Luid)) { + CloseHandle(token); + return INVALID_HANDLE_VALUE; + } + privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + SetLastError(ERROR_SUCCESS); + if (AdjustTokenPrivileges(token, FALSE, &privileges, sizeof(previous), + &previous, &previous_size) && GetLastError() == ERROR_SUCCESS) { + name.Buffer = native; + name.Length = (USHORT)(wcslen(native) * sizeof(wchar_t)); + name.MaximumLength = (USHORT)(name.Length + sizeof(wchar_t)); + attributes.ObjectName = &name; + attributes.Attributes = OBJ_CASE_INSENSITIVE; + if (create(&directory, GENERIC_WRITE | DELETE | SYNCHRONIZE, &attributes, &status, + NULL, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + *created ? FILE_OPEN : FILE_CREATE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT, NULL, 0) >= 0) + *created = TRUE; + else + directory = INVALID_HANDLE_VALUE; + } + if (previous.PrivilegeCount) + AdjustTokenPrivileges(token, FALSE, &previous, 0, NULL, NULL); + CloseHandle(token); + return directory; +} + +static BOOL nvidia_ngx_junction(const wchar_t *sys, const wchar_t *package) +{ + wchar_t host_prefix[MAX_PATH], driver_prefix[MAX_PATH], target[MAX_PATH]; + wchar_t link[MAX_PATH], file[MAX_PATH], native[MAX_PATH + 4]; + const wchar_t *leaf; + NvidiaNgxJunction reparse = { 0 }; + HANDLE directory; + DWORD attributes, bytes; + size_t target_length, native_length; + BOOL created = FALSE, result; + + if (swprintf_s(host_prefix, MAX_PATH, L"%s\\HostDriverStore\\FileRepository\\", sys) < 0 || + swprintf_s(driver_prefix, MAX_PATH, L"%s\\DriverStore\\FileRepository\\", sys) < 0) + return FALSE; + if (!_wcsnicmp(package, host_prefix, wcslen(host_prefix))) + leaf = package + wcslen(host_prefix); + else if (!_wcsnicmp(package, driver_prefix, wcslen(driver_prefix))) + leaf = package + wcslen(driver_prefix); + else return FALSE; + if (!leaf[0] || !wcscmp(leaf, L".") || !wcscmp(leaf, L"..") || wcspbrk(leaf, L"\\/:") || + swprintf_s(target, MAX_PATH, L"%s%s", host_prefix, leaf) < 0 || + swprintf_s(link, MAX_PATH, L"%s%s", driver_prefix, leaf) < 0 || + swprintf_s(native, ARRAYSIZE(native), L"\\??\\%s", target) < 0) + return FALSE; + attributes = GetFileAttributesW(link); + if (attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + wchar_t host_file[MAX_PATH]; + if (!(attributes & FILE_ATTRIBUTE_DIRECTORY) || + swprintf_s(file, MAX_PATH, L"%s\\_nvngx.dll", link) < 0 || + swprintf_s(host_file, MAX_PATH, L"%s\\_nvngx.dll", target) < 0 || + !validate_dll_export(file, IMAGE_FILE_MACHINE_AMD64, NULL)) return FALSE; + return GetFileAttributesW(host_file) == INVALID_FILE_ATTRIBUTES || files_equal(file, host_file); + } + if (swprintf_s(file, MAX_PATH, L"%s\\_nvngx.dll", target) < 0 || + !validate_dll_export(file, IMAGE_FILE_MACHINE_AMD64, NULL)) return FALSE; + native_length = wcslen(native) * sizeof(wchar_t); + target_length = wcslen(target) * sizeof(wchar_t); + if (attributes != INVALID_FILE_ATTRIBUTES) { + directory = CreateFileW(link, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); + if (directory == INVALID_HANDLE_VALUE) return FALSE; + result = DeviceIoControl(directory, FSCTL_GET_REPARSE_POINT, NULL, 0, + &reparse, sizeof(reparse), &bytes, NULL); + CloseHandle(directory); + return result && bytes >= (DWORD)FIELD_OFFSET(NvidiaNgxJunction, paths) && + reparse.tag == IO_REPARSE_TAG_MOUNT_POINT && + reparse.substitute_offset % sizeof(wchar_t) == 0 && + reparse.substitute_length == native_length && + (DWORD)reparse.substitute_offset + reparse.substitute_length <= + bytes - (DWORD)FIELD_OFFSET(NvidiaNgxJunction, paths) && + !_wcsnicmp(reparse.paths + reparse.substitute_offset / sizeof(wchar_t), + native, native_length / sizeof(wchar_t)); + } + directory = create_ngx_junction_directory(link, &created); + if (directory == INVALID_HANDLE_VALUE) { + if (created) RemoveDirectoryW(link); + return FALSE; + } + reparse.tag = IO_REPARSE_TAG_MOUNT_POINT; + reparse.substitute_length = (USHORT)native_length; + reparse.print_offset = (USHORT)(native_length + sizeof(wchar_t)); + reparse.print_length = (USHORT)target_length; + reparse.data_length = (USHORT)(8 + native_length + target_length + 2 * sizeof(wchar_t)); + memcpy(reparse.paths, native, native_length + sizeof(wchar_t)); + memcpy((BYTE *)reparse.paths + reparse.print_offset, target, target_length + sizeof(wchar_t)); + result = DeviceIoControl(directory, FSCTL_SET_REPARSE_POINT, &reparse, + reparse.data_length + 8, NULL, 0, &bytes, NULL); + if (!result) { + FILE_DISPOSITION_INFO disposition = { TRUE }; + SetFileInformationByHandle(directory, FileDispositionInfo, &disposition, sizeof(disposition)); + } + CloseHandle(directory); + return result; +} + +static BOOL provision_nvapi(const wchar_t *native_dir, const wchar_t *sys, + wchar_t packages[32][MAX_PATH], UINT count) +{ + wchar_t payload[MAX_PATH], original[MAX_PATH], other[MAX_PATH], backup[MAX_PATH], runtime[MAX_PATH]; + UINT i; + + if (!count || swprintf_s(payload, MAX_PATH, L"%s\\AppSandbox-NVIDIA-DLSS-shim.dll", native_dir) < 0 || + swprintf_s(original, MAX_PATH, L"%s\\nvapi64.dll", packages[0]) < 0 || + swprintf_s(runtime, MAX_PATH, L"%s\\nvapi64.dll", sys) < 0 || + swprintf_s(backup, MAX_PATH, L"%s\\appsandbox-nvapi64.dll", sys) < 0) + return FALSE; + if (!validate_dll_export(payload, IMAGE_FILE_MACHINE_AMD64, "appsandbox_nvapi") || + !validate_dll_export(payload, IMAGE_FILE_MACHINE_AMD64, "nvapi_QueryInterface") || + !validate_dll_export(original, IMAGE_FILE_MACHINE_AMD64, "nvapi_QueryInterface") || + validate_dll_export(original, IMAGE_FILE_MACHINE_AMD64, "appsandbox_nvapi")) return FALSE; + for (i = 1; i < count; i++) { + if (swprintf_s(other, MAX_PATH, L"%s\\nvapi64.dll", packages[i]) < 0 || + !files_equal(original, other)) return FALSE; + } + if (!replace_file(original, backup)) return FALSE; + return files_equal(payload, runtime) || + ((GetFileAttributesW(runtime) == INVALID_FILE_ATTRIBUTES || grant_system_file_control(runtime)) && + replace_file(payload, runtime)); +} + static const char *json_skip_whitespace(const char *p) { while (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t') p++; @@ -677,7 +846,7 @@ static BOOL provision_gl_vk_for_arch(const wchar_t *dir, const wchar_t *native_d if (native_dir && native_dir[0]) swprintf_s(src, MAX_PATH, L"%s\\%s", native_dir, payload); if (src[0] && validate_opengl_dll(src, machine, TRUE) && - (count = find_nvidia_vulkan_manifests(manifests, driver_sys, manifest)) != 0 && + (count = find_nvidia_driver_files(manifests, driver_sys, manifest)) != 0 && is_microsoft_opengl(backup) && validate_opengl_dll(backup, machine, FALSE)) { swprintf_s(backend, MAX_PATH, L"%s\\appsandbox-opengl32.dll", sys); if (replace_file(backup, backend) && @@ -769,3 +938,30 @@ BOOL gl_vk_provision_runtime(const wchar_t *dir, const wchar_t *native_dir, native_runtime); #endif } + +BOOL nvidia_dlss_provision(const wchar_t *native_dir) +{ +#if defined(_M_X64) + wchar_t sys[MAX_PATH], payload[MAX_PATH], packages[32][MAX_PATH]; + UINT count, i, length; + + if (!native_dir || !native_dir[0]) return TRUE; + if (swprintf_s(payload, MAX_PATH, L"%s\\AppSandbox-NVIDIA-DLSS-shim.dll", native_dir) < 0) + return FALSE; + if (GetFileAttributesW(payload) == INVALID_FILE_ATTRIBUTES) return TRUE; + length = GetSystemDirectoryW(sys, MAX_PATH); + if (!length || length >= MAX_PATH) return FALSE; + count = find_nvidia_driver_files(packages, sys, L"_nvngx.dll"); + if (!count) return TRUE; + for (i = 0; i < count; i++) { + wchar_t *slash = wcsrchr(packages[i], L'\\'); + if (!slash) return FALSE; + *slash = 0; + if (!nvidia_ngx_junction(sys, packages[i])) return FALSE; + } + return provision_nvapi(native_dir, sys, packages, count); +#else + (void)native_dir; + return TRUE; +#endif +} diff --git a/tools/agent/gl_vk_provision.h b/tools/agent/gl_vk_provision.h index ae43c37..8724e62 100644 --- a/tools/agent/gl_vk_provision.h +++ b/tools/agent/gl_vk_provision.h @@ -6,5 +6,6 @@ BOOL gpu_prefers_system_opengl(void); BOOL gl_vk_provision_runtime(const wchar_t *dir, const wchar_t *native_dir, const wchar_t *sys, BOOL *native_runtime); +BOOL nvidia_dlss_provision(const wchar_t *native_dir); #endif diff --git a/tools/nvidia/AppSandbox-NVIDIA-DLSS-shim.vcxproj b/tools/nvidia/AppSandbox-NVIDIA-DLSS-shim.vcxproj new file mode 100644 index 0000000..827257e --- /dev/null +++ b/tools/nvidia/AppSandbox-NVIDIA-DLSS-shim.vcxproj @@ -0,0 +1,102 @@ + + + + + Debug + ARM64 + + + Debug + x64 + + + Release + ARM64 + + + Release + x64 + + + + 17.0 + {B689C521-58F9-47E4-92FC-E110A39D51C8} + nvidia_dlss + 10.0 + + + + DynamicLibrary + v143 + Unicode + + + true + + + false + true + + + + $(MSBuildThisFileDirectory)..\..\ + AppSandbox-NVIDIA-DLSS-shim + true + + + $(SolutionDir)bin\$(Configuration)\nvidia\ + $(SolutionDir)obj\nvidia-dlss\$(Configuration)\ + + + $(SolutionDir)bin\$(Configuration)-ARM64\nvidia\ + $(SolutionDir)obj\nvidia-dlss\$(Configuration)-ARM64\ + + + + Level4 + UNICODE;_UNICODE;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) + true + true + CompileAsC + + + Windows + true + nvapi_exports.def + dxgi.lib;dxguid.lib;%(AdditionalDependencies) + + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + + + + + + + + + + + + + + + + diff --git a/tools/nvidia/nvapi_exports.def b/tools/nvidia/nvapi_exports.def new file mode 100644 index 0000000..518cf48 --- /dev/null +++ b/tools/nvidia/nvapi_exports.def @@ -0,0 +1,3 @@ +EXPORTS + nvapi_QueryInterface + appsandbox_nvapi diff --git a/tools/nvidia/nvapi_shim.c b/tools/nvidia/nvapi_shim.c new file mode 100644 index 0000000..ada9d9b --- /dev/null +++ b/tools/nvidia/nvapi_shim.c @@ -0,0 +1,202 @@ +#define COBJMACROS +#include +#include + +typedef void *(__cdecl *QueryInterfaceFn)(UINT id); + +static INIT_ONCE g_once = INIT_ONCE_STATIC_INIT; +static QueryInterfaceFn g_query; + +#if defined(_M_X64) +#pragma warning(push) +#pragma warning(disable: 4201) +#include +#include +#pragma warning(pop) + +#define NVAPI_OK 0 +#define NVAPI_NOT_SUPPORTED (-104) +#define NVAPI_MAX_PHYSICAL_GPUS 64 +#define NVAPI_LOGICAL_GPU_INFO 0x842b066e +#define NVAPI_PHYSICAL_GPU_ADAPTER 0x0ff07fde +#define NVAPI_D3D11_FATBIN 0x6086bd93 +#define NVAPI_D3D12_FATBIN 0x70c07832 + +typedef struct { + UINT version; + void *adapter_id; + UINT physical_count; + void *physical_gpus[NVAPI_MAX_PHYSICAL_GPUS]; + UINT reserved[8]; +} LogicalGpuInfo; + +typedef int (__cdecl *EnumPhysicalFn)(void **gpus, UINT *count); +typedef int (__cdecl *LogicalInfoFn)(void *gpu, LogicalGpuInfo *info); +typedef int (__cdecl *PhysicalAdapterFn)(void *gpu, void *luid); +typedef int (__cdecl *FatbinFn)(IUnknown *device, BYTE *supported); + +static LogicalInfoFn g_logical_info; +static PhysicalAdapterFn g_physical_adapter; +static FatbinFn g_d3d11_fatbin; +static FatbinFn g_d3d12_fatbin; +static SRWLOCK g_identity_lock = SRWLOCK_INIT; +static LUID g_device_luid; +static BOOL g_have_device_luid; + +static BOOL find_nvidia_adapter(const LUID *wanted, LUID *luid) +{ + IDXGIFactory1 *factory = NULL; + BOOL found = FALSE; + UINT i; + + if (FAILED(CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&factory))) return FALSE; + for (i = 0; !found; i++) { + IDXGIAdapter1 *adapter = NULL; + DXGI_ADAPTER_DESC1 desc; + HRESULT result; + + if (IDXGIFactory1_EnumAdapters1(factory, i, &adapter) != S_OK) break; + result = IDXGIAdapter1_GetDesc1(adapter, &desc); + IDXGIAdapter1_Release(adapter); + if (FAILED(result) || desc.VendorId != 0x10de || + (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) continue; + if (wanted && (wanted->LowPart != desc.AdapterLuid.LowPart || + wanted->HighPart != desc.AdapterLuid.HighPart)) continue; + if (luid) *luid = desc.AdapterLuid; + found = TRUE; + } + IDXGIFactory1_Release(factory); + return found; +} + +static void capture_device_identity(IUnknown *device) +{ + IDXGIDevice *dxgi = NULL; + ID3D12Device *d3d12 = NULL; + LUID luid = {0}; + BOOL found = FALSE; + + if (!device) return; + if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_IDXGIDevice, (void **)&dxgi))) { + IDXGIAdapter *adapter = NULL; + if (SUCCEEDED(IDXGIDevice_GetAdapter(dxgi, &adapter))) { + DXGI_ADAPTER_DESC desc; + if (SUCCEEDED(IDXGIAdapter_GetDesc(adapter, &desc))) { + luid = desc.AdapterLuid; + found = TRUE; + } + IDXGIAdapter_Release(adapter); + } + IDXGIDevice_Release(dxgi); + } else if (SUCCEEDED(IUnknown_QueryInterface(device, &IID_ID3D12Device, (void **)&d3d12))) { + ID3D12Device_GetAdapterLuid(d3d12, &luid); + ID3D12Device_Release(d3d12); + found = TRUE; + } + if (!found || !find_nvidia_adapter(&luid, NULL)) return; + AcquireSRWLockExclusive(&g_identity_lock); + g_device_luid = luid; + g_have_device_luid = TRUE; + ReleaseSRWLockExclusive(&g_identity_lock); +} + +static BOOL current_device_luid(LUID *luid) +{ + BOOL found; + + AcquireSRWLockShared(&g_identity_lock); + found = g_have_device_luid; + if (found) *luid = g_device_luid; + ReleaseSRWLockShared(&g_identity_lock); + return found || find_nvidia_adapter(NULL, luid); +} + +static int __cdecl logical_gpu_info_hook(void *gpu, LogicalGpuInfo *info) +{ + int result = g_logical_info ? g_logical_info(gpu, info) : NVAPI_NOT_SUPPORTED; + EnumPhysicalFn enumerate; + void *physical[NVAPI_MAX_PHYSICAL_GPUS] = {0}; + UINT count = 0; + LUID luid; + + if (result == NVAPI_OK || !info || + (info->version & 0xffff) < sizeof(*info)) return result; + enumerate = (EnumPhysicalFn)g_query(0xe5ac921f); + if (!enumerate || enumerate(physical, &count) != NVAPI_OK || !count || + !current_device_luid(&luid)) return result; + info->physical_count = 1; + info->physical_gpus[0] = physical[0]; + if (info->adapter_id) memcpy(info->adapter_id, &luid, sizeof(luid)); + return NVAPI_OK; +} + +static int __cdecl physical_gpu_adapter_hook(void *gpu, void *adapter_id) +{ + int result = g_physical_adapter ? g_physical_adapter(gpu, adapter_id) : NVAPI_NOT_SUPPORTED; + LUID luid; + + if (result == NVAPI_OK || !adapter_id || !current_device_luid(&luid)) return result; + memcpy(adapter_id, &luid, sizeof(luid)); + return NVAPI_OK; +} + +static int __cdecl d3d11_fatbin_hook(IUnknown *device, BYTE *supported) +{ + capture_device_identity(device); + return g_d3d11_fatbin ? g_d3d11_fatbin(device, supported) : NVAPI_NOT_SUPPORTED; +} + +static int __cdecl d3d12_fatbin_hook(IUnknown *device, BYTE *supported) +{ + capture_device_identity(device); + return g_d3d12_fatbin ? g_d3d12_fatbin(device, supported) : NVAPI_NOT_SUPPORTED; +} +#endif + +static BOOL CALLBACK load_nvapi(PINIT_ONCE once, PVOID parameter, PVOID *context) +{ + HMODULE module; + (void)once; + (void)parameter; + (void)context; + + module = LoadLibraryExW(L"appsandbox-nvapi64.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!module) return TRUE; + if (GetProcAddress(module, "appsandbox_nvapi")) { + FreeLibrary(module); + return TRUE; + } + g_query = (QueryInterfaceFn)GetProcAddress(module, "nvapi_QueryInterface"); + return TRUE; +} + +void *__cdecl nvapi_QueryInterface(UINT id) +{ + void *original; + + InitOnceExecuteOnce(&g_once, load_nvapi, NULL, NULL); + if (!g_query) return NULL; + original = g_query(id); +#if defined(_M_X64) + switch (id) { + case NVAPI_LOGICAL_GPU_INFO: + g_logical_info = (LogicalInfoFn)original; + return (void *)logical_gpu_info_hook; + case NVAPI_PHYSICAL_GPU_ADAPTER: + g_physical_adapter = (PhysicalAdapterFn)original; + return (void *)physical_gpu_adapter_hook; + case NVAPI_D3D11_FATBIN: + g_d3d11_fatbin = (FatbinFn)original; + return (void *)d3d11_fatbin_hook; + case NVAPI_D3D12_FATBIN: + g_d3d12_fatbin = (FatbinFn)original; + return (void *)d3d12_fatbin_hook; + } +#endif + return original; +} + +DWORD __cdecl appsandbox_nvapi(void) +{ + return 0x41534201; +} diff --git a/tools/sign/make-release.ps1 b/tools/sign/make-release.ps1 index 4fba6c5..a0a8f37 100644 --- a/tools/sign/make-release.ps1 +++ b/tools/sign/make-release.ps1 @@ -408,6 +408,7 @@ $required = @( ) if ($Platform -eq 'x64') { $required += 'resources\nvidia\AppSandbox-NVIDIA-VK-GL-shim.dll', 'resources\nvidia\AppSandbox-NVIDIA-VK-GL-shim32.dll' + $required += 'resources\nvidia\AppSandbox-NVIDIA-DLSS-shim.dll' } $missing = @($required | Where-Object { -not (Test-Path (Join-Path $stage $_)) }) if ($missing.Count) { throw "Refusing to package - missing expected artifacts: $($missing -join ', ')" }