diff --git a/.gitignore b/.gitignore
index 5881234..afcf7ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -419,6 +419,12 @@ FodyWeavers.xsd
!/vendor/webview2/arm64
!/vendor/webview2/arm64/**
+# Vendored ARM64 devcon.exe — same explicit un-ignore (the arm64/ rule above
+# otherwise catches it). The x64 devcon at vendor/devcon/devcon.exe is tracked
+# directly; the ARM64 build stages this copy into bin\Release-ARM64\drivers\.
+!/vendor/devcon/arm64
+!/vendor/devcon/arm64/**
+
# Local-only planning and notes — kept on disk for context, not pushed.
/docs/
diff --git a/AppSandbox.sln b/AppSandbox.sln
index e1ac8b5..43906af 100644
--- a/AppSandbox.sln
+++ b/AppSandbox.sln
@@ -178,6 +178,7 @@ Global
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|ARM64.ActiveCfg = Release|ARM64
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|x64.ActiveCfg = Release|x64
{0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|x64.Build.0 = Release|x64
+ {0FA17E55-9C3D-4B21-AE60-5D7C1B2E3F40}.Release|ARM64.Build.0 = Release|ARM64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Directory.Build.props b/Directory.Build.props
index d477a4d..104a162 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -33,4 +33,19 @@
true
+
+
+
+ false
+
diff --git a/src/backend_win/d3dlayers.c b/src/backend_win/d3dlayers.c
index ccd66ee..66f4bfa 100644
--- a/src/backend_win/d3dlayers.c
+++ b/src/backend_win/d3dlayers.c
@@ -7,8 +7,8 @@
* 2. Windows Update FE3 client protocol over WinHTTP (HTTPS):
* GetCookie -> SyncUpdates(filtered by WuCategoryId)
* -> GetExtendedUpdateInfo2 -> time-limited CDN url.
- * The x64 package is located by identifier in the response, so version
- * changes / extra updates do not break resolution.
+ * The package for the build architecture is located by identifier in the
+ * response, so version changes / extra updates do not break resolution.
* 3. Download the .appx (a zip) from the Microsoft CDN.
* 4. Extract with the in-box tar.exe (libarchive handles zip natively).
* 5. Copy the mapping-layer files into \d3dlayers\ (cached).
@@ -36,17 +36,33 @@
#define DL_FE3_PATH_SECURED L"/ClientWebService/client.asmx/secured"
#define DL_SOAP_HEADERS L"Content-Type: application/soap+xml; charset=utf-8\r\n"
#define DL_CACHE_SUBDIR L"d3dlayers"
-#define DL_APPX_NAME L"D3DMappingLayers_x64.appx"
+/* The D3D Mapping Layers Store package ships a per-architecture .appx; pick the
+ one matching the guest (= build) architecture. The host downloads it, so on
+ the ARM64 build we fetch and stage the arm64 layers for the arm64 guest. */
+#if defined(_M_ARM64)
+# define DL_ARCH L"arm64"
+# define DL_ARCH_A "arm64"
+#else
+# define DL_ARCH L"x64"
+# define DL_ARCH_A "x64"
+#endif
+#define DL_APPX_NAME L"D3DMappingLayers_" DL_ARCH L".appx"
+/* Substring identifying the package file in the SyncUpdates response — the
+ InstallerSpecificIdentifier contains e.g. "..._x64_..." / "..._arm64_...". */
+#define DL_ISI_ARCH "_" DL_ARCH_A "_"
#define DL_MAX_ROUNDS 5 /* FE3 faults intermittently; retry the chain */
-/* Files to extract: dest leaf name <- source path inside the .appx. */
+/* Files to extract: dest leaf name <- source path inside the .appx. The arch
+ subfolder inside the package (x64\ / arm64\) is selected by DL_ARCH; the
+ Vulkan ICD manifest is staged under the arch-neutral name dzn_icd.json so the
+ guest agent references one fixed filename regardless of architecture. */
static const wchar_t *const DL_DST[] = {
L"OpenGLOn12.dll", L"dxil.dll", L"OpenCLOn12.dll",
- L"clon12compiler.dll", L"vulkan_dzn.dll", L"dzn_icd.x64.json"
+ L"clon12compiler.dll", L"vulkan_dzn.dll", L"dzn_icd.json"
};
static const wchar_t *const DL_SRC[] = {
- L"x64\\OpenGLOn12.dll", L"x64\\dxil.dll", L"x64\\OpenCLOn12.dll",
- L"x64\\clon12compiler.dll", L"x64\\vulkan_dzn.dll", L"dzn_icd.x64.json"
+ DL_ARCH L"\\OpenGLOn12.dll", DL_ARCH L"\\dxil.dll", DL_ARCH L"\\OpenCLOn12.dll",
+ DL_ARCH L"\\clon12compiler.dll", DL_ARCH L"\\vulkan_dzn.dll", L"dzn_icd." DL_ARCH L".json"
};
#define DL_NFILES ((int)(sizeof(DL_DST)/sizeof(DL_DST[0])))
@@ -318,10 +334,10 @@ done:
/* ---- response parsing ---- */
-/* Find the x64 D3DMappingLayers .appx in a (decoded) SyncUpdates response and
- * resolve its UpdateIdentity by joining the file's enclosing to
- * the matching NewUpdates . */
-static BOOL parse_x64_leaf(const char *buf, char *uid, size_t uid_cch,
+/* Find the D3DMappingLayers .appx matching the build architecture in a
+ * (decoded) SyncUpdates response and resolve its UpdateIdentity by joining the
+ * file's enclosing to the matching NewUpdates . */
+static BOOL parse_leaf(const char *buf, char *uid, size_t uid_cch,
char *rev, size_t rev_cch, char *digest, size_t dig_cch)
{
const char *p = buf;
@@ -340,11 +356,11 @@ static BOOL parse_x64_leaf(const char *buf, char *uid, size_t uid_cch,
!attr_in(tag, "InstallerSpecificIdentifier", isi, sizeof(isi))) {
p = gt + 1; continue;
}
- if (!strstr(isi, "D3DMappingLayers") || !strstr(isi, "_x64_") ||
+ if (!strstr(isi, "D3DMappingLayers") || !strstr(isi, DL_ISI_ARCH) ||
!strstr(fn, ".appx")) {
p = gt + 1; continue;
}
- /* matched the x64 package file */
+ /* matched the package file for this architecture */
attr_in(tag, "Digest", digest, dig_cch);
/* nearest N before this = the ExtendedUpdateInfo id */
@@ -404,7 +420,7 @@ static BOOL parse_file_url(const char *buf, const char *digest,
return FALSE;
}
-/* Resolve the x64 package CDN url from Microsoft. Returns 0 on success. */
+/* Resolve the (build-architecture) package CDN url from Microsoft. Returns 0 on success. */
static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
{
int round, rc = -1;
@@ -438,8 +454,8 @@ static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
if (t1) { sync_req = str_replace_all(t1, "%CATID%", wucatid); free(t1); }
if (sync_req) { sync_resp = fe3_post(DL_FE3_PATH, sync_req); free(sync_req); }
if (sync_resp) html_unescape(sync_resp);
- if (!sync_resp || !parse_x64_leaf(sync_resp, uid, sizeof(uid), rev, sizeof(rev),
- digest, sizeof(digest))) {
+ if (!sync_resp || !parse_leaf(sync_resp, uid, sizeof(uid), rev, sizeof(rev),
+ digest, sizeof(digest))) {
free(sync_resp); Sleep(1500); continue;
}
free(sync_resp);
@@ -462,7 +478,7 @@ static int resolve_url(const char *wucatid, wchar_t *url_out, size_t url_cch)
return rc;
}
-/* The shipped dzn_icd.x64.json gives library_path as "x64\vulkan_dzn.dll",
+/* The shipped dzn_icd..json gives library_path as "\vulkan_dzn.dll",
* relative to the manifest. The layers are staged into one flat directory, so
* point library_path at the DLL's fully-qualified location in the guest.
*
@@ -479,7 +495,7 @@ static void rewrite_dzn_json(const wchar_t *dir)
char *buf, *fixed;
size_t rd;
- swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.x64.json", dir);
+ swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.json", dir);
if (_wfopen_s(&f, path, L"rb") != 0 || !f) return;
fseek(f, 0, SEEK_END); sz = ftell(f); fseek(f, 0, SEEK_SET);
if (sz <= 0 || sz > 65536) { fclose(f); return; }
@@ -489,10 +505,10 @@ static void rewrite_dzn_json(const wchar_t *dir)
fclose(f);
buf[rd] = 0;
- /* On disk the JSON value is the 18 bytes x64\\vulkan_dzn.dll (two
- * backslash characters, JSON-escaped). Match that exactly and replace it
- * with the absolute guest path (each backslash JSON-escaped). */
- fixed = str_replace_all(buf, "x64\\\\vulkan_dzn.dll",
+ /* On disk the JSON value is \\vulkan_dzn.dll (two backslash
+ * characters, JSON-escaped). Match that exactly and replace it with the
+ * absolute guest path (each backslash JSON-escaped). */
+ fixed = str_replace_all(buf, DL_ARCH_A "\\\\vulkan_dzn.dll",
"C:\\\\Windows\\\\AppSandbox\\\\d3dlayers\\\\vulkan_dzn.dll");
free(buf);
if (!fixed) return;
diff --git a/src/backend_win/d3dlayers.h b/src/backend_win/d3dlayers.h
index 41c5588..69222b5 100644
--- a/src/backend_win/d3dlayers.h
+++ b/src/backend_win/d3dlayers.h
@@ -14,7 +14,8 @@
* OpenCLOn12.dll OpenCL -> D3D12
* clon12compiler.dll OpenCL compiler backend
* vulkan_dzn.dll Vulkan (Mesa Dozen) -> D3D12
- * dzn_icd.x64.json Vulkan ICD manifest (points at vulkan_dzn.dll)
+ * dzn_icd.json Vulkan ICD manifest (points at vulkan_dzn.dll); staged under
+ * this arch-neutral name from the package's dzn_icd..json
*/
#ifndef D3DLAYERS_H
diff --git a/src/backend_win/disk_util.c b/src/backend_win/disk_util.c
index 1d2916d..7f22096 100644
--- a/src/backend_win/disk_util.c
+++ b/src/backend_win/disk_util.c
@@ -15,8 +15,27 @@
#include
-#define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-Win64-v10.0.0.0.msi"
-#define SSH_MSI_NAME L"OpenSSH-Win64-v10.0.0.0.msi"
+/* Architecture token written into every autounattend/unattend component's
+ processorArchitecture attribute. Matches the guest, which is the build arch
+ (see ASB_IS_ARM64 in disk_util.h). */
+#if ASB_IS_ARM64
+# define ASB_UA_ARCH L"arm64"
+#else
+# define ASB_UA_ARCH L"amd64"
+#endif
+
+/* OpenSSH Server MSI, staged into the guest and installed by SetupComplete.cmd.
+ The Win32-OpenSSH release ships a per-architecture MSI; pick the one matching
+ the guest (= build) architecture so it installs on an ARM64 guest. */
+#if ASB_IS_ARM64
+# define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-ARM64-v10.0.0.0.msi"
+# define SSH_MSI_NAME L"OpenSSH-ARM64-v10.0.0.0.msi"
+# define SSH_MSI_NAME_A "OpenSSH-ARM64-v10.0.0.0.msi" /* narrow, for the cmd scripts */
+#else
+# define SSH_MSI_URL L"https://github.com/PowerShell/Win32-OpenSSH/releases/download/10.0.0.0p2-Preview/OpenSSH-Win64-v10.0.0.0.msi"
+# define SSH_MSI_NAME L"OpenSSH-Win64-v10.0.0.0.msi"
+# define SSH_MSI_NAME_A "OpenSSH-Win64-v10.0.0.0.msi" /* narrow, for the cmd scripts */
+#endif
static const GUID VHDX_VENDOR_MS = {
0xec984aec, 0xa0f9, 0x47e9,
@@ -212,6 +231,32 @@ static const wchar_t *lang_to_input_locale(const wchar_t *lang)
return L"0409:00000409"; /* fallback */
}
+#if ASB_IS_ARM64
+/* Emit RunSynchronousCommand entries (inside an already-open )
+ that disable the Windows 11 setup hardware requirement checks by writing the
+ HKLM\SYSTEM\Setup\LabConfig bypass keys. ARM Windows HCS exposes no vTPM, so
+ without these Windows Setup's appraiser would refuse to install. `order` is
+ the next value to use; returns the order after the last entry.
+ ARM-only: on x64 the guest gets a real vTPM from HCS and needs no bypass. */
+static int write_tpm_bypass_commands(FILE *f, int order)
+{
+ static const wchar_t *const keys[] = {
+ L"BypassTPMCheck", L"BypassSecureBootCheck", L"BypassRAMCheck",
+ L"BypassStorageCheck", L"BypassCPUCheck"
+ };
+ int i;
+ for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); i++) {
+ fwprintf(f,
+ L" \n"
+ L" %d\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v %s /t REG_DWORD /d 1 /f\n"
+ L" \n",
+ order++, keys[i]);
+ }
+ return order;
+}
+#endif
+
/* Generate autounattend.xml with credentials and VM name substituted.
If is_template, adds SecureStartup-FilterDriver to disable BitLocker
(required for sysprep). */
@@ -238,7 +283,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" %s\n"
@@ -248,7 +293,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -274,19 +319,32 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L" 22\n"
L" 33NTFSC\n"
L" \n"
- L" \n"
+ L" \n",
+ lang, lang_to_input_locale(lang), lang, lang, lang);
+
+ /* ARM only: disable the Windows 11 Setup hardware checks via the
+ HKLM\SYSTEM\Setup\LabConfig keys, from a windowsPE RunSynchronous in the
+ Setup component (runs before the appraiser). ARM Windows HCS has no vTPM,
+ so Setup would otherwise refuse to install. */
+#if ASB_IS_ARM64
+ fwprintf(f, L" \n");
+ write_tpm_bypass_commands(f, 1);
+ fwprintf(f, L" \n");
+#endif
+
+ fwprintf(f,
L" \n"
L" \n"
L"\n"
L" \n"
L" \n"
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -294,7 +352,6 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L" 1\n"
L" reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f\n"
L" \n",
- lang, lang_to_input_locale(lang), lang, lang, lang,
comp_name);
{
@@ -336,7 +393,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
if (is_template) {
fwprintf(f,
L" \n"
L" true\n"
@@ -353,7 +410,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
fwprintf(f,
L" \n"
L" \n"
L" \n"
@@ -364,7 +421,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" \n"
@@ -382,7 +439,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
fwprintf(f,
L" \n"
L" \n"
L" %s\n"
@@ -391,7 +448,7 @@ static BOOL generate_autounattend(const wchar_t *output_path,
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -730,9 +787,9 @@ HRESULT iso_create_resources(const wchar_t *iso_path,
if (ssh_enabled) {
fputs(
"REM Install OpenSSH Server\r\n"
- "if exist \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
+ "if exist \"%ISODRV%\\" SSH_MSI_NAME_A "\" (\r\n"
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
- " msiexec /i \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
+ " msiexec /i \"%ISODRV%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
" net start sshd >> \"%LOG%\" 2>&1\r\n"
@@ -791,13 +848,13 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -821,13 +878,37 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
L" 5\n"
L" cmd /c for %%d in (D E F G H I J) do @if exist %%d:\\SetupComplete.cmd copy /Y %%d:\\SetupComplete.cmd C:\\Windows\\Setup\\Scripts\\\n"
L" \n"
+#if ASB_IS_ARM64
+ /* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on
+ ARM). Orders 6-10 continue this component's RunSynchronous. */
+ L" \n"
+ L" 6\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 1 /f\n"
+ L" \n"
+ L" \n"
+ L" 7\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 1 /f\n"
+ L" \n"
+ L" \n"
+ L" 8\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 1 /f\n"
+ L" \n"
+ L" \n"
+ L" 9\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassStorageCheck /t REG_DWORD /d 1 /f\n"
+ L" \n"
+ L" \n"
+ L" 10\n"
+ L" reg add HKLM\\SYSTEM\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 1 /f\n"
+ L" \n"
+#endif
L" \n"
L" \n"
L" \n"
L"\n"
L" \n"
L" \n"
L" %s\n"
@@ -836,7 +917,7 @@ static BOOL generate_unattend_instance(const wchar_t *output_path,
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -1204,9 +1285,9 @@ static void stage_agent_and_setup(const wchar_t *staging, const wchar_t *res_dir
if (ssh_enabled) {
fputs(
"REM Install OpenSSH Server\r\n"
- "if exist \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
+ "if exist \"%ISODRV%\\" SSH_MSI_NAME_A "\" (\r\n"
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
- " msiexec /i \"%ISODRV%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
+ " msiexec /i \"%ISODRV%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
" net start sshd >> \"%LOG%\" 2>&1\r\n"
@@ -1329,13 +1410,13 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -1364,6 +1445,10 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
L" bcdedit /set testsigning on\n"
L" \n", order++);
}
+#if ASB_IS_ARM64
+ /* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on ARM). */
+ write_tpm_bypass_commands(f, order);
+#endif
}
fwprintf(f,
@@ -1376,7 +1461,7 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
fwprintf(f,
L" \n"
L" \n"
L" %s\n"
@@ -1385,7 +1470,7 @@ BOOL generate_unattend_vhdx(const wchar_t *output_path,
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -1455,13 +1540,13 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" %s\n"
L" \n"
L" \n"
L" \n"
@@ -1490,13 +1575,17 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
L" bcdedit /set testsigning on\n"
L" \n", order++);
}
+#if ASB_IS_ARM64
+ /* ARM only: Windows 11 LabConfig hardware-check bypass keys (no vTPM on ARM). */
+ write_tpm_bypass_commands(f, order);
+#endif
}
fwprintf(f,
L" \n"
L" \n"
L" \n"
L" true\n"
@@ -1509,7 +1598,7 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
fwprintf(f,
L" \n"
L" \n"
L" \n"
@@ -1520,7 +1609,7 @@ BOOL generate_unattend_vhdx_template(const wchar_t *output_path,
L"\n"
L" \n"
L" \n"
L" \n"
@@ -1604,9 +1693,9 @@ BOOL generate_vhdx_setupcomplete(const wchar_t *output_path, BOOL ssh_enabled)
fputs(
"REM Install OpenSSH Server\r\n"
"set SSHDIR=%SystemRoot%\\AppSandbox\r\n"
- "if exist \"%SSHDIR%\\OpenSSH-Win64-v10.0.0.0.msi\" (\r\n"
+ "if exist \"%SSHDIR%\\" SSH_MSI_NAME_A "\" (\r\n"
" echo [SSH] Installing OpenSSH Server... >> \"%LOG%\"\r\n"
- " msiexec /i \"%SSHDIR%\\OpenSSH-Win64-v10.0.0.0.msi\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
+ " msiexec /i \"%SSHDIR%\\" SSH_MSI_NAME_A "\" /qn /norestart >> \"%LOG%\" 2>&1\r\n"
" echo [SSH] msiexec exit code: %errorlevel% >> \"%LOG%\"\r\n"
" sc config sshd start= auto >> \"%LOG%\" 2>&1\r\n"
" net start sshd >> \"%LOG%\" 2>&1\r\n"
diff --git a/src/backend_win/disk_util.h b/src/backend_win/disk_util.h
index a890d9d..2414534 100644
--- a/src/backend_win/disk_util.h
+++ b/src/backend_win/disk_util.h
@@ -3,6 +3,19 @@
#include
+/* Target architecture, resolved at compile time. The supported pairing is
+ x64 build -> x64 host -> x64 guest, and ARM64 build -> ARM host -> ARM guest
+ (no cross-arch installs), so build arch == host arch == guest arch and a
+ single compile-time flag drives every architecture decision: the HCS
+ SecuritySettings (ARM Windows HCS has no vTPM / guest-state isolation), the
+ autounattend processorArchitecture token + Win11 TPM-check bypass, the
+ OpenSSH MSI variant, and the GPU-PV D3D mapping-layer package. */
+#if defined(_M_ARM64)
+# define ASB_IS_ARM64 1
+#else
+# define ASB_IS_ARM64 0
+#endif
+
/* Create a new dynamically-expanding VHDX file.
size_gb: maximum virtual size in gigabytes. */
HRESULT vhdx_create(const wchar_t *path, ULONGLONG size_gb);
diff --git a/src/backend_win/gpu_enum.h b/src/backend_win/gpu_enum.h
index e64fa01..abce334 100644
--- a/src/backend_win/gpu_enum.h
+++ b/src/backend_win/gpu_enum.h
@@ -61,7 +61,7 @@ BOOL gpu_append_lxsslib_share(GpuDriverShareList *list);
/* Append a synthetic Plan9 share (name "AppSandbox.GlLayers") exposing the
host directory that holds the D3D mapping layers (OpenGLOn12/dxil/OpenCLOn12/
- clon12compiler/vulkan_dzn + dzn_icd.x64.json) and the OpenGL ICD shim. The
+ clon12compiler/vulkan_dzn + dzn_icd.json) and the OpenGL ICD shim. The
Windows guest agent copies this share AFTER the GPU driver shares and then
provisions it (stages dxil to System32, sets AppInit_DLLs + the Khronos
OpenCL/Vulkan ICD registry keys). guest_path is C:\Windows\AppSandbox\d3dlayers.
diff --git a/src/backend_win/hcs_vm.c b/src/backend_win/hcs_vm.c
index 2403147..ae10de8 100644
--- a/src/backend_win/hcs_vm.c
+++ b/src/backend_win/hcs_vm.c
@@ -1,6 +1,7 @@
#include "hcs_vm.h"
#include "vm_agent.h"
#include "ui.h"
+#include "disk_util.h" /* ASB_IS_ARM64 */
#include
#include
#include
@@ -1041,9 +1042,14 @@ BOOL hcs_build_vm_json(const VmConfig *config, const wchar_t *endpoint_guid,
name_esc);
}
- /* SecuritySettings with TPM — enabled for all Windows VMs.
- TPM does not block test-signed drivers; only Secure Boot does. */
+ /* SecuritySettings (virtual TPM + GuestStateOnly isolation) — x64 only.
+ The vTPM is required by the Windows 11 guest on x64. ARM Windows HCS
+ supports neither a vTPM nor guest-state isolation, so the whole block is
+ emitted only on x64. The Windows 11 Setup TPM/SecureBoot requirement
+ check is bypassed via the autounattend LabConfig keys (see
+ generate_autounattend in disk_util.c). */
security_section[0] = L'\0';
+#if !ASB_IS_ARM64
if (is_windows) {
wcscpy_s(security_section, 512,
L",\"SecuritySettings\":{"
@@ -1051,6 +1057,7 @@ BOOL hcs_build_vm_json(const VmConfig *config, const wchar_t *endpoint_guid,
L"\"Isolation\":{\"IsolationType\":\"GuestStateOnly\"}"
L"}");
}
+#endif
/* Plan9 shares — GPU driver directories for agent p9copy.
Skip for template VMs (no GPU assigned during template creation). */
diff --git a/tools/agent/agent.c b/tools/agent/agent.c
index 372e5fd..24c454b 100644
--- a/tools/agent/agent.c
+++ b/tools/agent/agent.c
@@ -454,7 +454,7 @@ static void gl_provision(const wchar_t *dir)
The manifest's library_path is the absolute guest DLL path (set host-side
in d3dlayers.c) so the loader can LoadLibraryEx it under
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, which requires a fully-qualified path. */
- swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.x64.json", dir);
+ swprintf_s(path, MAX_PATH, L"%s\\dzn_icd.json", dir);
if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) {
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Khronos\\Vulkan\\Drivers",
0, NULL, 0, KEY_SET_VALUE, NULL, &key, NULL) == ERROR_SUCCESS) {
diff --git a/tools/sign/AppSandboxPackage.vcxproj b/tools/sign/AppSandboxPackage.vcxproj
index 65c1737..b27a463 100644
--- a/tools/sign/AppSandboxPackage.vcxproj
+++ b/tools/sign/AppSandboxPackage.vcxproj
@@ -1,7 +1,7 @@