gpu: nova-core: falcon: store bar and dev in falcon

Store the bound device and `BAR0` mapping in `Falcon` instead of passing
them through every `Falcon` operation. This simplifies the `Falcon` API and
removes repeated `dev`/`bar` plumbing from reset, load, boot, mailbox, DMA,
and GSP/FSP-specific Falcon helpers.

`FalconHal` now receives a reference to a `Falcon` and uses its methods and
members instead of passing them individually.

Suggested-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://rust-for-linux.zulipchat.com/#narrow/channel/509436-Nova/topic/Storing.20driver-bound.20references.20into.20sub-devices/near/599137882
Signed-off-by: Tim Kovalenko <tim.kovalenko@proton.me>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patch.msgid.link/20260625-drm-bar-refactor-v2-1-9db6b890d92e@proton.me
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
Tim Kovalenko 2026-06-26 02:24:47 +00:00 committed by Alexandre Courbot
parent f1bd7119ac
commit 23d66dbab8
17 changed files with 263 additions and 316 deletions

View File

@ -5,10 +5,7 @@
use hal::FalconHal;
use kernel::{
device::{
self,
Device, //
},
device,
dma::{
Coherent,
CoherentBox,
@ -24,7 +21,6 @@
Io,
},
prelude::*,
sync::aref::ARef,
time::Delta,
};
@ -358,41 +354,47 @@ pub(crate) trait FalconFirmware {
}
/// Contains the base parameters common to all Falcon instances.
pub(crate) struct Falcon<E: FalconEngine> {
pub(crate) struct Falcon<'a, E: FalconEngine> {
hal: KBox<dyn FalconHal<E>>,
dev: ARef<device::Device>,
dev: &'a device::Device<device::Bound>,
bar: Bar0<'a>,
}
impl<E: FalconEngine + 'static> Falcon<E> {
impl<'a, E: FalconEngine + 'static> Falcon<'a, E> {
/// Create a new falcon instance.
pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> {
pub(crate) fn new(
dev: &'a device::Device<device::Bound>,
chipset: Chipset,
bar: Bar0<'a>,
) -> Result<Self> {
Ok(Self {
hal: hal::falcon_hal(chipset)?,
dev: dev.into(),
dev,
bar,
})
}
/// Resets DMA-related registers.
pub(crate) fn dma_reset(&self, bar: Bar0<'_>) {
bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
pub(crate) fn dma_reset(&self) {
self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
v.with_allow_phys_no_ctx(true)
});
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
);
}
/// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
pub(crate) fn reset(&self, bar: Bar0<'_>) -> Result {
self.hal.reset_eng(bar)?;
self.hal.select_core(self, bar)?;
self.hal.reset_wait_mem_scrubbing(bar)?;
pub(crate) fn reset(&self) -> Result {
self.hal.reset_eng(self)?;
self.hal.select_core(self)?;
self.hal.reset_wait_mem_scrubbing(self)?;
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_RM::from(bar.read(regs::NV_PMC_BOOT_0).into_raw()),
regs::NV_PFALCON_FALCON_RM::from(self.bar.read(regs::NV_PMC_BOOT_0).into_raw()),
);
Ok(())
@ -404,18 +406,14 @@ pub(crate) fn reset(&self, bar: Bar0<'_>) -> Result {
/// Write a slice to Falcon IMEM memory using programmed I/O (PIO).
///
/// Returns `EINVAL` if `img.len()` is not a multiple of 4.
fn pio_wr_imem_slice(
&self,
bar: Bar0<'_>,
load_offsets: FalconPioImemLoadTarget<'_>,
) -> Result {
fn pio_wr_imem_slice(&self, load_offsets: FalconPioImemLoadTarget<'_>) -> Result {
// Rejecting misaligned images here allows us to avoid checking
// inside the loops.
if load_offsets.data.len() % 4 != 0 {
return Err(EINVAL);
}
bar.write(
self.bar.write(
WithBase::of::<E>().at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMC::zeroed()
.with_secure(load_offsets.secure)
@ -426,13 +424,13 @@ fn pio_wr_imem_slice(
for (n, block) in load_offsets.data.chunks(MEM_BLOCK_ALIGNMENT).enumerate() {
let n = u16::try_from(n)?;
let tag: u16 = load_offsets.start_tag.checked_add(n).ok_or(ERANGE)?;
bar.write(
self.bar.write(
WithBase::of::<E>().at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMT::zeroed().with_tag(tag),
);
for word in block.chunks_exact(4) {
let w = [word[0], word[1], word[2], word[3]];
bar.write(
self.bar.write(
WithBase::of::<E>().at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_IMEMD::zeroed().with_data(u32::from_le_bytes(w)),
);
@ -445,18 +443,14 @@ fn pio_wr_imem_slice(
/// Write a slice to Falcon DMEM memory using programmed I/O (PIO).
///
/// Returns `EINVAL` if `img.len()` is not a multiple of 4.
fn pio_wr_dmem_slice(
&self,
bar: Bar0<'_>,
load_offsets: FalconPioDmemLoadTarget<'_>,
) -> Result {
fn pio_wr_dmem_slice(&self, load_offsets: FalconPioDmemLoadTarget<'_>) -> Result {
// Rejecting misaligned images here allows us to avoid checking
// inside the loops.
if load_offsets.data.len() % 4 != 0 {
return Err(EINVAL);
}
bar.write(
self.bar.write(
WithBase::of::<E>().at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_DMEMC::zeroed()
.with_aincw(true)
@ -465,7 +459,7 @@ fn pio_wr_dmem_slice(
for word in load_offsets.data.chunks_exact(4) {
let w = [word[0], word[1], word[2], word[3]];
bar.write(
self.bar.write(
WithBase::of::<E>().at(Self::PIO_PORT),
regs::NV_PFALCON_FALCON_DMEMD::zeroed().with_data(u32::from_le_bytes(w)),
);
@ -477,29 +471,28 @@ fn pio_wr_dmem_slice(
/// Perform a PIO copy into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
&self,
bar: Bar0<'_>,
fw: &F,
) -> Result {
bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
self.bar.update(regs::NV_PFALCON_FBIF_CTL::of::<E>(), |v| {
v.with_allow_phys_no_ctx(true)
});
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMACTL::zeroed(),
);
if let Some(imem_ns) = fw.imem_ns_load_params() {
self.pio_wr_imem_slice(bar, imem_ns)?;
self.pio_wr_imem_slice(imem_ns)?;
}
if let Some(imem_sec) = fw.imem_sec_load_params() {
self.pio_wr_imem_slice(bar, imem_sec)?;
self.pio_wr_imem_slice(imem_sec)?;
}
self.pio_wr_dmem_slice(bar, fw.dmem_load_params())?;
self.pio_wr_dmem_slice(fw.dmem_load_params())?;
self.hal.program_brom(self, bar, &fw.brom_params());
self.hal.program_brom(self, &fw.brom_params());
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
);
@ -513,7 +506,6 @@ pub(crate) fn pio_load<F: FalconFirmware<Target = E> + FalconPioLoadable>(
/// `sec` is set if the loaded firmware is expected to run in secure mode.
fn dma_wr(
&self,
bar: Bar0<'_>,
dma_obj: &Coherent<[u8]>,
target_mem: FalconMem,
load_offsets: FalconDmaLoadTarget,
@ -571,7 +563,7 @@ fn dma_wr(
// Set up the base source DMA address.
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMATRFBASE::zeroed().with_base(
// CAST: `as u32` is used on purpose since we do want to strip the upper bits,
@ -579,7 +571,7 @@ fn dma_wr(
(dma_start >> 8) as u32,
),
);
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMATRFBASE1::zeroed().try_with_base(dma_start >> 40)?,
);
@ -590,23 +582,23 @@ fn dma_wr(
for pos in (0..num_transfers).map(|i| i * DMA_LEN) {
// Perform a transfer of size `DMA_LEN`.
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMATRFMOFFS::zeroed()
.try_with_offs(load_offsets.dst_start + pos)?,
);
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_DMATRFFBOFFS::zeroed().with_offs(src_start + pos),
);
bar.write(WithBase::of::<E>(), cmd);
self.bar.write(WithBase::of::<E>(), cmd);
// Wait for the transfer to complete.
// TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
// should ever take that long.
read_poll_timeout(
|| Ok(bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
|| Ok(self.bar.read(regs::NV_PFALCON_FALCON_DMATRFCMD::of::<E>())),
|r| r.idle(),
Delta::ZERO,
Delta::from_secs(2),
@ -617,12 +609,7 @@ fn dma_wr(
}
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
&self,
dev: &Device<device::Bound>,
bar: Bar0<'_>,
fw: &F,
) -> Result {
fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) -> Result {
// DMA object with firmware content as the source of the DMA engine.
let dma_obj = {
let fw_slice = fw.as_slice();
@ -630,7 +617,7 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
// DMA copies are done in chunks of `MEM_BLOCK_ALIGNMENT`, so pad the length
// accordingly and fill with `0`.
let mut dma_obj = CoherentBox::zeroed_slice(
dev,
self.dev,
fw_slice.len().next_multiple_of(MEM_BLOCK_ALIGNMENT),
GFP_KERNEL,
)?;
@ -642,24 +629,20 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
dma_obj.into()
};
self.dma_reset(bar);
bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| {
v.with_target(FalconFbifTarget::CoherentSysmem)
.with_mem_type(FalconFbifMemType::Physical)
});
self.dma_reset();
self.bar
.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::<E>().at(0), |v| {
v.with_target(FalconFbifTarget::CoherentSysmem)
.with_mem_type(FalconFbifMemType::Physical)
});
self.dma_wr(
bar,
&dma_obj,
FalconMem::ImemSecure,
fw.imem_sec_load_params(),
)?;
self.dma_wr(bar, &dma_obj, FalconMem::Dmem, fw.dmem_load_params())?;
self.dma_wr(&dma_obj, FalconMem::ImemSecure, fw.imem_sec_load_params())?;
self.dma_wr(&dma_obj, FalconMem::Dmem, fw.dmem_load_params())?;
self.hal.program_brom(self, bar, &fw.brom_params());
self.hal.program_brom(self, &fw.brom_params());
// Set `BootVec` to start of non-secure code.
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_BOOTVEC::zeroed().with_value(fw.boot_addr()),
);
@ -668,10 +651,10 @@ fn dma_load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
}
/// Wait until the falcon CPU is halted.
pub(crate) fn wait_till_halted(&self, bar: Bar0<'_>) -> Result<()> {
pub(crate) fn wait_till_halted(&self) -> Result<()> {
// TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
read_poll_timeout(
|| Ok(bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())),
|| Ok(self.bar.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())),
|r| r.halted(),
Delta::ZERO,
Delta::from_secs(2),
@ -681,16 +664,17 @@ pub(crate) fn wait_till_halted(&self, bar: Bar0<'_>) -> Result<()> {
}
/// Start the falcon CPU.
pub(crate) fn start(&self, bar: Bar0<'_>) -> Result<()> {
match bar
pub(crate) fn start(&self) -> Result<()> {
match self
.bar
.read(regs::NV_PFALCON_FALCON_CPUCTL::of::<E>())
.alias_en()
{
true => bar.write(
true => self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::zeroed().with_startcpu(true),
),
false => bar.write(
false => self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_CPUCTL::zeroed().with_startcpu(true),
),
@ -700,16 +684,16 @@ pub(crate) fn start(&self, bar: Bar0<'_>) -> Result<()> {
}
/// Writes values to the mailbox registers if provided.
pub(crate) fn write_mailboxes(&self, bar: Bar0<'_>, mbox0: Option<u32>, mbox1: Option<u32>) {
pub(crate) fn write_mailboxes(&self, mbox0: Option<u32>, mbox1: Option<u32>) {
if let Some(mbox0) = mbox0 {
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_MAILBOX0::zeroed().with_value(mbox0),
);
}
if let Some(mbox1) = mbox1 {
bar.write(
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_MAILBOX1::zeroed().with_value(mbox1),
);
@ -717,21 +701,23 @@ pub(crate) fn write_mailboxes(&self, bar: Bar0<'_>, mbox0: Option<u32>, mbox1: O
}
/// Reads the value from `mbox0` register.
pub(crate) fn read_mailbox0(&self, bar: Bar0<'_>) -> u32 {
bar.read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>())
pub(crate) fn read_mailbox0(&self) -> u32 {
self.bar
.read(regs::NV_PFALCON_FALCON_MAILBOX0::of::<E>())
.value()
}
/// Reads the value from `mbox1` register.
pub(crate) fn read_mailbox1(&self, bar: Bar0<'_>) -> u32 {
bar.read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>())
pub(crate) fn read_mailbox1(&self) -> u32 {
self.bar
.read(regs::NV_PFALCON_FALCON_MAILBOX1::of::<E>())
.value()
}
/// Reads values from both mailbox registers.
pub(crate) fn read_mailboxes(&self, bar: Bar0<'_>) -> (u32, u32) {
let mbox0 = self.read_mailbox0(bar);
let mbox1 = self.read_mailbox1(bar);
pub(crate) fn read_mailboxes(&self) -> (u32, u32) {
let mbox0 = self.read_mailbox0();
let mbox1 = self.read_mailbox1();
(mbox0, mbox1)
}
@ -743,54 +729,43 @@ pub(crate) fn read_mailboxes(&self, bar: Bar0<'_>) -> (u32, u32) {
///
/// Wait up to two seconds for the firmware to complete, and return its exit status read from
/// the `MBOX0` and `MBOX1` registers.
pub(crate) fn boot(
&self,
bar: Bar0<'_>,
mbox0: Option<u32>,
mbox1: Option<u32>,
) -> Result<(u32, u32)> {
self.write_mailboxes(bar, mbox0, mbox1);
self.start(bar)?;
self.wait_till_halted(bar)?;
Ok(self.read_mailboxes(bar))
pub(crate) fn boot(&self, mbox0: Option<u32>, mbox1: Option<u32>) -> Result<(u32, u32)> {
self.write_mailboxes(mbox0, mbox1);
self.start()?;
self.wait_till_halted()?;
Ok(self.read_mailboxes())
}
/// Returns the fused version of the signature to use in order to run a HS firmware on this
/// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
pub(crate) fn signature_reg_fuse_version(
&self,
bar: Bar0<'_>,
engine_id_mask: u16,
ucode_id: u8,
) -> Result<u32> {
self.hal
.signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
.signature_reg_fuse_version(self, engine_id_mask, ucode_id)
}
/// Check if the RISC-V core is active.
///
/// Returns `true` if the RISC-V core is active, `false` otherwise.
pub(crate) fn is_riscv_active(&self, bar: Bar0<'_>) -> bool {
self.hal.is_riscv_active(bar)
pub(crate) fn is_riscv_active(&self) -> bool {
self.hal.is_riscv_active(self)
}
/// Load a firmware image into Falcon memory, using the preferred method for the current
/// chipset.
pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(
&self,
dev: &Device<device::Bound>,
bar: Bar0<'_>,
fw: &F,
) -> Result {
pub(crate) fn load<F: FalconFirmware<Target = E> + FalconDmaLoadable>(&self, fw: &F) -> Result {
match self.hal.load_method() {
LoadMethod::Dma => self.dma_load(dev, bar, fw),
LoadMethod::Pio => self.pio_load(bar, &fw.try_as_pio_loadable()?),
LoadMethod::Dma => self.dma_load(fw),
LoadMethod::Pio => self.pio_load(&fw.try_as_pio_loadable()?),
}
}
/// Write the application version to the OS register.
pub(crate) fn write_os_version(&self, bar: Bar0<'_>, app_version: u32) {
bar.write(
pub(crate) fn write_os_version(&self, app_version: u32) {
self.bar.write(
WithBase::of::<E>(),
regs::NV_PFALCON_FALCON_OS::zeroed().with_value(app_version),
);

View File

@ -21,7 +21,6 @@
};
use crate::{
driver::Bar0,
falcon::{
Falcon,
FalconEngine,
@ -48,18 +47,18 @@ impl RegisterBase<PFalcon2Base> for Fsp {
impl FalconEngine for Fsp {}
impl Falcon<Fsp> {
impl<'a> Falcon<'a, Fsp> {
/// Writes `data` to FSP external memory at offset `0`.
///
/// `data` is interpreted as little-endian 32-bit words. Returns `EINVAL`
/// if the `data` length is not 4-byte aligned.
fn write_emem(&mut self, bar: Bar0<'_>, data: &[u8]) -> Result {
fn write_emem(&mut self, data: &[u8]) -> Result {
if data.len() % 4 != 0 {
return Err(EINVAL);
}
// Begin a write burst at offset `0`, auto-incrementing on each write.
bar.write(
self.bar.write(
WithBase::of::<Fsp>(),
regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincw(true),
);
@ -68,7 +67,7 @@ fn write_emem(&mut self, bar: Bar0<'_>, data: &[u8]) -> Result {
let value = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
// Write the next 32-bit `value`; hardware advances the offset.
bar.write(
self.bar.write(
WithBase::of::<Fsp>(),
regs::NV_PFALCON_FALCON_EMEMD::zeroed().with_data(value),
);
@ -81,20 +80,23 @@ fn write_emem(&mut self, bar: Bar0<'_>, data: &[u8]) -> Result {
///
/// `data` is stored as little-endian 32-bit words. Returns `EINVAL` if
/// the `data` length is not 4-byte aligned.
fn read_emem(&mut self, bar: Bar0<'_>, data: &mut [u8]) -> Result {
fn read_emem(&mut self, data: &mut [u8]) -> Result {
if data.len() % 4 != 0 {
return Err(EINVAL);
}
// Begin a read burst at offset `0`, auto-incrementing on each read.
bar.write(
self.bar.write(
WithBase::of::<Fsp>(),
regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincr(true),
);
for chunk in data.chunks_exact_mut(4) {
// Read the next 32-bit word; hardware advances the offset.
let value = bar.read(regs::NV_PFALCON_FALCON_EMEMD::of::<Fsp>()).data();
let value = self
.bar
.read(regs::NV_PFALCON_FALCON_EMEMD::of::<Fsp>())
.data();
chunk.copy_from_slice(&value.to_le_bytes());
}
@ -107,9 +109,9 @@ fn read_emem(&mut self, bar: Bar0<'_>, data: &mut [u8]) -> Result {
///
/// The FSP message queue is not circular. Pointers are reset to 0 after each
/// message exchange, so `tail >= head` is always true when data is present.
fn poll_msgq(&self, bar: Bar0<'_>) -> u32 {
let head = bar.read(regs::NV_PFSP_MSGQ_HEAD::at(0)).val();
let tail = bar.read(regs::NV_PFSP_MSGQ_TAIL::at(0)).val();
fn poll_msgq(&self) -> u32 {
let head = self.bar.read(regs::NV_PFSP_MSGQ_HEAD::at(0)).val();
let tail = self.bar.read(regs::NV_PFSP_MSGQ_TAIL::at(0)).val();
if head == tail {
return 0;
@ -122,20 +124,20 @@ fn poll_msgq(&self, bar: Bar0<'_>) -> u32 {
/// Writes `packet` to FSP EMEM and updates the queue pointers to notify FSP.
///
/// Returns `EINVAL` if `packet` is empty or its length is not 4-byte aligned.
pub(crate) fn send_msg(&mut self, bar: Bar0<'_>, packet: &[u8]) -> Result {
pub(crate) fn send_msg(&mut self, packet: &[u8]) -> Result {
if packet.is_empty() {
return Err(EINVAL);
}
self.write_emem(bar, packet)?;
self.write_emem(packet)?;
// Update queue pointers. TAIL points at the last DWORD written.
let tail_offset = u32::try_from(packet.len() - 4).map_err(|_| EINVAL)?;
bar.write(
self.bar.write(
Array::at(0),
regs::NV_PFSP_QUEUE_TAIL::zeroed().with_address(tail_offset),
);
bar.write(
self.bar.write(
Array::at(0),
regs::NV_PFSP_QUEUE_HEAD::zeroed().with_address(0),
);
@ -148,9 +150,9 @@ pub(crate) fn send_msg(&mut self, bar: Bar0<'_>, packet: &[u8]) -> Result {
///
/// Returns `ETIMEDOUT` if no message was available until timeout, or a regular error code if a
/// memory allocation error occurred.
pub(crate) fn recv_msg(&mut self, bar: Bar0<'_>) -> Result<KVec<u8>> {
pub(crate) fn recv_msg(&mut self) -> Result<KVec<u8>> {
let msg_size = read_poll_timeout(
|| Ok(self.poll_msgq(bar)),
|| Ok(self.poll_msgq()),
|&size| size > 0,
Delta::from_millis(10),
Delta::from_millis(FSP_MSG_TIMEOUT_MS),
@ -160,11 +162,13 @@ pub(crate) fn recv_msg(&mut self, bar: Bar0<'_>) -> Result<KVec<u8>> {
let mut buffer = KVec::<u8>::new();
buffer.resize(msg_size, 0, GFP_KERNEL)?;
self.read_emem(bar, &mut buffer)?;
self.read_emem(&mut buffer)?;
// Reset message queue pointers after reading.
bar.write(Array::at(0), regs::NV_PFSP_MSGQ_TAIL::zeroed().with_val(0));
bar.write(Array::at(0), regs::NV_PFSP_MSGQ_HEAD::zeroed().with_val(0));
self.bar
.write(Array::at(0), regs::NV_PFSP_MSGQ_TAIL::zeroed().with_val(0));
self.bar
.write(Array::at(0), regs::NV_PFSP_MSGQ_HEAD::zeroed().with_val(0));
Ok(buffer)
}

View File

@ -14,7 +14,6 @@
};
use crate::{
driver::Bar0,
falcon::{
Falcon,
FalconEngine,
@ -37,20 +36,20 @@ impl RegisterBase<PFalcon2Base> for Gsp {
impl FalconEngine for Gsp {}
impl Falcon<Gsp> {
impl<'a> Falcon<'a, Gsp> {
/// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
/// allow GSP to signal CPU for processing new messages in message queue.
pub(crate) fn clear_swgen0_intr(&self, bar: Bar0<'_>) {
bar.write(
pub(crate) fn clear_swgen0_intr(&self) {
self.bar.write(
WithBase::of::<Gsp>(),
regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true),
);
}
/// Checks if GSP reload/resume has completed during the boot process.
pub(crate) fn check_reload_completed(&self, bar: Bar0<'_>, timeout: Delta) -> Result<bool> {
pub(crate) fn check_reload_completed(&self, timeout: Delta) -> Result<bool> {
read_poll_timeout(
|| Ok(bar.read(regs::NV_PGC6_BSI_SECURE_SCRATCH_14)),
|| Ok(self.bar.read(regs::NV_PGC6_BSI_SECURE_SCRATCH_14)),
|val| val.boot_stage_3_handoff(),
Delta::ZERO,
timeout,
@ -59,19 +58,21 @@ pub(crate) fn check_reload_completed(&self, bar: Bar0<'_>, timeout: Delta) -> Re
}
/// Returns whether the RISC-V branch privilege lockdown bit is set.
pub(crate) fn riscv_branch_privilege_lockdown(&self, bar: Bar0<'_>) -> bool {
bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
pub(crate) fn riscv_branch_privilege_lockdown(&self) -> bool {
self.bar
.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
.riscv_br_priv_lockdown()
}
/// Returns whether GSP registers can be read by the CPU.
pub(crate) fn priv_target_mask_released(&self, bar: Bar0<'_>) -> bool {
pub(crate) fn priv_target_mask_released(&self) -> bool {
/// Pattern returned by GSP register reads while the PRIV target mask still blocks CPU
/// access. The low byte varies; the upper 24 bits are fixed.
const LOCKED_PATTERN: u32 = 0xbadf_4100;
const LOCKED_MASK: u32 = 0xffff_ff00;
let hwcfg2 = bar
let hwcfg2 = self
.bar
.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>())
.into_raw();

View File

@ -3,7 +3,6 @@
use kernel::prelude::*;
use crate::{
driver::Bar0,
falcon::{
Falcon,
FalconBromParams,
@ -34,7 +33,7 @@ pub(crate) enum LoadMethod {
/// registers.
pub(crate) trait FalconHal<E: FalconEngine>: Send + Sync {
/// Activates the Falcon core if the engine is a risvc/falcon dual engine.
fn select_core(&self, _falcon: &Falcon<E>, _bar: Bar0<'_>) -> Result {
fn select_core(&self, _falcon: &Falcon<'_, E>) -> Result {
Ok(())
}
@ -42,24 +41,23 @@ fn select_core(&self, _falcon: &Falcon<E>, _bar: Bar0<'_>) -> Result {
/// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
fn signature_reg_fuse_version(
&self,
falcon: &Falcon<E>,
bar: Bar0<'_>,
falcon: &Falcon<'_, E>,
engine_id_mask: u16,
ucode_id: u8,
) -> Result<u32>;
/// Program the boot ROM registers prior to starting a secure firmware.
fn program_brom(&self, falcon: &Falcon<E>, bar: Bar0<'_>, params: &FalconBromParams);
fn program_brom(&self, falcon: &Falcon<'_, E>, params: &FalconBromParams);
/// Check if the RISC-V core is active.
/// Returns `true` if the RISC-V core is active, `false` otherwise.
fn is_riscv_active(&self, bar: Bar0<'_>) -> bool;
fn is_riscv_active(&self, falcon: &Falcon<'_, E>) -> bool;
/// Wait for memory scrubbing to complete.
fn reset_wait_mem_scrubbing(&self, bar: Bar0<'_>) -> Result;
fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result;
/// Reset the falcon engine.
fn reset_eng(&self, bar: Bar0<'_>) -> Result;
fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result;
/// Returns the method used to load data into the falcon's memory.
///

View File

@ -115,33 +115,34 @@ pub(super) fn new() -> Self {
}
impl<E: FalconEngine> FalconHal<E> for Ga102<E> {
fn select_core(&self, _falcon: &Falcon<E>, bar: Bar0<'_>) -> Result {
select_core_ga102::<E>(bar)
fn select_core(&self, falcon: &Falcon<'_, E>) -> Result {
select_core_ga102::<E>(falcon.bar)
}
fn signature_reg_fuse_version(
&self,
falcon: &Falcon<E>,
bar: Bar0<'_>,
falcon: &Falcon<'_, E>,
engine_id_mask: u16,
ucode_id: u8,
) -> Result<u32> {
signature_reg_fuse_version_ga102(&falcon.dev, bar, engine_id_mask, ucode_id)
signature_reg_fuse_version_ga102(falcon.dev, falcon.bar, engine_id_mask, ucode_id)
}
fn program_brom(&self, _falcon: &Falcon<E>, bar: Bar0<'_>, params: &FalconBromParams) {
program_brom_ga102::<E>(bar, params);
fn program_brom(&self, falcon: &Falcon<'_, E>, params: &FalconBromParams) {
program_brom_ga102::<E>(falcon.bar, params);
}
fn is_riscv_active(&self, bar: Bar0<'_>) -> bool {
bar.read(regs::NV_PRISCV_RISCV_CPUCTL::of::<E>())
fn is_riscv_active(&self, falcon: &Falcon<'_, E>) -> bool {
falcon
.bar
.read(regs::NV_PRISCV_RISCV_CPUCTL::of::<E>())
.active_stat()
}
fn reset_wait_mem_scrubbing(&self, bar: Bar0<'_>) -> Result {
fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 20ms.
read_poll_timeout(
|| Ok(bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>())),
|| Ok(falcon.bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>())),
|r| r.mem_scrubbing_done(),
Delta::ZERO,
Delta::from_millis(20),
@ -149,7 +150,9 @@ fn reset_wait_mem_scrubbing(&self, bar: Bar0<'_>) -> Result {
.map(|_| ())
}
fn reset_eng(&self, bar: Bar0<'_>) -> Result {
fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result {
let bar = falcon.bar;
let _ = bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<E>());
// According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
@ -162,7 +165,7 @@ fn reset_eng(&self, bar: Bar0<'_>) -> Result {
);
regs::NV_PFALCON_FALCON_ENGINE::reset_engine::<E>(bar);
self.reset_wait_mem_scrubbing(bar)?;
self.reset_wait_mem_scrubbing(falcon)?;
Ok(())
}

View File

@ -13,7 +13,6 @@
};
use crate::{
driver::Bar0,
falcon::{
hal::LoadMethod,
Falcon,
@ -34,31 +33,32 @@ pub(super) fn new() -> Self {
}
impl<E: FalconEngine> FalconHal<E> for Tu102<E> {
fn select_core(&self, _falcon: &Falcon<E>, _bar: Bar0<'_>) -> Result {
fn select_core(&self, _falcon: &Falcon<'_, E>) -> Result {
Ok(())
}
fn signature_reg_fuse_version(
&self,
_falcon: &Falcon<E>,
_bar: Bar0<'_>,
_falcon: &Falcon<'_, E>,
_engine_id_mask: u16,
_ucode_id: u8,
) -> Result<u32> {
Ok(0)
}
fn program_brom(&self, _falcon: &Falcon<E>, _bar: Bar0<'_>, _params: &FalconBromParams) {}
fn program_brom(&self, _falcon: &Falcon<'_, E>, _params: &FalconBromParams) {}
fn is_riscv_active(&self, bar: Bar0<'_>) -> bool {
bar.read(regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::of::<E>())
fn is_riscv_active(&self, falcon: &Falcon<'_, E>) -> bool {
falcon
.bar
.read(regs::NV_PRISCV_RISCV_CORE_SWITCH_RISCV_STATUS::of::<E>())
.active_stat()
}
fn reset_wait_mem_scrubbing(&self, bar: Bar0<'_>) -> Result {
fn reset_wait_mem_scrubbing(&self, falcon: &Falcon<'_, E>) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 10ms.
read_poll_timeout(
|| Ok(bar.read(regs::NV_PFALCON_FALCON_DMACTL::of::<E>())),
|| Ok(falcon.bar.read(regs::NV_PFALCON_FALCON_DMACTL::of::<E>())),
|r| r.mem_scrubbing_done(),
Delta::ZERO,
Delta::from_millis(10),
@ -66,9 +66,9 @@ fn reset_wait_mem_scrubbing(&self, bar: Bar0<'_>) -> Result {
.map(|_| ())
}
fn reset_eng(&self, bar: Bar0<'_>) -> Result {
regs::NV_PFALCON_FALCON_ENGINE::reset_engine::<E>(bar);
self.reset_wait_mem_scrubbing(bar)?;
fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result {
regs::NV_PFALCON_FALCON_ENGINE::reset_engine::<E>(falcon.bar);
self.reset_wait_mem_scrubbing(falcon)?;
Ok(())
}

View File

@ -15,7 +15,6 @@
};
use crate::{
driver::Bar0,
falcon::{
sec2::Sec2,
Falcon,
@ -293,8 +292,7 @@ pub(crate) fn new(
kind: BooterKind,
chipset: Chipset,
ver: &str,
falcon: &Falcon<<Self as FalconFirmware>::Target>,
bar: Bar0<'_>,
falcon: &Falcon<'_, <Self as FalconFirmware>::Target>,
) -> Result<Self> {
let fw_name = match kind {
BooterKind::Loader => "booter_load",
@ -339,11 +337,8 @@ pub(crate) fn new(
} else {
// Obtain the version from the fuse register, and extract the corresponding
// signature.
let reg_fuse_version = falcon.signature_reg_fuse_version(
bar,
brom_params.engine_id_mask,
brom_params.ucode_id,
)?;
let reg_fuse_version = falcon
.signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)?;
// `0` means the last signature should be used.
const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
@ -405,18 +400,14 @@ pub(crate) fn new(
pub(crate) fn run<T>(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
sec2_falcon: &Falcon<Sec2>,
sec2_falcon: &Falcon<'_, Sec2>,
wpr_meta: &Coherent<T>,
) -> Result {
sec2_falcon.reset(bar)?;
sec2_falcon.load(dev, bar, self)?;
sec2_falcon.reset()?;
sec2_falcon.load(self)?;
let wpr_handle = wpr_meta.dma_handle();
let (mbox0, mbox1) = sec2_falcon.boot(
bar,
Some(wpr_handle as u32),
Some((wpr_handle >> 32) as u32),
)?;
let (mbox0, mbox1) =
sec2_falcon.boot(Some(wpr_handle as u32), Some((wpr_handle >> 32) as u32))?;
dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
if mbox0 != 0 {

View File

@ -27,7 +27,6 @@
};
use crate::{
driver::Bar0,
falcon::{
gsp::Gsp,
Falcon,
@ -320,8 +319,7 @@ impl FwsecFirmware {
/// command.
pub(crate) fn new(
dev: &Device<device::Bound>,
falcon: &Falcon<Gsp>,
bar: Bar0<'_>,
falcon: &Falcon<'_, Gsp>,
bios: &Vbios,
cmd: FwsecCommand,
) -> Result<Self> {
@ -337,7 +335,7 @@ pub(crate) fn new(
.ok_or(EINVAL)?;
let desc_sig_versions = u32::from(desc.signature_versions());
let reg_fuse_version =
falcon.signature_reg_fuse_version(bar, desc.engine_id_mask(), desc.ucode_id())?;
falcon.signature_reg_fuse_version(desc.engine_id_mask(), desc.ucode_id())?;
dev_dbg!(
dev,
"desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
@ -390,21 +388,16 @@ pub(crate) fn new(
/// This must only be called on chipsets that do not need the FWSEC bootloader (i.e., where
/// [`Chipset::needs_fwsec_bootloader()`](crate::gpu::Chipset::needs_fwsec_bootloader) returns
/// `false`). On chipsets that do, use [`bootloader::FwsecFirmwareWithBl`] instead.
pub(crate) fn run(
&self,
dev: &Device<device::Bound>,
falcon: &Falcon<Gsp>,
bar: Bar0<'_>,
) -> Result<()> {
pub(crate) fn run(&self, dev: &Device<device::Bound>, falcon: &Falcon<'_, Gsp>) -> Result<()> {
// Reset falcon, load the firmware, and run it.
falcon
.reset(bar)
.reset()
.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
falcon
.load(dev, bar, self)
.load(self)
.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
let (mbox0, _) = falcon
.boot(bar, Some(0), None)
.boot(Some(0), None)
.inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;
if mbox0 != 0 {
dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);

View File

@ -12,10 +12,7 @@
Device, //
},
dma::Coherent,
io::{
register::WithBase, //
Io,
},
io::{register::WithBase, Io},
prelude::*,
ptr::{
Alignable,
@ -50,7 +47,7 @@
FIRMWARE_VERSION, //
},
gpu::Chipset,
num::FromSafeCast,
num::FromSafeCast, //
regs,
};
@ -278,15 +275,15 @@ pub(crate) fn new(
pub(crate) fn run(
&self,
dev: &Device<device::Bound>,
falcon: &Falcon<Gsp>,
falcon: &Falcon<'_, Gsp>,
bar: Bar0<'_>,
) -> Result<()> {
// Reset falcon, load the firmware, and run it.
falcon
.reset(bar)
.reset()
.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
falcon
.pio_load(bar, self)
.pio_load(self)
.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
// Configure DMA index for the bootloader to fetch the FWSEC firmware from system memory.
@ -301,7 +298,7 @@ pub(crate) fn run(
);
let (mbox0, _) = falcon
.boot(bar, Some(0), None)
.boot(Some(0), None)
.inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;
if mbox0 != 0 {
dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);

View File

@ -224,27 +224,27 @@ pub(crate) fn boot_params_dma_handle(&self) -> u64 {
/// 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,
) -> 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(
@ -262,13 +262,13 @@ pub(crate) fn wait_secure_boot(
/// Sends a message to FSP and waits for the response.
/// Returns the full response buffer on success.
fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: Bar0<'_>, msg: &M) -> Result<KVec<u8>>
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);
})?;
@ -330,7 +330,6 @@ fn send_sync_fsp<M>(&mut self, dev: &device::Device, bar: Bar0<'_>, msg: &M) ->
pub(crate) fn boot_fmc(
&mut self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
fb_layout: &FbLayout,
args: &FmcBootArgs,
) -> Result {
@ -341,7 +340,7 @@ pub(crate) fn boot_fmc(
GFP_KERNEL,
)?;
let _response_buf = 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(())

View File

@ -273,9 +273,9 @@ struct GspResources<'gpu> {
/// MMIO mapping of PCI BAR 0.
bar: Bar0<'gpu>,
/// GSP falcon instance, used for GSP boot up and cleanup.
gsp_falcon: Falcon<GspFalcon>,
gsp_falcon: Falcon<'gpu, GspFalcon>,
/// SEC2 falcon instance, used for GSP boot up and cleanup.
sec2_falcon: Falcon<Sec2Falcon>,
sec2_falcon: Falcon<'gpu, Sec2Falcon>,
/// GSP runtime data.
#[pin]
gsp: Gsp,
@ -351,10 +351,11 @@ pub(crate) fn new(
gsp_falcon: Falcon::new(
pdev.as_ref(),
spec.chipset,
bar
)
.inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
.inspect(|falcon| falcon.clear_swgen0_intr())?,
sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar)?,
gsp <- Gsp::new(pdev),

View File

@ -57,8 +57,8 @@ pub(crate) struct GspBootContext<'a> {
pub(crate) pdev: &'a pci::Device<device::Bound>,
pub(crate) bar: Bar0<'a>,
pub(crate) chipset: Chipset,
pub(crate) gsp_falcon: &'a Falcon<GspFalcon>,
pub(crate) sec2_falcon: &'a Falcon<Sec2Falcon>,
pub(crate) gsp_falcon: &'a Falcon<'a, GspFalcon>,
pub(crate) sec2_falcon: &'a Falcon<'a, Sec2Falcon>,
}
impl<'a> GspBootContext<'a> {

View File

@ -37,8 +37,8 @@ pub(super) struct BootUnloadArgs<'a> {
gsp: &'a super::Gsp,
dev: &'a device::Device<device::Bound>,
bar: Bar0<'a>,
gsp_falcon: &'a Falcon<Gsp>,
sec2_falcon: &'a Falcon<Sec2>,
gsp_falcon: &'a Falcon<'a, Gsp>,
sec2_falcon: &'a Falcon<'a, Sec2>,
unload_bundle: Option<super::UnloadBundle>,
}
@ -56,8 +56,8 @@ pub(super) fn new(
gsp: &'a super::Gsp,
dev: &'a device::Device<device::Bound>,
bar: Bar0<'a>,
gsp_falcon: &'a Falcon<Gsp>,
sec2_falcon: &'a Falcon<Sec2>,
gsp_falcon: &'a Falcon<'a, Gsp>,
sec2_falcon: &'a Falcon<'a, Sec2>,
unload_bundle: Option<super::UnloadBundle>,
) -> Self {
Self {
@ -120,17 +120,17 @@ pub(crate) fn boot(
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
let unload_guard = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?;
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
gsp_falcon.write_os_version(gsp_fw.bootloader.app_version);
// Poll for RISC-V to become active before continuing.
read_poll_timeout(
|| Ok(gsp_falcon.is_riscv_active(bar)),
|| Ok(gsp_falcon.is_riscv_active()),
|val: &bool| *val,
Delta::from_millis(10),
Delta::from_secs(5),
)?;
dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),);
dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(),);
self.cmdq
.send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
@ -149,7 +149,7 @@ pub(crate) fn boot(
fn shutdown_gsp(
cmdq: &Cmdq,
bar: Bar0<'_>,
gsp_falcon: &Falcon<Gsp>,
gsp_falcon: &Falcon<'_, Gsp>,
mode: commands::PowerStateLevel,
) -> Result {
// Command to shut the GSP down.
@ -158,7 +158,7 @@ fn shutdown_gsp(
// Wait until GSP signals it is suspended.
const LIBOS_INTERRUPT_PROCESSOR_SUSPENDED: u32 = bits::bit_u32(31);
read_poll_timeout(
|| Ok(gsp_falcon.read_mailbox0(bar)),
|| Ok(gsp_falcon.read_mailbox0()),
|&mb0| mb0 & LIBOS_INTERRUPT_PROCESSOR_SUSPENDED != 0,
Delta::from_millis(10),
Delta::from_secs(5),
@ -173,8 +173,8 @@ pub(crate) fn unload(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<Gsp>,
sec2_falcon: &Falcon<Sec2>,
gsp_falcon: &Falcon<'_, Gsp>,
sec2_falcon: &Falcon<'_, Sec2>,
unload_bundle: Option<super::UnloadBundle>,
) -> Result {
// Shut down the GSP. Keep going even in case of error.

View File

@ -42,8 +42,8 @@ fn run(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
sec2_falcon: &Falcon<Sec2>,
gsp_falcon: &Falcon<'_, GspEngine>,
sec2_falcon: &Falcon<'_, Sec2>,
) -> Result;
}

View File

@ -42,10 +42,10 @@ struct GspMbox {
impl GspMbox {
/// Reads both mailboxes from the GSP falcon.
fn read(gsp_falcon: &Falcon<GspEngine>, bar: Bar0<'_>) -> Self {
fn read(gsp_falcon: &Falcon<'_, GspEngine>) -> Self {
Self {
mbox0: gsp_falcon.read_mailbox0(bar),
mbox1: gsp_falcon.read_mailbox1(bar),
mbox0: gsp_falcon.read_mailbox0(),
mbox1: gsp_falcon.read_mailbox1(),
}
}
@ -60,8 +60,7 @@ fn combined_addr(&self) -> u64 {
/// either condition should stop the poll loop.
fn lockdown_released_or_error(
&self,
gsp_falcon: &Falcon<GspEngine>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<'_, GspEngine>,
fmc_boot_params_addr: u64,
) -> bool {
// GSP-FMC normally clears the boot parameters address from the mailboxes early during
@ -71,15 +70,14 @@ fn lockdown_released_or_error(
return self.combined_addr() != fmc_boot_params_addr;
}
!gsp_falcon.riscv_branch_privilege_lockdown(bar)
!gsp_falcon.riscv_branch_privilege_lockdown()
}
}
/// Waits for GSP lockdown to be released after FSP Chain of Trust.
fn wait_for_gsp_lockdown_release(
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
gsp_falcon: &Falcon<'_, GspEngine>,
fmc_boot_params_addr: u64,
) -> Result {
dev_dbg!(dev, "Waiting for GSP lockdown release\n");
@ -88,14 +86,14 @@ fn wait_for_gsp_lockdown_release(
|| {
// While the PRIV target mask is still locked to FSP, GSP register and mailbox reads
// are not meaningful. Wait until HWCFG2 says the CPU can read them.
Ok(match gsp_falcon.priv_target_mask_released(bar) {
Ok(match gsp_falcon.priv_target_mask_released() {
false => None,
true => Some(GspMbox::read(gsp_falcon, bar)),
true => Some(GspMbox::read(gsp_falcon)),
})
},
|mbox| match mbox {
None => false,
Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, bar, fmc_boot_params_addr),
Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, fmc_boot_params_addr),
},
Delta::from_millis(10),
Delta::from_secs(30),
@ -122,13 +120,13 @@ impl UnloadBundle for FspUnloadBundle {
fn run(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
_sec2_falcon: &Falcon<Sec2>,
_bar: Bar0<'_>,
gsp_falcon: &Falcon<'_, GspEngine>,
_sec2_falcon: &Falcon<'_, Sec2>,
) -> Result {
// GSP falcon does most of the work of resetting, so just wait for it to finish.
read_poll_timeout(
|| Ok(gsp_falcon.is_riscv_active(bar)),
|| Ok(gsp_falcon.is_riscv_active()),
|&active| !active,
Delta::from_millis(10),
Delta::from_secs(5),
@ -176,9 +174,9 @@ fn boot<'a>(
false,
)?;
fsp.boot_fmc(dev, bar, fb_layout, &args)?;
fsp.boot_fmc(dev, fb_layout, &args)?;
wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params_dma_handle())?;
wait_for_gsp_lockdown_release(dev, gsp_falcon, args.boot_params_dma_handle())?;
Ok(unload_guard)
}

View File

@ -62,12 +62,11 @@ impl FwsecUnloadFirmware {
/// Loads the FWSEC SB firmware, as well as its bootloader if `chipset` requires it.
fn new(
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
chipset: Chipset,
bios: &Vbios,
gsp_falcon: &Falcon<GspEngine>,
gsp_falcon: &Falcon<'_, GspEngine>,
) -> Result<Self> {
let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bar, bios, FwsecCommand::Sb)?;
let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bios, FwsecCommand::Sb)?;
Ok(if chipset.needs_fwsec_bootloader() {
Self::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
@ -81,10 +80,10 @@ fn run(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
gsp_falcon: &Falcon<'_, GspEngine>,
) -> Result {
match self {
Self::WithoutBl(fw) => fw.run(dev, gsp_falcon, bar),
Self::WithoutBl(fw) => fw.run(dev, gsp_falcon),
Self::WithBl(fw) => fw.run(dev, gsp_falcon, bar),
}
}
@ -101,22 +100,20 @@ impl Sec2UnloadBundle {
/// Load and prepare the resources required to properly reset the GSP after it has been stopped.
fn build(
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
chipset: Chipset,
bios: &Vbios,
gsp_falcon: &Falcon<GspEngine>,
sec2_falcon: &Falcon<Sec2>,
gsp_falcon: &Falcon<'_, GspEngine>,
sec2_falcon: &Falcon<'_, Sec2>,
) -> Result<KBox<dyn UnloadBundle>> {
KBox::new(
Self {
fwsec_sb: FwsecUnloadFirmware::new(dev, bar, chipset, bios, gsp_falcon)?,
fwsec_sb: FwsecUnloadFirmware::new(dev, chipset, bios, gsp_falcon)?,
booter_unloader: BooterFirmware::new(
dev,
BooterKind::Unloader,
chipset,
FIRMWARE_VERSION,
sec2_falcon,
bar,
)?,
},
GFP_KERNEL,
@ -131,8 +128,8 @@ fn run(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
sec2_falcon: &Falcon<Sec2>,
gsp_falcon: &Falcon<'_, GspEngine>,
sec2_falcon: &Falcon<'_, Sec2>,
) -> Result {
// Run FWSEC-SB to reset the GSP falcon to its pre-libos state.
// Log errors but keep going if it fails.
@ -148,13 +145,12 @@ fn run(
return Ok(());
}
sec2_falcon.reset(bar)?;
sec2_falcon.load(dev, bar, &self.booter_unloader)?;
sec2_falcon.reset()?;
sec2_falcon.load(&self.booter_unloader)?;
// Sentinel value to confirm that Booter Unloader has run.
const MAILBOX_SENTINEL: u32 = 0xff;
let (mbox0, _) =
sec2_falcon.boot(bar, Some(MAILBOX_SENTINEL), Some(MAILBOX_SENTINEL))?;
let (mbox0, _) = sec2_falcon.boot(Some(MAILBOX_SENTINEL), Some(MAILBOX_SENTINEL))?;
if mbox0 != 0 {
dev_err!(dev, "Booter Unloader returned error 0x{:x}\n", mbox0);
return Err(EINVAL);
@ -183,7 +179,7 @@ fn run(
fn run_fwsec_frts(
dev: &device::Device<device::Bound>,
chipset: Chipset,
falcon: &Falcon<GspEngine>,
falcon: &Falcon<'_, GspEngine>,
bar: Bar0<'_>,
bios: &Vbios,
fb_layout: &FbLayout,
@ -202,7 +198,6 @@ fn run_fwsec_frts(
let fwsec_frts = FwsecFirmware::new(
dev,
falcon,
bar,
bios,
FwsecCommand::Frts {
frts_addr: fb_layout.frts.start,
@ -216,7 +211,7 @@ fn run_fwsec_frts(
fwsec_frts_bl.run(dev, falcon, bar)?;
} else {
// Load and run FWSEC-FRTS directly.
fwsec_frts.run(dev, falcon, bar)?;
fwsec_frts.run(dev, falcon)?;
}
// SCRATCH_E contains the error code for FWSEC-FRTS.
@ -286,18 +281,17 @@ fn boot<'a>(
//
// If the unload bundle creation fails, the GPU will need to be reset before the driver can
// be probed again.
let unload_bundle =
Sec2UnloadBundle::build(dev, bar, chipset, &bios, gsp_falcon, sec2_falcon)
.inspect_err(|e| {
dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e);
dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
dev_warn!(
dev,
"The GPU will need to be reset before the driver can bind again.\n"
);
})
.ok()
.map(crate::gsp::UnloadBundle);
let unload_bundle = Sec2UnloadBundle::build(dev, chipset, &bios, gsp_falcon, sec2_falcon)
.inspect_err(|e| {
dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e);
dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
dev_warn!(
dev,
"The GPU will need to be reset before the driver can bind again.\n"
);
})
.ok()
.map(crate::gsp::UnloadBundle);
// Wrap the unload bundle into a drop guard so it is automatically run upon failure.
let unload_guard =
@ -308,13 +302,10 @@ fn boot<'a>(
run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?;
}
gsp_falcon.reset(bar)?;
gsp_falcon.reset()?;
let libos_handle = gsp.libos.dma_handle();
let (mbox0, mbox1) = gsp_falcon.boot(
bar,
Some(libos_handle as u32),
Some((libos_handle >> 32) as u32),
)?;
let (mbox0, mbox1) =
gsp_falcon.boot(Some(libos_handle as u32), Some((libos_handle >> 32) as u32))?;
dev_dbg!(dev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
dev_dbg!(
@ -328,9 +319,8 @@ fn boot<'a>(
chipset,
FIRMWARE_VERSION,
sec2_falcon,
bar,
)?
.run(dev, bar, sec2_falcon, wpr_meta)?;
.run(dev, sec2_falcon, wpr_meta)?;
Ok(unload_guard)
}

View File

@ -133,9 +133,9 @@ pub(crate) struct GspSequencer<'a> {
/// `Bar0` for register access.
bar: Bar0<'a>,
/// SEC2 falcon for core operations.
sec2_falcon: &'a Falcon<Sec2>,
sec2_falcon: &'a Falcon<'a, Sec2>,
/// GSP falcon for core operations.
gsp_falcon: &'a Falcon<Gsp>,
gsp_falcon: &'a Falcon<'a, Gsp>,
/// LibOS DMA handle address.
libos_dma_handle: u64,
/// Bootloader application version.
@ -213,16 +213,16 @@ fn run(&self, seq: &GspSequencer<'_>) -> Result {
GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
GspSeqCmd::RegStore(cmd) => cmd.run(seq),
GspSeqCmd::CoreReset => {
seq.gsp_falcon.reset(seq.bar)?;
seq.gsp_falcon.dma_reset(seq.bar);
seq.gsp_falcon.reset()?;
seq.gsp_falcon.dma_reset();
Ok(())
}
GspSeqCmd::CoreStart => {
seq.gsp_falcon.start(seq.bar)?;
seq.gsp_falcon.start()?;
Ok(())
}
GspSeqCmd::CoreWaitForHalt => {
seq.gsp_falcon.wait_till_halted(seq.bar)?;
seq.gsp_falcon.wait_till_halted()?;
Ok(())
}
GspSeqCmd::CoreResume => {
@ -231,35 +231,32 @@ fn run(&self, seq: &GspSequencer<'_>) -> Result {
// sequencer will start both.
// Reset the GSP to prepare it for resuming.
seq.gsp_falcon.reset(seq.bar)?;
seq.gsp_falcon.reset()?;
// Write the libOS DMA handle to GSP mailboxes.
seq.gsp_falcon.write_mailboxes(
seq.bar,
Some(seq.libos_dma_handle as u32),
Some((seq.libos_dma_handle >> 32) as u32),
);
// Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP.
seq.sec2_falcon.start(seq.bar)?;
seq.sec2_falcon.start()?;
// Poll until GSP-RM reload/resume has completed (up to 2 seconds).
seq.gsp_falcon
.check_reload_completed(seq.bar, Delta::from_secs(2))?;
seq.gsp_falcon.check_reload_completed(Delta::from_secs(2))?;
// Verify SEC2 completed successfully by checking its mailbox for errors.
let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar);
let mbox0 = seq.sec2_falcon.read_mailbox0();
if mbox0 != 0 {
dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0);
return Err(EIO);
}
// Configure GSP with the bootloader version.
seq.gsp_falcon
.write_os_version(seq.bar, seq.bootloader_app_version);
seq.gsp_falcon.write_os_version(seq.bootloader_app_version);
// Verify the GSP's RISC-V core is active indicating successful GSP boot.
if !seq.gsp_falcon.is_riscv_active(seq.bar) {
if !seq.gsp_falcon.is_riscv_active() {
dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n");
return Err(EIO);
}
@ -345,9 +342,9 @@ pub(crate) struct GspSequencerParams<'a> {
/// LibOS DMA handle address.
pub(crate) libos_dma_handle: u64,
/// GSP falcon for core operations.
pub(crate) gsp_falcon: &'a Falcon<Gsp>,
pub(crate) gsp_falcon: &'a Falcon<'a, Gsp>,
/// SEC2 falcon for core operations.
pub(crate) sec2_falcon: &'a Falcon<Sec2>,
pub(crate) sec2_falcon: &'a Falcon<'a, Sec2>,
/// Device for logging.
pub(crate) dev: &'a device::Device,
/// BAR0 for register access.