summaryrefslogtreecommitdiff
path: root/drivers/gpu/nova-core/fsp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nova-core/fsp.rs')
-rw-r--r--drivers/gpu/nova-core/fsp.rs103
1 files changed, 66 insertions, 37 deletions
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 8fc243c66e35..f0c595175c9c 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -11,6 +11,7 @@ use kernel::{
device,
dma::Coherent,
io::poll::read_poll_timeout,
+ num::TryIntoBounded,
prelude::*,
ptr::{
Alignable,
@@ -31,9 +32,12 @@ use crate::{
Falcon, //
},
fb::FbLayout,
- firmware::fsp::{
- FmcSignatures,
- FspFirmware, //
+ firmware::{
+ fsp::{
+ FmcSignatures,
+ FspFirmware, //
+ },
+ FIRMWARE_VERSION, //
},
gpu::Chipset,
gsp::GspFmcBootParams,
@@ -57,12 +61,35 @@ struct NvdmPayloadCommandResponse {
error_code: u32,
}
-/// Complete FSP response structure with MCTP and NVDM headers.
+/// Common MCTP and NVDM headers shared by all FSP messages.
#[repr(C, packed)]
#[derive(Clone, Copy)]
-struct FspResponse {
+struct FspMessageHeader {
mctp_header: MctpHeader,
nvdm_header: NvdmHeader,
+}
+
+// SAFETY: FspMessageHeader is a packed C struct with only integral fields.
+unsafe impl AsBytes for FspMessageHeader {}
+
+// SAFETY: FspMessageHeader is a packed C struct with only integral fields.
+unsafe impl FromBytes for FspMessageHeader {}
+
+impl FspMessageHeader {
+ /// Construct a standard FSP message header for the given NVDM type.
+ fn new(nvdm_type: NvdmType) -> Self {
+ Self {
+ mctp_header: MctpHeader::single_packet(),
+ nvdm_header: NvdmHeader::new(nvdm_type),
+ }
+ }
+}
+
+/// Complete FSP response structure with MCTP and NVDM headers.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspResponse {
+ header: FspMessageHeader,
response: NvdmPayloadCommandResponse,
}
@@ -94,23 +121,23 @@ struct NvdmPayloadCot {
gsp_boot_args_sysmem_offset: u64,
}
-/// Complete FSP message structure with MCTP and NVDM headers.
+/// Complete FSP COT (Chain of Trust) message structure.
#[repr(C)]
#[derive(Clone, Copy)]
-struct FspMessage {
- mctp_header: MctpHeader,
- nvdm_header: NvdmHeader,
+struct FspCotMessage {
+ header: FspMessageHeader,
cot: NvdmPayloadCot,
}
-impl FspMessage {
- /// Returns an in-place initializer for [`FspMessage`].
+impl FspCotMessage {
+ /// Returns an in-place initializer for [`FspCotMessage`].
fn new<'a>(
fb_layout: &FbLayout,
fsp_fw: &'a FspFirmware,
args: &'a FmcBootArgs,
) -> Result<impl Init<Self> + 'a> {
- // frts_offset is relative to FB end: FRTS_location = FB_END - frts_offset
+ // frts_vidmem_offset is measured from the end of FB, so FRTS sits at
+ // (end of FB) - frts_vidmem_offset.
let frts_vidmem_offset = if !args.resume {
let frts_reserved_size = fb_layout.heap.len() + u64::from(fb_layout.pmu_reserved_size);
@@ -131,8 +158,7 @@ impl FspMessage {
let size = num::usize_into_u16::<{ core::mem::size_of::<NvdmPayloadCot>() }>();
Ok(init!(Self {
- mctp_header: MctpHeader::single_packet(),
- nvdm_header: NvdmHeader::new(NvdmType::Cot),
+ header: FspMessageHeader::new(NvdmType::Cot),
// The payload is packed, so we cannot use `init!`. Initialize it member-by-member using
// `chain`.
cot <- pin_init::init_zeroed(),
@@ -143,8 +169,8 @@ impl FspMessage {
msg.cot.gsp_fmc_sysmem_offset = fsp_fw.fmc_image.dma_handle();
msg.cot.frts_vidmem_offset = frts_vidmem_offset;
msg.cot.frts_vidmem_size = frts_size;
- // frts_sysmem_* intentionally left at zero for now, but will be needed for e.g.
- // systems without VRAM.
+ // frts_sysmem_* are left at zero because this path places FRTS in vidmem. The sysmem
+ // fields point to an FRTS buffer in sysmem instead, for systems without VRAM.
msg.cot.gsp_boot_args_sysmem_offset = args.fmc_boot_params.dma_handle();
msg.cot.sigs = *fsp_fw.fmc_sigs;
@@ -153,11 +179,11 @@ impl FspMessage {
}
}
-// SAFETY: `FspMessage` is `#[repr(C)]` with no padding, so all of its
+// SAFETY: `FspCotMessage` is `#[repr(C)]` with no padding, so all of its
// bytes are initialized.
-unsafe impl AsBytes for FspMessage {}
+unsafe impl AsBytes for FspCotMessage {}
-impl MessageToFsp for FspMessage {
+impl MessageToFsp for FspCotMessage {
const NVDM_TYPE: NvdmType = NvdmType::Cot;
}
@@ -199,28 +225,28 @@ impl FmcBootArgs {
/// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot
/// has completed. It owns the FSP falcon and the FMC firmware, which are used for the subsequent
/// Chain of Trust boot.
-pub(crate) struct Fsp {
- falcon: Falcon<FspEngine>,
+pub(crate) struct Fsp<'a> {
+ falcon: Falcon<'a, FspEngine>,
fsp_fw: FspFirmware,
}
-impl Fsp {
+impl<'a> Fsp<'a> {
/// Waits for FSP secure boot completion, then returns the [`Fsp`] interface.
///
/// Polls the thermal scratch register until FSP signals boot completion or the timeout
/// elapses. Returning an [`Fsp`] only on success guarantees, at the API level, that the
/// interface is not used before secure boot has completed.
pub(crate) fn wait_secure_boot(
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
+ dev: &'a device::Device<device::Bound>,
+ bar: Bar0<'a>,
chipset: Chipset,
- fsp_fw: FspFirmware,
- ) -> Result<Fsp> {
+ ) -> Result<Fsp<'a>> {
/// FSP secure boot completion timeout in milliseconds.
const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
let hal = hal::fsp_hal(chipset).ok_or(ENOTSUPP)?;
- let falcon = Falcon::<FspEngine>::new(dev, chipset)?;
+ let falcon = Falcon::<FspEngine>::new(dev, chipset, bar)?;
+ let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
read_poll_timeout(
|| Ok(hal.fsp_boot_status(bar)),
@@ -236,13 +262,14 @@ impl Fsp {
}
/// Sends a message to FSP and waits for the response.
- fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: Bar0<'_>, msg: &M) -> Result
+ /// Returns the full response buffer on success.
+ fn send_sync_fsp<M>(&mut self, dev: &device::Device, msg: &M) -> Result<KVec<u8>>
where
M: MessageToFsp,
{
- self.falcon.send_msg(bar, msg.as_bytes())?;
+ self.falcon.send_msg(msg.as_bytes())?;
- let response_buf = self.falcon.recv_msg(bar).inspect_err(|e| {
+ let response_buf = self.falcon.recv_msg().inspect_err(|e| {
dev_err!(dev, "FSP response error: {:?}\n", e);
})?;
@@ -251,8 +278,8 @@ impl Fsp {
EIO
})?;
- let mctp_header = response.mctp_header;
- let nvdm_header = response.nvdm_header;
+ let mctp_header = response.header.mctp_header;
+ let nvdm_header = response.header.nvdm_header;
let command_nvdm_type = response.response.command_nvdm_type;
let error_code = response.response.error_code;
@@ -274,7 +301,7 @@ impl Fsp {
return Err(EIO);
}
- if command_nvdm_type != u8::from(M::NVDM_TYPE).into() {
+ if command_nvdm_type.try_into_bounded() != Some(M::NVDM_TYPE.into()) {
dev_err!(
dev,
"Expected NVDM type {:?} in reply, got {:#x}\n",
@@ -294,7 +321,7 @@ impl Fsp {
return Err(EIO);
}
- Ok(())
+ Ok(response_buf)
}
/// Boots GSP FMC via FSP Chain of Trust.
@@ -304,15 +331,17 @@ impl Fsp {
pub(crate) fn boot_fmc(
&mut self,
dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
fb_layout: &FbLayout,
args: &FmcBootArgs,
) -> Result {
dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);
- let msg = KBox::init(FspMessage::new(fb_layout, &self.fsp_fw, args)?, GFP_KERNEL)?;
+ let msg = KBox::init(
+ FspCotMessage::new(fb_layout, &self.fsp_fw, args)?,
+ GFP_KERNEL,
+ )?;
- self.send_sync_fsp(dev, bar, &*msg)?;
+ let _response_buf = self.send_sync_fsp(dev, &*msg)?;
dev_dbg!(dev, "FSP Chain of Trust completed successfully\n");
Ok(())