summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core/firmware/gsp_fmc.rs
blob: 94fcc86c7dff1f2c6b0f251e846d0236529a31be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

//! GSP-FMC (First Mutable Code) is loaded by FSP into GSP to serve as the loader and verifier of
//! GSP-RM.

use kernel::{
    device,
    dma::Coherent,
    prelude::*, //
};

use crate::{
    firmware::tlv::{
        request_tlv, //
        Tlv,
    },
    gpu::Chipset, //
};

/// Size of the GSP-FMC SHA-384 hash, in bytes.
const FMC_HASH_SIZE: usize = 48;
/// Maximum size of the GSP-FMC public key (RSA-3072), in bytes.
///
/// The `PKEY` tag may be shorter, so the remaining bytes are zero-padded.
const FMC_PKEY_SIZE: usize = 384;
/// Maximum size of the GSP-FMC signature (RSA-3072), in bytes.
///
/// The `SIGN` tag may be shorter, so the remaining bytes are zero-padded.
const FMC_SIG_SIZE: usize = 384;

/// Structure to hold FMC signatures.
///
/// C representation is used because this type is used for communication with the FSP.
#[derive(Debug, Clone, Copy, Zeroable)]
#[repr(C)]
pub(crate) struct FmcSignatures {
    pub(crate) hash384: [u8; FMC_HASH_SIZE],
    pub(crate) public_key: [u8; FMC_PKEY_SIZE],
    pub(crate) signature: [u8; FMC_SIG_SIZE],
}

pub(crate) struct GspFmcFirmware<'a> {
    /// FMC firmware image data
    pub(crate) fmc_image: Coherent<'a, [u8]>,
    /// FMC firmware signatures.
    pub(crate) fmc_sigs: KBox<FmcSignatures>,
}

impl<'a> GspFmcFirmware<'a> {
    pub(crate) fn new(dev: &'a device::Device<device::Bound>, chipset: Chipset) -> Result<Self> {
        let fw = request_tlv(dev, chipset, "fmc")?;
        let tlv = Tlv::new(fw.data())?;
        dev_dbg!(
            dev,
            "loaded GSP-FMC firmware v{}\n",
            tlv.get_string(b"VERS")?
        );

        let fmc_image_data = tlv.get_bytes(b"BLOB")?;
        let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?;

        Ok(Self {
            fmc_image,
            fmc_sigs: Self::extract_fmc_signatures(&tlv, dev)?,
        })
    }

    /// Extract FMC firmware signatures for Chain of Trust verification.
    ///
    /// Extracts real cryptographic signatures from FMC TLV firmware tags.
    /// Returns signatures in a heap-allocated structure to prevent stack overflow.
    fn extract_fmc_signatures(tlv: &Tlv<'_>, dev: &device::Device) -> Result<KBox<FmcSignatures>> {
        let hash_section = tlv.get_bytes(b"HASH")?;
        let pkey_section = tlv.get_bytes(b"PKEY")?;
        let sig_section = tlv.get_bytes(b"SIGN")?;

        // The hash section is a SHA-384 output: it must be exactly `FMC_HASH_SIZE` bytes.
        if hash_section.len() != FMC_HASH_SIZE {
            dev_err!(
                dev,
                "FMC hash section size {} != expected {}\n",
                hash_section.len(),
                FMC_HASH_SIZE
            );
            return Err(EINVAL);
        }

        // The key and signature sections are zero-padded to a fixed maximum, so they may be
        // shorter, but must not exceed the destination buffers.
        if pkey_section.len() > FMC_PKEY_SIZE {
            dev_err!(
                dev,
                "FMC public key section size {} > maximum {}\n",
                pkey_section.len(),
                FMC_PKEY_SIZE
            );
            return Err(EINVAL);
        }
        if sig_section.len() > FMC_SIG_SIZE {
            dev_err!(
                dev,
                "FMC signature section size {} > maximum {}\n",
                sig_section.len(),
                FMC_SIG_SIZE
            );
            return Err(EINVAL);
        }

        // Initialize the signatures in place to avoid building the large `FmcSignatures` on the
        // stack, then fill each section from the firmware.
        let signatures = KBox::init(
            pin_init::init_zeroed::<FmcSignatures>().chain(|sigs| {
                // PANIC: src and dst lengths are both `FMC_HASH_SIZE` (verified above).
                sigs.hash384.copy_from_slice(hash_section);
                // PANIC: dst is sliced to src.len(); src.len() <= `FMC_PKEY_SIZE` (verified above).
                sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section);
                // PANIC: dst is sliced to src.len(); src.len() <= `FMC_SIG_SIZE` (verified above).
                sigs.signature[..sig_section.len()].copy_from_slice(sig_section);
                Ok(())
            }),
            GFP_KERNEL,
        )?;

        Ok(signatures)
    }
}