diff --git a/efi/preinstall/check_host_security.go b/efi/preinstall/check_host_security.go index 7e96fd46..4d673f9e 100644 --- a/efi/preinstall/check_host_security.go +++ b/efi/preinstall/check_host_security.go @@ -303,7 +303,12 @@ func checkDiscreteTPMPartialResetAttackMitigationStatusAMD64(env internal_efi.Ho // checkHostSecurityARM64Platform selects the platform-specific firmware // integrity check. Tests replace this to supply synthetic platforms. var checkHostSecurityARM64Platform = func(env internal_efi.HostEnvironmentARM64, cpuManufacturer string) (platformFirmwareIntegrityConfig, error) { - return platformFirmwareIntegrityNone, &UnsupportedPlatformError{fmt.Errorf("unsupported CPU manufacturer: %s", cpuManufacturer)} + switch cpuManufacturer { + case "NVIDIA": + return checkHostSecurityNVIDIA(env) + default: + return platformFirmwareIntegrityNone, &UnsupportedPlatformError{fmt.Errorf("unsupported CPU manufacturer: %s", cpuManufacturer)} + } } func checkHostSecurityARM64(env internal_efi.HostEnvironment, log *tcglog.Log) (platformFirmwareIntegrityConfig, error) { diff --git a/efi/preinstall/check_host_security_nvidia.go b/efi/preinstall/check_host_security_nvidia.go new file mode 100644 index 00000000..1bd2f751 --- /dev/null +++ b/efi/preinstall/check_host_security_nvidia.go @@ -0,0 +1,61 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2026 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package preinstall + +import ( + "fmt" + "strings" + + internal_efi "github.com/snapcore/secboot/internal/efi" +) + +const nvidiaDGXSparkCPUVersion = "GB10" +const nvidiaRTXSparkCPUVersionPrefix = "NVIDIA RTX Spark" + +// isNvidiaSparkCPUVersion reports whether cpuVersion identifies a supported NVIDIA +// Spark platform: either the DGX Spark (exact match on "GB10") or any RTX Spark +// variant (prefix match on "NVIDIA RTX Spark"). +func isNvidiaSparkCPUVersion(cpuVersion string) bool { + return cpuVersion == nvidiaDGXSparkCPUVersion || strings.HasPrefix(cpuVersion, nvidiaRTXSparkCPUVersionPrefix) +} + +func checkHostSecurityNVIDIA(env internal_efi.HostEnvironmentARM64) (platformFirmwareIntegrityConfig, error) { + cpuVersion, err := env.CPUVersion() + if err != nil { + return platformFirmwareIntegrityNone, &UnsupportedPlatformError{fmt.Errorf("cannot determine CPU version: %w", err)} + } + + switch { + case isNvidiaSparkCPUVersion(cpuVersion): + return checkHostSecurityNVIDIASpark(env) + default: + return platformFirmwareIntegrityNone, &UnsupportedPlatformError{fmt.Errorf("unsupported NVIDIA CPU version: %s", cpuVersion)} + } +} + +func checkHostSecurityNVIDIASpark(env internal_efi.HostEnvironmentARM64) (platformFirmwareIntegrityConfig, error) { + // TODO: Implement proper HW ROT fusing checks, once we have the documentation + // from NVIDIA to do so. This will involve checking fuses and will return + // platformFirmwareIntegrityVerified if set correctly. + + // TODO: Implement proper debug authentication checks, once we have the documentation + // from NVIDIA to do so. + return platformFirmwareIntegrityVerified, nil +} diff --git a/efi/preinstall/check_host_security_nvidia_test.go b/efi/preinstall/check_host_security_nvidia_test.go new file mode 100644 index 00000000..414488c1 --- /dev/null +++ b/efi/preinstall/check_host_security_nvidia_test.go @@ -0,0 +1,74 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2026 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package preinstall_test + +import ( + "errors" + + . "github.com/snapcore/secboot/efi/preinstall" + "github.com/snapcore/secboot/internal/efitest" + "github.com/snapcore/secboot/internal/testutil" + . "gopkg.in/check.v1" +) + +func (s *hostSecurityARM64Suite) TestCheckHostSecurityGoodDGXSparkVerified(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "GB10"), + efitest.WithSysfsDevices(makeArm64IOMMUDevices()...), + ) + log := efitest.NewLog(c, &efitest.LogOptions{}) + + integrity, err := CheckHostSecurity(env, log) + c.Check(err, IsNil) + c.Check(integrity, Equals, PlatformFirmwareIntegrityVerified) +} + +func (s *hostSecurityARM64Suite) TestCheckHostSecurityGoodRTXSparkVerified(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "NVIDIA RTX Spark N1X (5120-core GPU, 18-core CPU)"), + efitest.WithSysfsDevices(makeArm64IOMMUDevices()...), + ) + log := efitest.NewLog(c, &efitest.LogOptions{}) + + integrity, err := CheckHostSecurity(env, log) + c.Check(err, IsNil) + c.Check(integrity, Equals, PlatformFirmwareIntegrityVerified) +} + +func (s *hostSecurityARM64Suite) TestCheckHostSecurityGoodRTXSparkAlternativeSKUVerified(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "NVIDIA RTX Spark N1X (4096-core GPU, 16-core CPU)"), + efitest.WithSysfsDevices(makeArm64IOMMUDevices()...), + ) + log := efitest.NewLog(c, &efitest.LogOptions{}) + + integrity, err := CheckHostSecurity(env, log) + c.Check(err, IsNil) + c.Check(integrity, Equals, PlatformFirmwareIntegrityVerified) +} + +func (s *hostSecurityARM64Suite) TestCheckHostSecurityErrUnsupportedNVIDIACPUVersion(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts(efitest.WithARM64Environment("NVIDIA", "N2X")) + + _, err := CheckHostSecurity(env, nil) + c.Check(err, ErrorMatches, `unsupported platform: unsupported NVIDIA CPU version: N2X`) + var upe *UnsupportedPlatformError + c.Check(errors.As(err, &upe), testutil.IsTrue) +} diff --git a/efi/preinstall/check_host_security_test.go b/efi/preinstall/check_host_security_test.go index 670cf328..9b81e2b0 100644 --- a/efi/preinstall/check_host_security_test.go +++ b/efi/preinstall/check_host_security_test.go @@ -683,6 +683,17 @@ func (s *hostSecurityARM64Suite) TestCheckDiscreteTPMPartialResetAttackMitigatio c.Check(status, Equals, DtpmPartialResetAttackMitigationNotRequired) } +func (s *hostSecurityARM64Suite) TestCheckDiscreteTPMPartialResetAttackMitigationStatusUnavailableForNvidiaDGXSpark(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "GB10"), + efitest.WithSysfsDevices(makeArm64TPMDevice("tpm_crb")), + ) + + status, err := CheckDiscreteTPMPartialResetAttackMitigationStatus(env, makeArm64PCRResults(c)) + c.Check(err, IsNil) + c.Check(status, Equals, DtpmPartialResetAttackMitigationUnavailable) +} + func (s *hostSecuritySuite) TestCheckHostSecurityUnsupportedArchitecture(c *C) { restore := MockRuntimeGOARCH("ppc64le") defer restore() diff --git a/efi/preinstall/check_tpm.go b/efi/preinstall/check_tpm.go index 72593954..078ff680 100644 --- a/efi/preinstall/check_tpm.go +++ b/efi/preinstall/check_tpm.go @@ -533,7 +533,12 @@ func isTPMDiscreteARM64(env internal_efi.HostEnvironment) (bool, error) { return false, &UnsupportedPlatformError{fmt.Errorf("cannot determine CPU manufacturer: %w", err)} } - return false, &UnsupportedPlatformError{fmt.Errorf("unsupported CPU manufacturer: %s", cpuManufacturer)} + switch cpuManufacturer { + case "NVIDIA": + return isTPMDiscreteNvidia(arm64Env) + default: + return false, &UnsupportedPlatformError{fmt.Errorf("unsupported CPU manufacturer: %s", cpuManufacturer)} + } } // isTPMFirmwareOptee determines whether the default TPM is an OP-TEE firmware TPM, diff --git a/efi/preinstall/check_tpm_nvidia.go b/efi/preinstall/check_tpm_nvidia.go new file mode 100644 index 00000000..9041734d --- /dev/null +++ b/efi/preinstall/check_tpm_nvidia.go @@ -0,0 +1,42 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2026 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package preinstall + +import ( + "fmt" + + internal_efi "github.com/snapcore/secboot/internal/efi" +) + +// isTPMDiscreteNvidia determines whether the default TPM is discrete on NVIDIA systems +func isTPMDiscreteNvidia(env internal_efi.HostEnvironmentARM64) (bool, error) { + cpuVersion, err := env.CPUVersion() + if err != nil { + return false, &UnsupportedPlatformError{fmt.Errorf("cannot determine CPU version: %w", err)} + } + + switch { + // We just happen to know that the NVIDIA DGX Spark and RTX Spark have a dTPM + case isNvidiaSparkCPUVersion(cpuVersion): + return true, nil + default: + return false, &UnsupportedPlatformError{fmt.Errorf("unsupported NVIDIA CPU version: %s", cpuVersion)} + } +} diff --git a/efi/preinstall/check_tpm_nvidia_test.go b/efi/preinstall/check_tpm_nvidia_test.go new file mode 100644 index 00000000..09b08811 --- /dev/null +++ b/efi/preinstall/check_tpm_nvidia_test.go @@ -0,0 +1,74 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2026 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package preinstall_test + +import ( + "errors" + + . "github.com/snapcore/secboot/efi/preinstall" + "github.com/snapcore/secboot/internal/efitest" + "github.com/snapcore/secboot/internal/testutil" + . "gopkg.in/check.v1" +) + +func (s *tpmARM64Suite) TestIsTPMDiscreteDGXSpark(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "GB10"), + efitest.WithSysfsDevices(makeArm64TPMDevice("tpm_crb")), + ) + + discrete, err := IsTPMDiscrete(env) + c.Check(err, IsNil) + c.Check(discrete, testutil.IsTrue) +} + +func (s *tpmARM64Suite) TestIsTPMDiscreteRTXSpark(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "NVIDIA RTX Spark N1X (5120-core GPU, 18-core CPU)"), + efitest.WithSysfsDevices(makeArm64TPMDevice("tpm_crb")), + ) + + discrete, err := IsTPMDiscrete(env) + c.Check(err, IsNil) + c.Check(discrete, testutil.IsTrue) +} + +func (s *tpmARM64Suite) TestIsTPMDiscreteRTXSparkAlternativeSKU(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "NVIDIA RTX Spark N1X (4096-core GPU, 16-core CPU)"), + efitest.WithSysfsDevices(makeArm64TPMDevice("tpm_crb")), + ) + + discrete, err := IsTPMDiscrete(env) + c.Check(err, IsNil) + c.Check(discrete, testutil.IsTrue) +} + +func (s *tpmARM64Suite) TestIsTPMDiscreteErrUnsupportedNVIDIACPUVersion(c *C) { + env := efitest.NewMockHostEnvironmentWithOpts( + efitest.WithARM64Environment("NVIDIA", "N2X"), + efitest.WithSysfsDevices(makeArm64TPMDevice("tpm_crb")), + ) + + _, err := IsTPMDiscrete(env) + c.Check(err, ErrorMatches, `unsupported platform: unsupported NVIDIA CPU version: N2X`) + var upe *UnsupportedPlatformError + c.Check(errors.As(err, &upe), testutil.IsTrue) +} diff --git a/efi/preinstall/checks_fixture_test.go b/efi/preinstall/checks_fixture_test.go index 51e9297f..c41b9a40 100644 --- a/efi/preinstall/checks_fixture_test.go +++ b/efi/preinstall/checks_fixture_test.go @@ -267,5 +267,19 @@ func runChecksPlatformHostFixtures() []runChecksHostFixture { additionalExpectedFlags: RequireLockToPlatformFirmware, arch: "arm64", }, + { + name: "nvidia-dgx-spark-tpm-crb", + capabilities: runChecksHostCapabilityValid | + runChecksHostCapabilityNotVirtualMachine | + runChecksHostCapabilityDiscreteTPM | + runChecksHostCapabilityStartupLocality0AccessibleFromOS | + runChecksHostCapabilityStartupLocality3AccessibleFromOS | + runChecksHostCapabilityStartupLocality4AccessibleFromOS, + environment: efitest.WithARM64Environment("NVIDIA", "GB10"), + virtualizationMode: internal_efi.VirtModeNone, + virtualizationDetection: internal_efi.DetectVirtModeAll, + sysfsDevices: newDevices("tpm_crb", true), + arch: "arm64", + }, } }