opentitanlib/transport/hyperdebug/
spi.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Result, bail, ensure};
6use rusb::{Direction, Recipient, RequestType};
7use std::cell::Cell;
8use std::mem::size_of;
9use std::rc::Rc;
10use std::time::Duration;
11use zerocopy::{FromBytes, Immutable, IntoBytes};
12
13use crate::io::eeprom;
14use crate::io::gpio::GpioPin;
15use crate::io::spi::{
16    AssertChipSelect, MaxSizes, SpiError, Target, TargetChipDeassert, Transfer, TransferMode,
17};
18use crate::transport::TransportError;
19use crate::transport::hyperdebug::{BulkInterface, Inner};
20
21pub struct HyperdebugSpiTarget {
22    inner: Rc<Inner>,
23    interface: BulkInterface,
24    target_enable_cmd: u8,
25    target_idx: u8,
26    feature_bitmap: u16,
27    supports_tpm_poll: bool,
28    max_sizes: MaxSizes,
29    cs_asserted_count: Cell<u32>,
30}
31
32/// HyperDebug will wait up to one second for EEPROM chip to report "ready" after a write
33/// operation, sending a response if it gives up.  Make sure that we leave a little time for USB
34/// latency, so that we will receive the "giving up" response, rather than timeout ourselves in
35/// opentitanlib.
36const TRANSFER_START_TIMEOUT: Duration = Duration::from_millis(1100);
37
38const USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG: u16 = 0;
39const USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG: u16 = 1;
40const USB_SPI_PKT_ID_CMD_TRANSFER_START: u16 = 2;
41const USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE: u16 = 3;
42//const USB_SPI_PKT_ID_CMD_RESTART_RESPONSE: u16 = 4;
43const USB_SPI_PKT_ID_RSP_TRANSFER_START: u16 = 5;
44const USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE: u16 = 6;
45const USB_SPI_PKT_ID_CMD_CHIP_SELECT: u16 = 7;
46const USB_SPI_PKT_ID_RSP_CHIP_SELECT: u16 = 8;
47const USB_SPI_PKT_ID_CMD_EEPROM_TRANSFER_START: u16 = 9;
48
49pub const USB_SPI_REQ_ENABLE: u8 = 0;
50//const USB_SPI_REQ_DISABLE: u8 = 1;
51pub const USB_SPI_REQ_ENABLE_AP: u8 = 2;
52pub const USB_SPI_REQ_ENABLE_EC: u8 = 3;
53
54const USB_MAX_SIZE: usize = 64;
55const FULL_DUPLEX: usize = 65535;
56
57const FEATURE_BIT_FULL_DUPLEX: u16 = 0x0001;
58const FEATURE_BIT_EEPROM: u16 = 0x0002;
59const FEATURE_BIT_EEPROM_DUAL: u16 = 0x0004;
60const FEATURE_BIT_EEPROM_QUAD: u16 = 0x0008;
61const FEATURE_BIT_EEPROM_OCTO: u16 = 0x0010;
62const FEATURE_BIT_EEPROM_DTR: u16 = 0x0020;
63const FEATURE_BIT_EEPROM_DOUBLE_BUFFER: u16 = 0x0040;
64
65const EEPROM_FLAGS_OPCODE_LEN_POS: u8 = 0;
66const EEPROM_FLAGS_ADDR_LEN_POS: u8 = 2;
67const EEPROM_FLAGS_MODE_111: u32 = 0x00000000;
68const EEPROM_FLAGS_MODE_11N: u32 = 0x00000020;
69const EEPROM_FLAGS_MODE_1NN: u32 = 0x00000040;
70const EEPROM_FLAGS_MODE_NNN: u32 = 0x00000060;
71const EEPROM_FLAGS_WIDTH_1WIRE: u32 = 0x00000000;
72const EEPROM_FLAGS_WIDTH_2WIRE: u32 = 0x00000080;
73const EEPROM_FLAGS_WIDTH_4WIRE: u32 = 0x00000100;
74const EEPROM_FLAGS_WIDTH_8WIRE: u32 = 0x00000180;
75const EEPROM_FLAGS_DTR: u32 = 0x00000200;
76const EEPROM_FLAGS_DUMMY_CYCLES_POS: u8 = 10;
77const EEPROM_FLAGS_GSC_READY: u32 = 0x04000000;
78const EEPROM_FLAGS_TPM: u32 = 0x08000000;
79const EEPROM_FLAGS_WRITE_ENABLE: u32 = 0x10000000;
80const EEPROM_FLAGS_POLL_BUSY: u32 = 0x20000000;
81const EEPROM_FLAGS_DOUBLE_BUFFER: u32 = 0x40000000;
82const EEPROM_FLAGS_WRITE: u32 = 0x80000000;
83
84const STATUS_SUCCESS: u16 = 0x0000;
85const STATUS_TIMEOUT: u16 = 0x0001;
86const STATUS_BUSY: u16 = 0x0002;
87const STATUS_WRITE_COUNT_INVALID: u16 = 0x0003;
88const STATUS_READ_COUNT_INVALID: u16 = 0x0004;
89const STATUS_DISABLED: u16 = 0x0005;
90const STATUS_RX_BAD_DATA_INDEX: u16 = 0x0006;
91const STATUS_RX_DATA_OVERFLOW: u16 = 0x0007;
92const STATUS_RX_UNEXPECTED_PACKET: u16 = 0x0008;
93const STATUS_UNSUPPORTED_FULL_DUPLEX: u16 = 0x0009;
94const STATUS_UNSUPPORTED_FLASH_MODE: u16 = 0x000A;
95const STATUS_STREAMING_FIRST_SUCCESS: u16 = 0x000B;
96
97fn status_code_description(status_code: u16) -> String {
98    match status_code {
99        STATUS_SUCCESS => "success",
100        STATUS_TIMEOUT => "timeout",
101        STATUS_BUSY => "busy",
102        STATUS_WRITE_COUNT_INVALID => "protocol corruption (WRITE_COUNT_INVALID)",
103        STATUS_READ_COUNT_INVALID => "protocol corruption (READ_COUNT_INVALID)",
104        STATUS_DISABLED => "port not enabled",
105        STATUS_RX_BAD_DATA_INDEX => "protocol corruption (RX_BAD_DATA_INDEX)",
106        STATUS_RX_DATA_OVERFLOW => "protocol corruption (RX_DATA_OVERFLOW)",
107        STATUS_RX_UNEXPECTED_PACKET => "protocol corruption (RX_UNEXPECTED_PACKET)",
108        STATUS_UNSUPPORTED_FULL_DUPLEX => "full duplex not supported",
109        STATUS_UNSUPPORTED_FLASH_MODE => "requested flash mode not supported",
110        _ => {
111            return format!("unknown error {:04x}", status_code);
112        }
113    }
114    .to_string()
115}
116
117#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
118#[repr(C)]
119struct RspUsbSpiConfig {
120    packet_id: u16,
121    max_write_chunk: u16,
122    max_read_chunk: u16,
123    feature_bitmap: u16,
124}
125
126#[derive(Immutable, IntoBytes, FromBytes, Debug)]
127#[repr(C)]
128struct CmdTransferStart {
129    packet_id: u16,
130    write_count: u16,
131    read_count: u16,
132    data: [u8; USB_MAX_SIZE - 6],
133}
134impl CmdTransferStart {
135    fn new() -> Self {
136        Self {
137            packet_id: USB_SPI_PKT_ID_CMD_TRANSFER_START,
138            write_count: 0,
139            read_count: 0,
140            data: [0; USB_MAX_SIZE - 6],
141        }
142    }
143}
144
145#[derive(Immutable, IntoBytes, FromBytes, Debug)]
146#[repr(C)]
147struct CmdEepromTransferStart {
148    packet_id: u16,
149    count: u16,
150    flags: u32,
151    data: [u8; USB_MAX_SIZE - 8],
152}
153impl CmdEepromTransferStart {
154    fn new() -> Self {
155        Self {
156            packet_id: USB_SPI_PKT_ID_CMD_EEPROM_TRANSFER_START,
157            count: 0,
158            flags: 0,
159            data: [0; USB_MAX_SIZE - 8],
160        }
161    }
162}
163
164#[derive(Immutable, IntoBytes, FromBytes, Debug)]
165#[repr(C)]
166struct CmdTransferContinue {
167    packet_id: u16,
168    data_index: u16,
169    data: [u8; USB_MAX_SIZE - 4],
170}
171impl CmdTransferContinue {
172    fn new() -> Self {
173        Self {
174            packet_id: USB_SPI_PKT_ID_CMD_TRANSFER_CONTINUE,
175            data_index: 0,
176            data: [0; USB_MAX_SIZE - 4],
177        }
178    }
179}
180
181#[derive(Immutable, IntoBytes, FromBytes, Debug)]
182#[repr(C)]
183struct RspTransferStart {
184    packet_id: u16,
185    status_code: u16,
186    data: [u8; USB_MAX_SIZE - 4],
187}
188impl RspTransferStart {
189    fn new() -> Self {
190        Self {
191            packet_id: 0,
192            status_code: 0,
193            data: [0; USB_MAX_SIZE - 4],
194        }
195    }
196}
197
198#[derive(Immutable, IntoBytes, FromBytes, Debug)]
199#[repr(C)]
200struct RspTransferContinue {
201    packet_id: u16,
202    data_index: u16,
203    data: [u8; USB_MAX_SIZE - 4],
204}
205impl RspTransferContinue {
206    fn new() -> Self {
207        Self {
208            packet_id: 0,
209            data_index: 0,
210            data: [0; USB_MAX_SIZE - 4],
211        }
212    }
213}
214
215#[derive(Immutable, IntoBytes, FromBytes, Debug)]
216#[repr(C)]
217struct CmdChipSelect {
218    packet_id: u16,
219    flags: u16,
220}
221impl CmdChipSelect {
222    fn new(assert_chip_select: bool) -> Self {
223        Self {
224            packet_id: USB_SPI_PKT_ID_CMD_CHIP_SELECT,
225            flags: u16::from(assert_chip_select),
226        }
227    }
228}
229
230#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
231#[repr(C)]
232struct RspChipSelect {
233    packet_id: u16,
234    status_code: u16,
235}
236impl RspChipSelect {
237    fn new() -> Self {
238        Self {
239            packet_id: 0,
240            status_code: 0,
241        }
242    }
243}
244
245enum StreamState<'a> {
246    NoPending,
247    PendingWrite,
248    PendingRead(&'a mut [u8]),
249}
250
251impl HyperdebugSpiTarget {
252    pub fn open(
253        inner: &Rc<Inner>,
254        spi_interface: &BulkInterface,
255        enable_cmd: u8,
256        idx: u8,
257        supports_tpm_poll: bool,
258    ) -> Result<Self> {
259        let mut usb_handle = inner.usb_device.borrow_mut();
260
261        // Tell HyperDebug to enable SPI bridge, and to address particular SPI device.
262        inner.selected_spi.set(idx);
263        usb_handle.write_control(
264            rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
265            enable_cmd,
266            idx as u16,
267            spi_interface.interface as u16,
268            &[],
269        )?;
270
271        // Exclusively claim SPI interface, preparing for bulk transfers.
272        usb_handle.claim_interface(spi_interface.interface)?;
273
274        // Initial bulk request/response to query capabilities.
275        usb_handle.write_bulk(
276            spi_interface.out_endpoint,
277            &USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG.to_le_bytes(),
278        )?;
279        let mut resp: RspUsbSpiConfig = Default::default();
280        let rc = usb_handle.read_bulk(spi_interface.in_endpoint, resp.as_mut_bytes())?;
281        ensure!(
282            rc == size_of::<RspUsbSpiConfig>(),
283            TransportError::CommunicationError(
284                "Unrecognized reponse to GET_USB_SPI_CONFIG".to_string()
285            )
286        );
287        ensure!(
288            resp.packet_id == USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG,
289            TransportError::CommunicationError(
290                "Unrecognized reponse to GET_USB_SPI_CONFIG".to_string()
291            )
292        );
293
294        Ok(Self {
295            inner: Rc::clone(inner),
296            interface: *spi_interface,
297            target_enable_cmd: enable_cmd,
298            target_idx: idx,
299            feature_bitmap: resp.feature_bitmap,
300            supports_tpm_poll,
301            max_sizes: MaxSizes {
302                read: resp.max_read_chunk as usize,
303                write: resp.max_write_chunk as usize,
304            },
305            cs_asserted_count: Cell::new(0),
306        })
307    }
308
309    /// Instruct HyperDebug device which SPI bus subsequent transactions should be forwarded to.
310    fn select_my_spi_bus(&self) -> Result<()> {
311        if self.inner.selected_spi.get() != self.target_idx {
312            self.inner.selected_spi.set(self.target_idx);
313            self.inner.usb_device.borrow().write_control(
314                rusb::request_type(Direction::Out, RequestType::Vendor, Recipient::Interface),
315                self.target_enable_cmd,
316                self.target_idx as u16,
317                self.interface.interface as u16,
318                &[],
319            )?;
320        }
321        Ok(())
322    }
323
324    /// Transmit data for a single SPI operation, using one or more USB packets.
325    fn transmit(&self, wbuf: &[u8], rbuf_len: usize) -> Result<()> {
326        let mut req = CmdTransferStart::new();
327        req.write_count = wbuf.len() as u16;
328        req.read_count = rbuf_len as u16;
329        let databytes = std::cmp::min(USB_MAX_SIZE - 6, wbuf.len());
330        req.data[0..databytes].clone_from_slice(&wbuf[0..databytes]);
331        self.usb_write_bulk(&req.as_bytes()[0..6 + databytes])?;
332        let mut index = databytes;
333
334        while index < wbuf.len() {
335            let mut req = CmdTransferContinue::new();
336            req.data_index = index as u16;
337            let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
338            req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
339            self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
340            index += databytes;
341        }
342        Ok(())
343    }
344
345    /// Preform TPM transactions, that is, send four bytes of header/address, then repeatedly poll
346    /// for ready statys from the device, before sending/receiving the data bytes.  Optionally
347    /// wait for falling edge on "GSC ready" pin, at appropriate time during tracsation.
348    fn tpm_transmit(&self, wbuf: &[u8], rbuf_len: usize, await_gsc_ready: bool) -> Result<()> {
349        const TPM_HEADER_SIZE: usize = 4;
350        let mut req = CmdEepromTransferStart::new();
351        if rbuf_len == 0 {
352            req.flags |= EEPROM_FLAGS_WRITE;
353            req.count = (wbuf.len() - TPM_HEADER_SIZE) as u16;
354            ensure!(
355                wbuf.len() > TPM_HEADER_SIZE,
356                SpiError::InvalidDataLength(wbuf.len())
357            );
358        } else {
359            req.count = rbuf_len as u16;
360            ensure!(
361                wbuf.len() == TPM_HEADER_SIZE,
362                SpiError::InvalidDataLength(wbuf.len())
363            );
364        }
365
366        req.flags |= (TPM_HEADER_SIZE as u32) << EEPROM_FLAGS_ADDR_LEN_POS;
367        req.flags |= EEPROM_FLAGS_TPM;
368        if await_gsc_ready {
369            req.flags |= EEPROM_FLAGS_GSC_READY;
370        }
371
372        let data_start_offset = 0;
373        // Optional write data bytes
374        let databytes = std::cmp::min(USB_MAX_SIZE - 8 - data_start_offset, wbuf.len());
375        req.data[data_start_offset..data_start_offset + databytes]
376            .clone_from_slice(&wbuf[0..databytes]);
377        self.usb_write_bulk(&req.as_bytes()[0..8 + data_start_offset + databytes])?;
378        let mut index = databytes;
379
380        while index < wbuf.len() {
381            let mut req = CmdTransferContinue::new();
382            req.data_index = index as u16;
383            let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
384            req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
385            self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
386            index += databytes;
387        }
388        Ok(())
389    }
390
391    /// Receive data for a single SPI operation, using one or more USB packets.
392    fn receive(&self, rbuf: &mut [u8]) -> Result<()> {
393        let mut resp = RspTransferStart::new();
394        let bytecount = self.usb_read_bulk_timeout(resp.as_mut_bytes(), TRANSFER_START_TIMEOUT)?;
395        ensure!(
396            bytecount >= 4,
397            TransportError::CommunicationError("Short reponse to TRANSFER_START".to_string())
398        );
399        ensure!(
400            resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_START,
401            TransportError::CommunicationError(format!(
402                "Unrecognized reponse to TRANSFER_START: {}",
403                resp.packet_id
404            ))
405        );
406        ensure!(
407            resp.status_code == STATUS_SUCCESS,
408            TransportError::CommunicationError(format!(
409                "SPI error: {}",
410                status_code_description(resp.status_code)
411            ))
412        );
413        let databytes = bytecount - 4;
414        rbuf[0..databytes].clone_from_slice(&resp.data[0..databytes]);
415        let mut index = databytes;
416        while index < rbuf.len() {
417            let mut resp = RspTransferContinue::new();
418            let bytecount = self.usb_read_bulk(resp.as_mut_bytes())?;
419            ensure!(
420                bytecount > 4,
421                TransportError::CommunicationError(
422                    "Short reponse to TRANSFER_CONTINUE".to_string()
423                )
424            );
425            ensure!(
426                resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_CONTINUE,
427                TransportError::CommunicationError(format!(
428                    "Unrecognized reponse to TRANSFER_CONTINUE: {}",
429                    resp.packet_id
430                ))
431            );
432            ensure!(
433                resp.data_index == index as u16,
434                TransportError::CommunicationError(
435                    "Unexpected byte index in reponse to TRANSFER_START".to_string()
436                )
437            );
438            let databytes = bytecount - 4;
439            rbuf[index..index + databytes].clone_from_slice(&resp.data[0..databytes]);
440            index += databytes;
441        }
442        Ok(())
443    }
444
445    fn receive_first_streaming(&self) -> Result<()> {
446        let mut resp = RspTransferStart::new();
447        let bytecount = self.usb_read_bulk(resp.as_mut_bytes())?;
448        ensure!(
449            bytecount >= 4,
450            TransportError::CommunicationError("Short reponse to TRANSFER_START".to_string())
451        );
452        ensure!(
453            resp.packet_id == USB_SPI_PKT_ID_RSP_TRANSFER_START,
454            TransportError::CommunicationError(format!(
455                "Unrecognized reponse to TRANSFER_START: {}",
456                resp.packet_id
457            ))
458        );
459        ensure!(
460            resp.status_code == STATUS_STREAMING_FIRST_SUCCESS,
461            TransportError::CommunicationError(format!(
462                "SPI error: {}, expected streaming response",
463                status_code_description(resp.status_code)
464            ))
465        );
466        Ok(())
467    }
468
469    fn verify_width(&self, requested_width: eeprom::DataWidth) -> Result<()> {
470        match requested_width {
471            eeprom::DataWidth::Single => (),
472            eeprom::DataWidth::Dual => ensure!(
473                self.feature_bitmap & FEATURE_BIT_EEPROM_DUAL != 0,
474                SpiError::InvalidDataWidth(requested_width)
475            ),
476            eeprom::DataWidth::Quad => ensure!(
477                self.feature_bitmap & FEATURE_BIT_EEPROM_QUAD != 0,
478                SpiError::InvalidDataWidth(requested_width)
479            ),
480            eeprom::DataWidth::Octo => ensure!(
481                self.feature_bitmap & FEATURE_BIT_EEPROM_OCTO != 0,
482                SpiError::InvalidDataWidth(requested_width)
483            ),
484        }
485        Ok(())
486    }
487
488    /// Transmit data for a single SPI operation, using one or more USB packets.
489    fn eeprom_transmit<'a>(
490        &self,
491        write_enable: Option<&eeprom::Cmd>,
492        cmd: &eeprom::Cmd,
493        wbuf: &[u8],
494        rbuf: &'a mut [u8],
495        wait_for_busy_clear: bool,
496        stream_state: StreamState,
497    ) -> Result<StreamState<'a>> {
498        let double_buffer = self.feature_bitmap & FEATURE_BIT_EEPROM_DOUBLE_BUFFER != 0;
499
500        self.verify_width(cmd.get_width())?;
501
502        let mut req = CmdEepromTransferStart::new();
503        let mut idx = 0;
504        if rbuf.is_empty() {
505            req.flags |= EEPROM_FLAGS_WRITE;
506            req.count = wbuf.len() as u16;
507        } else {
508            req.count = rbuf.len() as u16;
509        }
510
511        req.flags |= match cmd.get_switch() {
512            eeprom::Switch::Mode111 => EEPROM_FLAGS_MODE_111,
513            eeprom::Switch::Mode11N => EEPROM_FLAGS_MODE_11N,
514            eeprom::Switch::Mode1NN => EEPROM_FLAGS_MODE_1NN,
515            eeprom::Switch::ModeNNN => EEPROM_FLAGS_MODE_NNN,
516        };
517        req.flags |= match cmd.get_width() {
518            eeprom::DataWidth::Single => EEPROM_FLAGS_WIDTH_1WIRE,
519            eeprom::DataWidth::Dual => EEPROM_FLAGS_WIDTH_2WIRE,
520            eeprom::DataWidth::Quad => EEPROM_FLAGS_WIDTH_4WIRE,
521            eeprom::DataWidth::Octo => EEPROM_FLAGS_WIDTH_8WIRE,
522        };
523        if cmd.get_double_transfer_rate() {
524            ensure!(
525                self.feature_bitmap & FEATURE_BIT_EEPROM_DTR != 0,
526                SpiError::InvalidDoubleTransferRate()
527            );
528            req.flags |= EEPROM_FLAGS_DTR;
529        }
530
531        // Command bytes
532        req.flags |= (cmd.get_opcode_len() as u32) << EEPROM_FLAGS_OPCODE_LEN_POS;
533
534        if double_buffer {
535            req.flags |= EEPROM_FLAGS_DOUBLE_BUFFER;
536        }
537        if let Some(pre_cmd) = write_enable {
538            req.flags |= EEPROM_FLAGS_WRITE_ENABLE;
539            let opcode_bytes = pre_cmd.get_opcode();
540            if opcode_bytes.len() != 1 {
541                panic!("Illegal write enable sequence");
542            }
543            req.data[idx..idx + opcode_bytes.len()].clone_from_slice(opcode_bytes);
544            idx += opcode_bytes.len();
545        }
546        if wait_for_busy_clear {
547            req.flags |= EEPROM_FLAGS_POLL_BUSY;
548        }
549        let opcode_bytes = cmd.get_opcode();
550        let address_bytes =
551            &cmd.get_address().to_be_bytes()[(4 - cmd.get_address_len()) as usize..];
552
553        req.data[idx..idx + opcode_bytes.len()].clone_from_slice(opcode_bytes);
554        idx += opcode_bytes.len();
555        let mut addr_len = cmd.get_address_len();
556        req.data[idx..idx + address_bytes.len()].clone_from_slice(address_bytes);
557        idx += address_bytes.len();
558        if cmd.get_switch() == eeprom::Switch::Mode111
559            && cmd.get_dummy_cycles().is_multiple_of(8)
560            && addr_len + cmd.get_dummy_cycles() / 8 <= 7
561        {
562            // In cases when the number of dummy cycles is divisible by 8, stuff a number of
563            // zero bytes after the address.  This allows debuggers without native support for
564            // an arbitrary number of dummy cycles to perform the transaction.
565            addr_len += cmd.get_dummy_cycles() / 8;
566            for _ in 0..cmd.get_dummy_cycles() / 8 {
567                req.data[idx] = 0x00;
568                idx += 1;
569            }
570        } else if cmd.get_dummy_cycles() < 32 {
571            req.flags |= (cmd.get_dummy_cycles() as u32) << EEPROM_FLAGS_DUMMY_CYCLES_POS
572        } else {
573            bail!(SpiError::InvalidDummyCycles(cmd.get_dummy_cycles()));
574        }
575        req.flags |= (addr_len as u32) << EEPROM_FLAGS_ADDR_LEN_POS;
576
577        let data_start_offset = idx;
578        // Optional write data bytes
579        let databytes = std::cmp::min(USB_MAX_SIZE - 8 - data_start_offset, wbuf.len());
580        req.data[data_start_offset..data_start_offset + databytes]
581            .clone_from_slice(&wbuf[0..databytes]);
582        self.usb_write_bulk(&req.as_bytes()[0..8 + data_start_offset + databytes])?;
583        let mut index = databytes;
584
585        while index < wbuf.len() {
586            let mut req = CmdTransferContinue::new();
587            req.data_index = index as u16;
588            let databytes = std::cmp::min(USB_MAX_SIZE - 4, wbuf.len() - index);
589            req.data[0..databytes].clone_from_slice(&wbuf[index..index + databytes]);
590            self.usb_write_bulk(&req.as_bytes()[0..4 + databytes])?;
591            index += databytes;
592        }
593
594        let next_stream_state = if rbuf.is_empty() {
595            StreamState::PendingWrite
596        } else {
597            StreamState::PendingRead(rbuf)
598        };
599        if double_buffer {
600            self.receive_streamed_data(stream_state)?;
601            Ok(next_stream_state)
602        } else {
603            self.receive_streamed_data(next_stream_state)?;
604            Ok(StreamState::NoPending)
605        }
606    }
607
608    fn receive_streamed_data(&self, stream_state: StreamState) -> Result<()> {
609        match stream_state {
610            StreamState::NoPending => self.receive_first_streaming(),
611            StreamState::PendingWrite => self.receive(&mut []),
612            StreamState::PendingRead(rbuf) => self.receive(rbuf),
613        }
614    }
615
616    fn eeprom_transmit_get_last_streamed_data(&self) -> Result<()> {
617        let mut req = CmdEepromTransferStart::new();
618        req.count = 0;
619        req.flags = EEPROM_FLAGS_DOUBLE_BUFFER;
620        self.usb_write_bulk(&req.as_bytes()[0..8])
621    }
622
623    fn get_last_streamed_data(&self, stream_state: StreamState) -> Result<()> {
624        match stream_state {
625            StreamState::NoPending => Ok(()),
626            StreamState::PendingWrite => {
627                self.eeprom_transmit_get_last_streamed_data()?;
628                self.receive(&mut [])
629            }
630            StreamState::PendingRead(rbuf) => {
631                self.eeprom_transmit_get_last_streamed_data()?;
632                self.receive(rbuf)
633            }
634        }
635    }
636
637    /// Request assertion or deassertion of chip select
638    fn do_assert_cs(&self, assert: bool) -> Result<()> {
639        let mut count = self.cs_asserted_count.get();
640        if assert {
641            if count == 0 {
642                self._do_assert_cs(assert)?;
643            }
644            count += 1;
645        } else {
646            if count == 1 {
647                self._do_assert_cs(assert)?;
648            }
649            count -= 1;
650        }
651        self.cs_asserted_count.set(count);
652        Ok(())
653    }
654
655    fn _do_assert_cs(&self, assert: bool) -> Result<()> {
656        let req = CmdChipSelect::new(assert);
657        self.usb_write_bulk(req.as_bytes())?;
658
659        let mut resp = RspChipSelect::new();
660        let bytecount = self.usb_read_bulk(resp.as_mut_bytes())?;
661        ensure!(
662            bytecount >= 4,
663            TransportError::CommunicationError("Unrecognized reponse to CHIP_SELECT".to_string())
664        );
665        ensure!(
666            resp.packet_id == USB_SPI_PKT_ID_RSP_CHIP_SELECT,
667            TransportError::CommunicationError("Unrecognized reponse to CHIP_SELECT".to_string())
668        );
669        ensure!(
670            resp.status_code == STATUS_SUCCESS,
671            TransportError::CommunicationError(format!("SPI error: {}", resp.status_code))
672        );
673        Ok(())
674    }
675
676    /// Send one USB packet.
677    fn usb_write_bulk(&self, buf: &[u8]) -> Result<()> {
678        self.inner
679            .usb_device
680            .borrow()
681            .write_bulk(self.interface.out_endpoint, buf)?;
682        Ok(())
683    }
684
685    /// Receive one USB packet.
686    fn usb_read_bulk(&self, buf: &mut [u8]) -> Result<usize> {
687        self.inner
688            .usb_device
689            .borrow()
690            .read_bulk(self.interface.in_endpoint, buf)
691    }
692
693    /// Receive one USB packet, with particular timeout.
694    fn usb_read_bulk_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
695        self.inner
696            .usb_device
697            .borrow()
698            .read_bulk_timeout(self.interface.in_endpoint, buf, timeout)
699    }
700}
701
702impl Target for HyperdebugSpiTarget {
703    fn get_transfer_mode(&self) -> Result<TransferMode> {
704        Ok(TransferMode::Mode0)
705    }
706    fn set_transfer_mode(&self, _mode: TransferMode) -> Result<()> {
707        todo!();
708    }
709
710    fn get_bits_per_word(&self) -> Result<u32> {
711        Ok(8)
712    }
713    fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> {
714        match bits_per_word {
715            8 => Ok(()),
716            _ => Err(SpiError::InvalidWordSize(bits_per_word).into()),
717        }
718    }
719
720    fn get_max_speed(&self) -> Result<u32> {
721        let mut buf = String::new();
722        let captures = self.inner.cmd_one_line_output_match(
723            &format!("spi info {}", &self.target_idx),
724            &super::SPI_REGEX,
725            &mut buf,
726        )?;
727        Ok(captures.get(3).unwrap().as_str().parse().unwrap())
728    }
729    fn set_max_speed(&self, frequency: u32) -> Result<()> {
730        self.inner
731            .cmd_no_output(&format!("spi set speed {} {}", &self.target_idx, frequency))
732    }
733
734    fn supports_bidirectional_transfer(&self) -> Result<bool> {
735        Ok((self.feature_bitmap & FEATURE_BIT_FULL_DUPLEX) != 0)
736    }
737
738    fn supports_tpm_poll(&self) -> Result<bool> {
739        Ok(self.supports_tpm_poll)
740    }
741
742    fn set_pins(
743        &self,
744        serial_clock: Option<&Rc<dyn GpioPin>>,
745        host_out_device_in: Option<&Rc<dyn GpioPin>>,
746        host_in_device_out: Option<&Rc<dyn GpioPin>>,
747        chip_select: Option<&Rc<dyn GpioPin>>,
748        gsc_ready: Option<&Rc<dyn GpioPin>>,
749    ) -> Result<()> {
750        if serial_clock.is_some() || host_out_device_in.is_some() || host_in_device_out.is_some() {
751            bail!(SpiError::InvalidPin);
752        }
753        if let Some(pin) = chip_select {
754            self.inner.cmd_no_output(&format!(
755                "spi set cs {} {}",
756                &self.target_idx,
757                pin.get_internal_pin_name().ok_or(SpiError::InvalidPin)?
758            ))?;
759        }
760        if let Some(pin) = gsc_ready {
761            self.inner.cmd_no_output(&format!(
762                "spi set ready {} {}",
763                &self.target_idx,
764                pin.get_internal_pin_name().ok_or(SpiError::InvalidPin)?
765            ))?;
766        }
767        Ok(())
768    }
769
770    fn get_max_transfer_count(&self) -> Result<usize> {
771        // The protocol imposes no limits to the number of Transfers
772        // in a transaction.
773        Ok(usize::MAX)
774    }
775
776    fn get_max_transfer_sizes(&self) -> Result<MaxSizes> {
777        Ok(self.max_sizes)
778    }
779
780    fn get_flashrom_programmer(&self) -> Result<String> {
781        Ok(format!(
782            "raiden_debug_spi:serial={},target={}",
783            self.inner.usb_device.borrow().get_serial_number(),
784            self.target_idx
785        ))
786    }
787
788    fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
789        let mut idx: usize = 0;
790        self.select_my_spi_bus()?;
791
792        // Simple cases involving using only a single USB command can be handled without explicit
793        // embracing commands to hold CS asserted across a sequence of transfers, use that for
794        // avoiding several USB roundtrips in the common cases.
795        match transaction {
796            [Transfer::Write(wbuf), Transfer::Read(rbuf)] => {
797                // Hyperdebug can do SPI write followed by SPI read as a single USB
798                // request/reply.  Take advantage of that by detecting pairs of
799                // Transfer::Write followed by Transfer::Read.
800                ensure!(
801                    wbuf.len() <= self.max_sizes.write,
802                    SpiError::InvalidDataLength(wbuf.len())
803                );
804                ensure!(
805                    rbuf.len() <= self.max_sizes.read,
806                    SpiError::InvalidDataLength(rbuf.len())
807                );
808                self.transmit(wbuf, rbuf.len())?;
809                self.receive(rbuf)?;
810                return Ok(());
811            }
812            [
813                Transfer::Write(wbuf),
814                Transfer::TpmPoll,
815                Transfer::Read(rbuf),
816                Transfer::GscReady,
817            ] => {
818                // Hyperdebug can do SPI TPM transaction as a single USB
819                // request/reply.
820                ensure!(
821                    wbuf.len() <= self.max_sizes.write,
822                    SpiError::InvalidDataLength(wbuf.len())
823                );
824                ensure!(
825                    rbuf.len() <= self.max_sizes.read,
826                    SpiError::InvalidDataLength(rbuf.len())
827                );
828                self.tpm_transmit(wbuf, rbuf.len(), true)?;
829                self.receive(rbuf)?;
830                return Ok(());
831            }
832            [
833                Transfer::Write(wbuf),
834                Transfer::TpmPoll,
835                Transfer::Read(rbuf),
836            ] => {
837                // Hyperdebug can do SPI TPM transaction as a single USB
838                // request/reply.
839                ensure!(
840                    wbuf.len() <= self.max_sizes.write,
841                    SpiError::InvalidDataLength(wbuf.len())
842                );
843                ensure!(
844                    rbuf.len() <= self.max_sizes.read,
845                    SpiError::InvalidDataLength(rbuf.len())
846                );
847                self.tpm_transmit(wbuf, rbuf.len(), false)?;
848                self.receive(rbuf)?;
849                return Ok(());
850            }
851            [Transfer::Write(wbuf)] => {
852                ensure!(
853                    wbuf.len() <= self.max_sizes.write,
854                    SpiError::InvalidDataLength(wbuf.len())
855                );
856                self.transmit(wbuf, 0)?;
857                self.receive(&mut [])?;
858                return Ok(());
859            }
860            [Transfer::Write(wbuf1), Transfer::Write(wbuf2)] => {
861                if wbuf1.len() + wbuf2.len() <= self.max_sizes.write {
862                    let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
863                    combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
864                    combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
865                    self.transmit(&combined_buf, 0)?;
866                    self.receive(&mut [])?;
867                    return Ok(());
868                }
869            }
870            [
871                Transfer::Write(wbuf1),
872                Transfer::TpmPoll,
873                Transfer::Write(wbuf2),
874                Transfer::GscReady,
875            ] => {
876                // Hyperdebug can do SPI TPM transaction as a single USB
877                // request/reply.
878                ensure!(
879                    wbuf1.len() + wbuf2.len() <= self.max_sizes.write,
880                    SpiError::InvalidDataLength(wbuf1.len() + wbuf2.len())
881                );
882                let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
883                combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
884                combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
885                self.tpm_transmit(&combined_buf, 0, true)?;
886                self.receive(&mut [])?;
887                return Ok(());
888            }
889            [
890                Transfer::Write(wbuf1),
891                Transfer::TpmPoll,
892                Transfer::Write(wbuf2),
893            ] => {
894                // Hyperdebug can do SPI TPM transaction as a single USB
895                // request/reply.
896                ensure!(
897                    wbuf1.len() + wbuf2.len() <= self.max_sizes.write,
898                    SpiError::InvalidDataLength(wbuf1.len() + wbuf2.len())
899                );
900                let mut combined_buf = vec![0u8; wbuf1.len() + wbuf2.len()];
901                combined_buf[..wbuf1.len()].clone_from_slice(wbuf1);
902                combined_buf[wbuf1.len()..].clone_from_slice(wbuf2);
903                self.tpm_transmit(&combined_buf, 0, false)?;
904                self.receive(&mut [])?;
905                return Ok(());
906            }
907            [Transfer::Read(rbuf)] => {
908                ensure!(
909                    rbuf.len() <= self.max_sizes.read,
910                    SpiError::InvalidDataLength(rbuf.len())
911                );
912                self.transmit(&[], rbuf.len())?;
913                self.receive(rbuf)?;
914                return Ok(());
915            }
916            _ => (),
917        }
918
919        // If control flow reaches this point, we have a more complicated sequence of operations,
920        // and have to explicitly tell HyperDebug to keep the CS asserted while we issue each
921        // command in turn.
922        self.do_assert_cs(true)?;
923        while idx < transaction.len() {
924            match &mut transaction[idx..] {
925                [Transfer::Write(wbuf), Transfer::Read(rbuf), ..] => {
926                    ensure!(
927                        wbuf.len() <= self.max_sizes.write,
928                        SpiError::InvalidDataLength(wbuf.len())
929                    );
930                    ensure!(
931                        rbuf.len() <= self.max_sizes.read,
932                        SpiError::InvalidDataLength(rbuf.len())
933                    );
934                    self.transmit(wbuf, rbuf.len())?;
935                    self.receive(rbuf)?;
936                    // Skip two steps ahead, as two items were processed.
937                    idx += 2;
938                    continue;
939                }
940                [Transfer::Write(wbuf), ..] => {
941                    ensure!(
942                        wbuf.len() <= self.max_sizes.write,
943                        SpiError::InvalidDataLength(wbuf.len())
944                    );
945                    self.transmit(wbuf, 0)?;
946                    self.receive(&mut [])?;
947                }
948                [Transfer::Read(rbuf), ..] => {
949                    ensure!(
950                        rbuf.len() <= self.max_sizes.read,
951                        SpiError::InvalidDataLength(rbuf.len())
952                    );
953                    self.transmit(&[], rbuf.len())?;
954                    self.receive(rbuf)?;
955                }
956                [Transfer::Both(wbuf, rbuf), ..] => {
957                    ensure!(
958                        (self.feature_bitmap & FEATURE_BIT_FULL_DUPLEX) != 0,
959                        TransportError::CommunicationError(
960                            "HyperDebug does not support bidirectional SPI".to_string()
961                        )
962                    );
963                    ensure!(
964                        rbuf.len() == wbuf.len(),
965                        SpiError::MismatchedDataLength(wbuf.len(), rbuf.len())
966                    );
967                    ensure!(
968                        wbuf.len() <= self.max_sizes.read && wbuf.len() <= self.max_sizes.write,
969                        SpiError::InvalidDataLength(wbuf.len())
970                    );
971                    self.transmit(wbuf, FULL_DUPLEX)?;
972                    self.receive(rbuf)?;
973                }
974                [Transfer::TpmPoll, ..] => bail!(TransportError::UnsupportedOperation),
975                [Transfer::GscReady, ..] => bail!(TransportError::UnsupportedOperation),
976                [] => (),
977            }
978            idx += 1;
979        }
980        self.do_assert_cs(false)?;
981        Ok(())
982    }
983
984    fn run_eeprom_transactions(&self, mut transactions: &mut [eeprom::Transaction]) -> Result<()> {
985        if self.feature_bitmap & FEATURE_BIT_EEPROM == 0 {
986            // Debugger hardware/firmware does not support multi-lane extensions, attempt to
987            // perform operation using basic SPI read/write.
988            return eeprom::default_run_eeprom_transactions(self, transactions);
989        }
990        self.select_my_spi_bus()?;
991        let mut stream_state = StreamState::NoPending;
992        loop {
993            match transactions {
994                [
995                    eeprom::Transaction::Command(pre_cmd),
996                    eeprom::Transaction::Write(cmd, wbuf),
997                    eeprom::Transaction::WaitForBusyClear,
998                    rest @ ..,
999                ] => {
1000                    if pre_cmd.get_opcode().len() == 1 {
1001                        stream_state = self.eeprom_transmit(
1002                            Some(pre_cmd), /* write_enable */
1003                            cmd,
1004                            wbuf,
1005                            &mut [],
1006                            true, /* wait_for_busy_clear */
1007                            stream_state,
1008                        )?;
1009                    } else {
1010                        stream_state =
1011                            self.eeprom_transmit(None, pre_cmd, &[], &mut [], false, stream_state)?;
1012                        stream_state = self.eeprom_transmit(
1013                            None, /* write_enable */
1014                            cmd,
1015                            wbuf,
1016                            &mut [],
1017                            true, /* wait_for_busy_clear */
1018                            stream_state,
1019                        )?;
1020                    }
1021                    transactions = rest;
1022                }
1023                [eeprom::Transaction::Command(cmd), rest @ ..] => {
1024                    stream_state =
1025                        self.eeprom_transmit(None, cmd, &[], &mut [], false, stream_state)?;
1026                    transactions = rest;
1027                }
1028                [eeprom::Transaction::Read(cmd, rbuf), rest @ ..] => {
1029                    stream_state =
1030                        self.eeprom_transmit(None, cmd, &[], rbuf, false, stream_state)?;
1031                    transactions = rest;
1032                }
1033                [
1034                    eeprom::Transaction::Write(cmd, wbuf),
1035                    eeprom::Transaction::WaitForBusyClear,
1036                    rest @ ..,
1037                ] => {
1038                    stream_state = self.eeprom_transmit(
1039                        None, /* write_enable */
1040                        cmd,
1041                        wbuf,
1042                        &mut [],
1043                        true, /* wait_for_busy_clear */
1044                        stream_state,
1045                    )?;
1046                    transactions = rest;
1047                }
1048                [eeprom::Transaction::Write(cmd, wbuf), rest @ ..] => {
1049                    stream_state =
1050                        self.eeprom_transmit(None, cmd, wbuf, &mut [], false, stream_state)?;
1051                    transactions = rest;
1052                }
1053                [eeprom::Transaction::WaitForBusyClear, rest @ ..] => {
1054                    self.get_last_streamed_data(stream_state)?;
1055                    let mut status = eeprom::STATUS_WIP;
1056                    while status & eeprom::STATUS_WIP != 0 {
1057                        self.run_transaction(&mut [
1058                            Transfer::Write(&[eeprom::READ_STATUS]),
1059                            Transfer::Read(std::slice::from_mut(&mut status)),
1060                        ])?;
1061                    }
1062                    stream_state = StreamState::NoPending;
1063                    transactions = rest;
1064                }
1065                [] => {
1066                    return self.get_last_streamed_data(stream_state);
1067                }
1068            }
1069        }
1070    }
1071
1072    fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
1073        self.do_assert_cs(true)?;
1074        Ok(AssertChipSelect::new(self))
1075    }
1076}
1077
1078impl TargetChipDeassert for HyperdebugSpiTarget {
1079    fn deassert_cs(&self) {
1080        // We cannot propagate errors through `Drop::drop()`, so panic on any error.  (Logging
1081        // would be another option.)
1082        self.do_assert_cs(false)
1083            .expect("Error while deasserting CS");
1084    }
1085}