|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! GSP Sequencer implementation for Pre-hopper GSP boot sequence. |
| 4 | +
|
| 5 | +use core::{ |
| 6 | + array, |
| 7 | + mem::size_of, // |
| 8 | +}; |
| 9 | + |
| 10 | +use kernel::{ |
| 11 | + device, |
| 12 | + prelude::*, |
| 13 | + time::Delta, |
| 14 | + transmute::FromBytes, |
| 15 | + types::ARef, // |
| 16 | +}; |
| 17 | + |
| 18 | +use crate::{ |
| 19 | + driver::Bar0, |
| 20 | + falcon::{ |
| 21 | + gsp::Gsp, |
| 22 | + sec2::Sec2, |
| 23 | + Falcon, // |
| 24 | + }, |
| 25 | + gsp::{ |
| 26 | + cmdq::{ |
| 27 | + Cmdq, |
| 28 | + MessageFromGsp, // |
| 29 | + }, |
| 30 | + fw, |
| 31 | + }, |
| 32 | + sbuffer::SBufferIter, |
| 33 | +}; |
| 34 | + |
| 35 | +/// GSP Sequencer information containing the command sequence and data. |
| 36 | +struct GspSequence { |
| 37 | + /// Current command index for error reporting. |
| 38 | + cmd_index: u32, |
| 39 | + /// Command data buffer containing the sequence of commands. |
| 40 | + cmd_data: KVec<u8>, |
| 41 | +} |
| 42 | + |
| 43 | +impl MessageFromGsp for GspSequence { |
| 44 | + const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer; |
| 45 | + type InitError = Error; |
| 46 | + type Message = fw::RunCpuSequencer; |
| 47 | + |
| 48 | + fn read( |
| 49 | + msg: &Self::Message, |
| 50 | + sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, |
| 51 | + ) -> Result<Self, Self::InitError> { |
| 52 | + let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?; |
| 53 | + Ok(GspSequence { |
| 54 | + cmd_index: msg.cmd_index(), |
| 55 | + cmd_data, |
| 56 | + }) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>(); |
| 61 | + |
| 62 | +/// GSP Sequencer Command types with payload data. |
| 63 | +/// Commands have an opcode and an opcode-dependent struct. |
| 64 | +#[allow(dead_code)] |
| 65 | +pub(crate) enum GspSeqCmd {} |
| 66 | + |
| 67 | +impl GspSeqCmd { |
| 68 | + /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes. |
| 69 | + pub(crate) fn new(data: &[u8], _dev: &device::Device) -> Result<(Self, usize)> { |
| 70 | + let _fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?; |
| 71 | + let _opcode_size = core::mem::size_of::<u32>(); |
| 72 | + |
| 73 | + // NOTE: At this commit, NO opcodes exist yet, so just return error. |
| 74 | + // Later commits will add match arms here. |
| 75 | + Err(EINVAL) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +/// GSP Sequencer for executing firmware commands during boot. |
| 80 | +#[expect(dead_code)] |
| 81 | +pub(crate) struct GspSequencer<'a> { |
| 82 | + /// Sequencer information with command data. |
| 83 | + seq_info: GspSequence, |
| 84 | + /// `Bar0` for register access. |
| 85 | + bar: &'a Bar0, |
| 86 | + /// SEC2 falcon for core operations. |
| 87 | + sec2_falcon: &'a Falcon<Sec2>, |
| 88 | + /// GSP falcon for core operations. |
| 89 | + gsp_falcon: &'a Falcon<Gsp>, |
| 90 | + /// LibOS DMA handle address. |
| 91 | + libos_dma_handle: u64, |
| 92 | + /// Bootloader application version. |
| 93 | + bootloader_app_version: u32, |
| 94 | + /// Device for logging. |
| 95 | + dev: ARef<device::Device>, |
| 96 | +} |
| 97 | + |
| 98 | +/// Trait for running sequencer commands. |
| 99 | +pub(crate) trait GspSeqCmdRunner { |
| 100 | + fn run(&self, sequencer: &GspSequencer<'_>) -> Result; |
| 101 | +} |
| 102 | + |
| 103 | +impl GspSeqCmdRunner for GspSeqCmd { |
| 104 | + fn run(&self, _seq: &GspSequencer<'_>) -> Result { |
| 105 | + Ok(()) |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +/// Iterator over GSP sequencer commands. |
| 110 | +pub(crate) struct GspSeqIter<'a> { |
| 111 | + /// Command data buffer. |
| 112 | + cmd_data: &'a [u8], |
| 113 | + /// Current position in the buffer. |
| 114 | + current_offset: usize, |
| 115 | + /// Total number of commands to process. |
| 116 | + total_cmds: u32, |
| 117 | + /// Number of commands processed so far. |
| 118 | + cmds_processed: u32, |
| 119 | + /// Device for logging. |
| 120 | + dev: ARef<device::Device>, |
| 121 | +} |
| 122 | + |
| 123 | +impl<'a> Iterator for GspSeqIter<'a> { |
| 124 | + type Item = Result<GspSeqCmd>; |
| 125 | + |
| 126 | + fn next(&mut self) -> Option<Self::Item> { |
| 127 | + // Stop if we've processed all commands or reached the end of data. |
| 128 | + if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() { |
| 129 | + return None; |
| 130 | + } |
| 131 | + |
| 132 | + // Check if we have enough data for opcode. |
| 133 | + if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() { |
| 134 | + return Some(Err(EIO)); |
| 135 | + } |
| 136 | + |
| 137 | + let offset = self.current_offset; |
| 138 | + |
| 139 | + // Handle command creation based on available data, |
| 140 | + // zero-pad if necessary (since last command may not be full size). |
| 141 | + let mut buffer = [0u8; CMD_SIZE]; |
| 142 | + let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() { |
| 143 | + CMD_SIZE |
| 144 | + } else { |
| 145 | + self.cmd_data.len() - offset |
| 146 | + }; |
| 147 | + buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]); |
| 148 | + let cmd_result = GspSeqCmd::new(&buffer, &self.dev); |
| 149 | + |
| 150 | + cmd_result.map_or_else( |
| 151 | + |_err| { |
| 152 | + dev_err!(self.dev, "Error parsing command at offset {}", offset); |
| 153 | + None |
| 154 | + }, |
| 155 | + |(cmd, size)| { |
| 156 | + self.current_offset += size; |
| 157 | + self.cmds_processed += 1; |
| 158 | + Some(Ok(cmd)) |
| 159 | + }, |
| 160 | + ) |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +impl<'a> GspSequencer<'a> { |
| 165 | + fn iter(&self) -> GspSeqIter<'_> { |
| 166 | + let cmd_data = &self.seq_info.cmd_data[..]; |
| 167 | + |
| 168 | + GspSeqIter { |
| 169 | + cmd_data, |
| 170 | + current_offset: 0, |
| 171 | + total_cmds: self.seq_info.cmd_index, |
| 172 | + cmds_processed: 0, |
| 173 | + dev: self.dev.clone(), |
| 174 | + } |
| 175 | + } |
| 176 | +} |
| 177 | + |
| 178 | +/// Parameters for running the GSP sequencer. |
| 179 | +pub(crate) struct GspSequencerParams<'a> { |
| 180 | + /// Bootloader application version. |
| 181 | + pub(crate) bootloader_app_version: u32, |
| 182 | + /// LibOS DMA handle address. |
| 183 | + pub(crate) libos_dma_handle: u64, |
| 184 | + /// GSP falcon for core operations. |
| 185 | + pub(crate) gsp_falcon: &'a Falcon<Gsp>, |
| 186 | + /// SEC2 falcon for core operations. |
| 187 | + pub(crate) sec2_falcon: &'a Falcon<Sec2>, |
| 188 | + /// Device for logging. |
| 189 | + pub(crate) dev: ARef<device::Device>, |
| 190 | + /// BAR0 for register access. |
| 191 | + pub(crate) bar: &'a Bar0, |
| 192 | +} |
| 193 | + |
| 194 | +impl<'a> GspSequencer<'a> { |
| 195 | + pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { |
| 196 | + let seq_info = loop { |
| 197 | + match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) { |
| 198 | + Ok(seq_info) => break seq_info, |
| 199 | + Err(ERANGE) => continue, |
| 200 | + Err(e) => return Err(e), |
| 201 | + } |
| 202 | + }; |
| 203 | + |
| 204 | + let sequencer = GspSequencer { |
| 205 | + seq_info, |
| 206 | + bar: params.bar, |
| 207 | + sec2_falcon: params.sec2_falcon, |
| 208 | + gsp_falcon: params.gsp_falcon, |
| 209 | + libos_dma_handle: params.libos_dma_handle, |
| 210 | + bootloader_app_version: params.bootloader_app_version, |
| 211 | + dev: params.dev, |
| 212 | + }; |
| 213 | + |
| 214 | + dev_dbg!(sequencer.dev, "Running CPU Sequencer commands"); |
| 215 | + |
| 216 | + for cmd_result in sequencer.iter() { |
| 217 | + match cmd_result { |
| 218 | + Ok(cmd) => cmd.run(&sequencer)?, |
| 219 | + Err(e) => { |
| 220 | + dev_err!( |
| 221 | + sequencer.dev, |
| 222 | + "Error running command at index {}", |
| 223 | + sequencer.seq_info.cmd_index |
| 224 | + ); |
| 225 | + return Err(e); |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + dev_dbg!( |
| 231 | + sequencer.dev, |
| 232 | + "CPU Sequencer commands completed successfully" |
| 233 | + ); |
| 234 | + Ok(()) |
| 235 | + } |
| 236 | +} |
0 commit comments