diff --git a/README.md b/README.md index cc790f2..d3dd5ec 100644 --- a/README.md +++ b/README.md @@ -75,12 +75,25 @@ Available subcommands: (append --help to subcommand for details) target reset on - Put the target device into reset. target reset off - Take the target device out of reset target reset pulse - Quickly put the target device in and out of reset + target debug enable - Enable target debug. + target debug disable - Disable target debug. + target debug get - Get target debug status. console - Open a console for communicating with the RoT or devices attached to the RoT. payload getstatus - Show the current payload update status + payload verify - Verify the staging side. payload status - Show payload status payload update - Perform payload update protocol for Titan images. + payload confirm - Finish a payload update confirmation. + payload get_timeout - Get the current payload update confirmation timeout. + payload timeout_enable - Enable/Disable the timeout mechanism for payload update confirm payload read - Read content of staging flash for Titan images. + payload erase - Erase a range on the staging side. + payload activate - Activate a payload side for next boot. + payload info all - Display detailed payload info for a Titan image, including region details. + payload info nonstatic - Print non-static regions in a Titan image. payload info - Display payload info for a Titan image. + payload mauv compiled - Display compiled MAUV for the payload. + payload mauv effective - Display effective MAUV for the payload. firmware_update update_from_flash_and_reset - Installs a firmware update from a bundle staged in the external flash. dfu update - Directly install a PIE-RoT fwupdate. dfu check - Check that the device is running firmware matching a fwupdate bundle. @@ -119,6 +132,7 @@ Available subcommands: (append --help to subcommand for details) storage read - Read from the controlled storage storage write - Write to the controlled storage storage delete - Delete from the controlled storage + gpio set_drive_strength - Set GPIO drive strength hello - A test function to send and receive an integer opentitan_version - Get OpenTitan version extract_ot_bundle - Get OpenTitan version @@ -143,6 +157,12 @@ Available subcommands: (append --help to subcommand for details) security get_attestation_pub_cert - Get the Attestation Public Cert security get_signed_attestation_pub_cert - Get the Signed Attestation Public Cert security attestation - Fetch attestation information, including tokens and certificates. + mauv compiled - Get compiled MAUV + mauv effective - Get effective MAUV + tpm get_mode - Get the current TPM mode. + tpm set_mode disabled - Set the TPM mode to DISABLED. + tpm set_mode tpm_spi - Set the TPM mode to TPM_SPI. + tpm set_mode spi_nor_mailbox - Set the TPM mode to SPI_NOR_MAILBOX. Global flags: --transport (default: "") @@ -161,8 +181,8 @@ Global flags: Maximum duration (in microseconds) to wait when SPI device indicates that it is busy --spidev_device_busy_wait_check_interval (default: "100") Interval duration (in microseconds) to wait before checking SPI device status again when it indicates that the device is busy - --quadmode (default: "auto") - Enable Quad SPI mode (auto|on|force|off). Default: auto + --spidev_mode (default: "single") + SPI mode toggles (single|dual|quad). --mtddev_path (default: "") The full MTD path of the RoT mailbox; for example '/dev/mtd0'. If unspecified, will attempt to detect the correct device automatically --mtddev_name (default: "hoth-mailbox") diff --git a/examples/htool.c b/examples/htool.c index 65a8c36..b11d34c 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -2010,6 +2010,9 @@ static const struct htool_param GLOBAL_FLAGS[] = { .default_value = "100", .desc = "Interval duration (in microseconds) to wait before checking SPI " "device status again when it indicates that the device is busy"}, + // TODO(michaelfield) : b/346345769 - enable spidev mode auto-detection + {HTOOL_FLAG_VALUE, .name = "spidev_mode", .default_value = "single", + .desc = "SPI mode toggles (single|dual|quad)."}, {HTOOL_FLAG_VALUE, .name = "mtddev_path", .default_value = "", .desc = "The full MTD path of the RoT mailbox; for example " "'/dev/mtd0'. If unspecified, will attempt to detect " diff --git a/examples/htool_spi.c b/examples/htool_spi.c index 198da09..26987b6 100644 --- a/examples/htool_spi.c +++ b/examples/htool_spi.c @@ -32,6 +32,7 @@ struct libhoth_device* htool_libhoth_spi_device(void) { int rv; const char* spidev_path_str; + const char* mode_str; uint32_t mailbox_location; bool atomic; uint32_t spidev_speed_hz; @@ -39,6 +40,7 @@ struct libhoth_device* htool_libhoth_spi_device(void) { uint32_t spidev_device_busy_wait_check_interval; rv = htool_get_param_string(htool_global_flags(), "spidev_path", &spidev_path_str) || + htool_get_param_string(htool_global_flags(), "spidev_mode", &mode_str) || htool_get_param_u32(htool_global_flags(), "mailbox_location", &mailbox_location) || htool_get_param_bool(htool_global_flags(), "spidev_atomic", &atomic) || @@ -54,6 +56,18 @@ struct libhoth_device* htool_libhoth_spi_device(void) { return NULL; } + enum libhoth_spi_mode operation_mode; + if (!strcmp(mode_str, "single")) { + operation_mode = LIBHOTH_SPI_MODE_SINGLE; + } else if (!strcmp(mode_str, "dual")) { + operation_mode = LIBHOTH_SPI_MODE_DUAL; + } else if (!strcmp(mode_str, "quad")) { + operation_mode = LIBHOTH_SPI_MODE_QUAD; + } else { + fprintf(stderr, "Invalid spidev mode: %s\n", mode_str); + return NULL; + } + if (strlen(spidev_path_str) <= 0) { fprintf(stderr, "Invalid spidev path: %s\n", spidev_path_str); return NULL; @@ -78,6 +92,7 @@ struct libhoth_device* htool_libhoth_spi_device(void) { .mailbox = mailbox_location, .atomic = atomic, .speed = spidev_speed_hz, + .operation_mode = operation_mode, .device_busy_wait_timeout = spidev_device_busy_wait_timeout, .device_busy_wait_check_interval = spidev_device_busy_wait_check_interval, .timeout_us = timeout_us, diff --git a/transports/libhoth_spi.c b/transports/libhoth_spi.c index 09070a8..b659629 100644 --- a/transports/libhoth_spi.c +++ b/transports/libhoth_spi.c @@ -35,6 +35,41 @@ #define DID_VID_ADDR 0xD40F00 +static uint8_t mode_to_nbits(enum libhoth_spi_mode mode) { + switch (mode) { + case LIBHOTH_SPI_MODE_DUAL: + return 2; + case LIBHOTH_SPI_MODE_QUAD: + return 4; + case LIBHOTH_SPI_MODE_SINGLE: + default: + return 1; + } +} + +static uint8_t mode_to_read_opcode(enum libhoth_spi_mode mode) { + switch (mode) { + case LIBHOTH_SPI_MODE_DUAL: + return SPI_NOR_OPCODE_DUAL_READ; + case LIBHOTH_SPI_MODE_QUAD: + return SPI_NOR_OPCODE_QUAD_READ; + case LIBHOTH_SPI_MODE_SINGLE: + default: + return SPI_NOR_OPCODE_SLOW_READ; + } +} + +static uint8_t mode_to_write_opcode(enum libhoth_spi_mode mode) { + switch (mode) { + case LIBHOTH_SPI_MODE_QUAD: + return SPI_NOR_OPCODE_QUAD_PAGE_PROGRAM; + case LIBHOTH_SPI_MODE_SINGLE: + case LIBHOTH_SPI_MODE_DUAL: + default: + return SPI_NOR_OPCODE_PAGE_PROGRAM; + } +} + static int spi_nor_address(uint8_t* buf, uint32_t address, bool address_mode_4b) { if (address_mode_4b) { @@ -128,7 +163,8 @@ static int spi_nor_write_enable(const int fd) { return LIBHOTH_OK; } -static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address, +static int spi_nor_write(int fd, bool address_mode_4b, + enum libhoth_spi_mode mode, unsigned int address, const void* data, size_t data_len, uint32_t device_busy_wait_timeout, uint32_t device_busy_wait_check_interval) { @@ -147,7 +183,7 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address, uint8_t rq_buf[5] = {0}; // 1 for command opcode, 4 (max) for address // Page Program OPCODE + Address - rq_buf[0] = SPI_NOR_OPCODE_PAGE_PROGRAM; + rq_buf[0] = mode_to_write_opcode(mode); int address_len = spi_nor_address(&rq_buf[1], address, address_mode_4b); xfer[0] = (struct spi_ioc_transfer){ .tx_buf = (unsigned long)rq_buf, @@ -163,6 +199,11 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address, .len = chunk_send_size, }; + if (mode != LIBHOTH_SPI_MODE_SINGLE) { + uint8_t nbits = mode_to_nbits(mode); + xfer[1].tx_nbits = nbits; + } + status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer); if (status < 0) { return LIBHOTH_ERR_FAIL; @@ -180,7 +221,8 @@ static int spi_nor_write(int fd, bool address_mode_4b, unsigned int address, return LIBHOTH_OK; } -static int spi_nor_read(int fd, bool address_mode_4b, unsigned int address, +static int spi_nor_read(int fd, bool address_mode_4b, + enum libhoth_spi_mode mode, unsigned int address, void* data, size_t data_len) { if (fd < 0 || !data || !data_len) return LIBHOTH_ERR_INVALID_PARAMETER; @@ -188,7 +230,7 @@ static int spi_nor_read(int fd, bool address_mode_4b, unsigned int address, struct spi_ioc_transfer xfer[2] = {0}; // Read OPCODE and mailbox address - rd_request[0] = SPI_NOR_OPCODE_SLOW_READ; + rd_request[0] = mode_to_read_opcode(mode); int address_len = spi_nor_address(&rd_request[1], address, address_mode_4b); xfer[0] = (struct spi_ioc_transfer){ .tx_buf = (unsigned long)rd_request, @@ -201,6 +243,11 @@ static int spi_nor_read(int fd, bool address_mode_4b, unsigned int address, .len = data_len, }; + if (mode != LIBHOTH_SPI_MODE_SINGLE) { + uint8_t nbits = mode_to_nbits(mode); + xfer[1].rx_nbits = nbits; + } + int status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer); if (status < 0) { return LIBHOTH_ERR_FAIL; @@ -281,6 +328,7 @@ int libhoth_spi_open(const struct libhoth_spi_device_init_options* options, spi_dev->fd = fd; spi_dev->mailbox_address = options->mailbox; spi_dev->address_mode_4b = true; + spi_dev->mode = options->operation_mode; spi_dev->device_busy_wait_timeout = options->device_busy_wait_timeout; spi_dev->device_busy_wait_check_interval = options->device_busy_wait_check_interval; @@ -311,12 +359,52 @@ int libhoth_spi_open(const struct libhoth_spi_device_init_options* options, } } - if (options->mode) { - const uint8_t mode = (uint8_t)options->mode; - if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { + uint32_t mode = options->mode; + if (ioctl(fd, SPI_IOC_RD_MODE32, &spi_dev->original_mode) < 0) { + status = LIBHOTH_ERR_FAIL; + goto err_out; + } + + // TODO(michaelfield): Readback the SFDP and check that quadmode is + // supported. If not, then we should fail unless the user explicitly + // requested to force quadmode. + + if (options->operation_mode == LIBHOTH_SPI_MODE_QUAD) { + // Quadmode spi needs to use 32-bit mode flags + mode |= (SPI_TX_QUAD | SPI_RX_QUAD); + + if (ioctl(fd, SPI_IOC_WR_MODE32, &mode) < 0) { status = LIBHOTH_ERR_FAIL; goto err_out; } + } else if (options->operation_mode == LIBHOTH_SPI_MODE_DUAL) { + // Dualmode spi needs to use 32-bit mode flags + mode |= (SPI_TX_DUAL | SPI_RX_DUAL); + + if (ioctl(fd, SPI_IOC_WR_MODE32, &mode) < 0) { + status = LIBHOTH_ERR_FAIL; + goto err_out; + } + } else { + // Set the mode anyways. + // There is a failure mode wherein a bad mode setting will stick. + // Even if mode is zero, we still want to write it. + if (ioctl(fd, SPI_IOC_WR_MODE32, &mode) < 0) { + status = LIBHOTH_ERR_FAIL; + goto err_out; + } + } + + // read back the mode, and verify that it is what we expect. + uint32_t read_mode; + if (ioctl(fd, SPI_IOC_RD_MODE32, &read_mode) < 0) { + status = LIBHOTH_ERR_FAIL; + goto err_out; + } + + if (read_mode != mode) { + status = LIBHOTH_ERR_FAIL; + goto err_out; } if (options->speed) { @@ -353,7 +441,7 @@ int libhoth_spi_send_request(struct libhoth_device* dev, const void* request, struct libhoth_spi_device* spi_dev = (struct libhoth_spi_device*)dev->user_ctx; - return spi_nor_write(spi_dev->fd, spi_dev->address_mode_4b, + return spi_nor_write(spi_dev->fd, spi_dev->address_mode_4b, spi_dev->mode, spi_dev->mailbox_address, request, request_size, spi_dev->device_busy_wait_timeout, spi_dev->device_busy_wait_check_interval); @@ -377,7 +465,7 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response, (struct libhoth_spi_device*)dev->user_ctx; // Read Header From Mailbox - status = spi_nor_read(spi_dev->fd, spi_dev->address_mode_4b, + status = spi_nor_read(spi_dev->fd, spi_dev->address_mode_4b, spi_dev->mode, spi_dev->mailbox_address, response, sizeof(struct hoth_host_response)); if (status != LIBHOTH_OK) { @@ -397,7 +485,7 @@ int libhoth_spi_receive_response(struct libhoth_device* dev, void* response, if (host_response.data_len > 0) { // Read remainder of data based on header length uint8_t* const data_start = (uint8_t*)response + total_bytes; - status = spi_nor_read(spi_dev->fd, spi_dev->address_mode_4b, + status = spi_nor_read(spi_dev->fd, spi_dev->address_mode_4b, spi_dev->mode, spi_dev->mailbox_address + total_bytes, data_start, host_response.data_len); if (status != LIBHOTH_OK) { @@ -467,7 +555,7 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev, // Page Program OPCODE + Mailbox Address uint8_t pp_buf[5] = {0}; - pp_buf[0] = SPI_NOR_OPCODE_PAGE_PROGRAM; + pp_buf[0] = mode_to_write_opcode(spi_dev->mode); int address_len = spi_nor_address(&pp_buf[1], address, address_mode_4b); xfer[1] = (struct spi_ioc_transfer){ .tx_buf = (unsigned long)pp_buf, @@ -481,11 +569,15 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev, .cs_change = 1, }; + if (spi_dev->mode != LIBHOTH_SPI_MODE_SINGLE) { + uint8_t nbits = mode_to_nbits(spi_dev->mode); + xfer[2].tx_nbits = nbits; + } // Wait for status register is handled by the spidev driver. // Read opcode + Mailbox Address uint8_t rd_buf[5] = {0}; - rd_buf[0] = SPI_NOR_OPCODE_SLOW_READ; + rd_buf[0] = mode_to_read_opcode(spi_dev->mode); address_len = spi_nor_address(&rd_buf[1], address, address_mode_4b); xfer[3] = (struct spi_ioc_transfer){ .tx_buf = (unsigned long)rd_buf, @@ -498,6 +590,11 @@ int libhoth_spi_send_and_receive_response(struct libhoth_device* dev, .len = max_response_size, }; + if (spi_dev->mode != LIBHOTH_SPI_MODE_SINGLE) { + uint8_t nbits = mode_to_nbits(spi_dev->mode); + xfer[4].rx_nbits = nbits; + } + int rc = LIBHOTH_OK; int status = ioctl(spi_dev->fd, SPI_IOC_MESSAGE(5), xfer); if (status < 0) { @@ -564,6 +661,12 @@ int libhoth_spi_close(struct libhoth_device* dev) { struct libhoth_spi_device* spi_dev = (struct libhoth_spi_device*)dev->user_ctx; + // Mode settings can be sticky! Restore the original mode to ensure any bad + // modesetting does not presist across processes. + if (ioctl(spi_dev->fd, SPI_IOC_WR_MODE32, &spi_dev->original_mode) < 0) { + // We can't do much here, but we should at least close the fd. + perror("Failed to restore SPI mode"); + } close(spi_dev->fd); free(dev->user_ctx); return LIBHOTH_OK; diff --git a/transports/libhoth_spi.h b/transports/libhoth_spi.h index f0dac8a..1840d7f 100644 --- a/transports/libhoth_spi.h +++ b/transports/libhoth_spi.h @@ -25,6 +25,12 @@ extern "C" { struct libhoth_device; +enum libhoth_spi_mode { + LIBHOTH_SPI_MODE_SINGLE = 0, + LIBHOTH_SPI_MODE_DUAL, + LIBHOTH_SPI_MODE_QUAD, +}; + struct libhoth_spi_device_init_options { // The device filepath to open const char* path; @@ -34,6 +40,7 @@ struct libhoth_spi_device_init_options { int mode; int speed; int atomic; + enum libhoth_spi_mode operation_mode; uint32_t device_busy_wait_timeout; uint32_t device_busy_wait_check_interval; uint32_t timeout_us; @@ -43,6 +50,8 @@ struct libhoth_spi_device { int fd; unsigned int mailbox_address; bool address_mode_4b; + enum libhoth_spi_mode mode; + uint32_t original_mode; void* buffered_request; size_t buffered_request_size; @@ -55,7 +64,10 @@ enum { SPI_NOR_OPCODE_READ_STATUS = 0x05, SPI_NOR_OPCODE_WRITE_ENABLE = 0x06, SPI_NOR_OPCODE_PAGE_PROGRAM = 0x02, + SPI_NOR_OPCODE_QUAD_PAGE_PROGRAM = 0x38, SPI_NOR_OPCODE_SLOW_READ = 0x03, + SPI_NOR_OPCODE_DUAL_READ = 0x3B, + SPI_NOR_OPCODE_QUAD_READ = 0x6B, SPI_NOR_FLASH_PAGE_SIZE = 256, // in bytes };