diff --git a/LICENSE b/LICENSE index 425c003446..8913ecfb81 100644 --- a/LICENSE +++ b/LICENSE @@ -3842,6 +3842,7 @@ arch/arm/src/rp2040/rp2040_pio.h arch/arm/src/rp2040/rp2040_pio_instructions.h arch/arm/src/rp2040/rp2040_pll.c arch/arm/src/rp2040/rp2040_xosc.c +boards/arm/rp2040/common/src/rp2040_uniqueid.c ========================================================== Based upon the software originally developed by Raspberry Pi (Trading) Ltd. diff --git a/boards/arm/rp2040/common/include/rp2040_uniqueid.h b/boards/arm/rp2040/common/include/rp2040_uniqueid.h new file mode 100644 index 0000000000..46edcec3d8 --- /dev/null +++ b/boards/arm/rp2040/common/include/rp2040_uniqueid.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * boards/arm/rp2040/common/include/rp2040_uniqueid.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __BOARDS_ARM_RP2040_COMMON_INCLUDE_RP2040_UNIQUEID_H +#define __BOARDS_ARM_RP2040_COMMON_INCLUDE_RP2040_UNIQUEID_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RP2040_FLASH_ID_SIZE 8 +#define RP2040_FLASH_ID_BUFFER_SIZE 13 +#define RP2040_FLASH_ID_BUFFER_OFFSET 5 +#define RP2040_FLASH_RUID_CMD 0x4b + +#ifdef __cplusplus +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rp2040_uniqueid_initialize + * + * Description: + * The RP2040 doesn't have a unique ID, so we load the ID from the + * connected flash chip. We use the flash ID to seed a simple xorshift + * PRNG. The PRNG then generates CONFIG_BOARDCTL_UNIQUEID_SIZE bytes, + * which we will use as the board's unique ID. + * + * Retrieving the flash id is somewhat slow and complex, so we only do + * this during initialization and store the result for later use. + * + * Assumptions/Limitations: + * This uniqueid implementation requires a flash chip. It should not be + * used on boards without flash. + * + ****************************************************************************/ + +void rp2040_uniqueid_initialize(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __BOARDS_ARM_RP2040_COMMON_INCLUDE_RP2040_UNIQUEID_H */ \ No newline at end of file diff --git a/boards/arm/rp2040/common/src/Make.defs b/boards/arm/rp2040/common/src/Make.defs index f7f13bce68..43c8a11cbc 100644 --- a/boards/arm/rp2040/common/src/Make.defs +++ b/boards/arm/rp2040/common/src/Make.defs @@ -91,6 +91,10 @@ ifeq ($(CONFIG_LCD_BACKPACK),y) CSRCS += rp2040_lcd_backpack.c endif +ifeq ($(CONFIG_BOARDCTL_UNIQUEID),y) + CSRCS += rp2040_uniqueid.c +endif + DEPPATH += --dep-path src VPATH += :src CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src diff --git a/boards/arm/rp2040/common/src/rp2040_common_initialize.c b/boards/arm/rp2040/common/src/rp2040_common_initialize.c index 4a9058193f..d5bab584e6 100644 --- a/boards/arm/rp2040/common/src/rp2040_common_initialize.c +++ b/boards/arm/rp2040/common/src/rp2040_common_initialize.c @@ -31,6 +31,7 @@ #include "arm_internal.h" #include "rp2040_gpio.h" +#include "rp2040_uniqueid.h" /**************************************************************************** * Pre-processor Definitions @@ -123,6 +124,10 @@ void rp2040_common_earlyinitialize(void) void rp2040_common_initialize(void) { +#ifdef CONFIG_BOARDCTL_UNIQUEID + rp2040_uniqueid_initialize(); +#endif + /* Set default I2C pin */ #ifdef CONFIG_RP2040_I2C0 diff --git a/boards/arm/rp2040/common/src/rp2040_uniqueid.c b/boards/arm/rp2040/common/src/rp2040_uniqueid.c new file mode 100644 index 0000000000..653028c2bc --- /dev/null +++ b/boards/arm/rp2040/common/src/rp2040_uniqueid.c @@ -0,0 +1,341 @@ +/**************************************************************************** + * boards/arm/rp2040/common/src/rp2040_uniqueid.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include "rp2040_uniqueid.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ROM_FUNC_CIF ROM_TABLE_CODE('I', 'F') +#define ROM_FUNC_FEX ROM_TABLE_CODE('E', 'X') +#define ROM_FUNC_FFC ROM_TABLE_CODE('F', 'C') + +#define QSPI_SS_CTRL_OUTOVER_LSB 8 +#define QSPI_SS_CTRL_OUTOVER_VALUE_LOW 0x2 +#define QSPI_SS_CTRL_OUTOVER_VALUE_HIGH 0x3 +#define QSPI_SS_CTRL_OUTOVER_BITS 0x00000300 +#define QSPI_SS_CTRL 0x4001800c +#define XIP_BASE 0x10000000 +#define XIP_SSI_SR 0x18000028 +#define XIP_SSI_DR0 0x18000060 +#define SSI_SR_TFNF_BITS 0x00000002 +#define SSI_SR_RFNE_BITS 0x00000008 +#define BOOT2_SIZE_WORDS 64 +#define REG_ALIAS_XOR_BITS (0x1u << 12u) + +#define ROM_TABLE_CODE(c1, c2) ((c1) | ((c2) << 8)) +#define hw_alias_check_addr(addr) ((uintptr_t)(addr)) +#define hw_xor_alias_untyped(addr) ((void *)(REG_ALIAS_XOR_BITS | hw_alias_check_addr(addr))) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef volatile uint32_t io_rw_32; +typedef void (*rom_cif_fn)(void); +typedef void (*rom_fex_fn)(void); +typedef void (*rom_ffc_fn)(void); +typedef void (*rom_flash_enter_cmd_xip_fn)(void); +typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void __compiler_memory_barrier(void); +static inline void *rom_hword_as_ptr(uint16_t rom_address); +static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2); +static void *rf_lookup(uint32_t code); +static void hw_xor_bits(io_rw_32 *addr, uint32_t mask); +static void hw_write_masked(io_rw_32 *addr, + uint32_t values, + uint32_t write_mask); +static void flash_cs_force (bool high); +void rp2040_flash_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint8_t g_uniqueid[CONFIG_BOARDCTL_UNIQUEID_SIZE]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: __compiler_memory_barrier + * + * Description: + * Prevent compiler from moving memory access across this barrier. + * + ****************************************************************************/ + +static inline void __compiler_memory_barrier(void) +{ +} + +/**************************************************************************** + * Name: rom_hword_as_ptr + * + * Description: + * Converts a (well known) address value into a pointer to that address. + * + ****************************************************************************/ + +static inline void *rom_hword_as_ptr(uint16_t rom_address) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + return (void *)(uintptr_t)*(uint16_t *)(uintptr_t)rom_address; +#pragma GCC diagnostic pop +} + +/**************************************************************************** + * Name: + * + * Description: + * Return a bootrom lookup code based on two ASCII characters. + * + * Input Parameters: + * uint8_t c1: first character + * uint8_t c2: second character + * + * Returned Value: + * A code to use with rf_lookup + * + ****************************************************************************/ + +static inline uint32_t rom_table_code(uint8_t c1, uint8_t c2) +{ + return ROM_TABLE_CODE((uint32_t) c1, (uint32_t) c2); +} + +/**************************************************************************** + * Name: rf_lookup + * + * Description: + * Lookup a bootrom function by code. + * + * Input Parameters: + * uint32_t code: A code from rom_table_code() + * + * Returned Value: + * a pointer to the function, or NULL if the code does not match any + * bootrom function + * + ****************************************************************************/ + +always_inline_function static void *rf_lookup(uint32_t code) +{ + rom_table_lookup_fn rom_table_lookup; + rom_table_lookup = (rom_table_lookup_fn) rom_hword_as_ptr(0x18); + uint16_t *func_table = (uint16_t *) rom_hword_as_ptr(0x14); + return rom_table_lookup(func_table, code); +} + +/**************************************************************************** + * Name: hw_xor_bits + * + * Description: + * Helper function for flash_cs_force. + * + ****************************************************************************/ + +always_inline_function static void hw_xor_bits(io_rw_32 *addr, uint32_t mask) +{ + *(io_rw_32 *) hw_xor_alias_untyped((volatile void *) addr) = mask; +} + +/**************************************************************************** + * Name: hw_write_masked + * + * Description: + * Helper function for flash_cs_force. + * + ****************************************************************************/ + +always_inline_function static void hw_write_masked(io_rw_32 *addr, + uint32_t values, + uint32_t write_mask) +{ + hw_xor_bits(addr, (*addr ^ values) & write_mask); +} + +/**************************************************************************** + * Name: flash_cs_force + * + * Description: + * Override the chip select line to flash chip. + * + * Input Parameters: + * bool high: true to force CS high, false to force low + * + ****************************************************************************/ + +noinline_function locate_code(".ram_code.flash_cs_force") +static void flash_cs_force (bool high) +{ + uint32_t field_val = high ? + QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : + QSPI_SS_CTRL_OUTOVER_VALUE_LOW; + hw_write_masked((io_rw_32 *)QSPI_SS_CTRL, + field_val << QSPI_SS_CTRL_OUTOVER_LSB, + QSPI_SS_CTRL_OUTOVER_BITS + ); +} + +/**************************************************************************** + * Name: rp2040_flash_cmd + * + * Description: + * Send a command to flash chip and receive the result. + * + * Input Parameters: + * uint8_t* txbuf: Pointer to buffer to send + * uint8_t* rxbuf: Pointer to buffer to hold received value + * size_t count: Number of bytes to send / receive + * + ****************************************************************************/ + +noinline_function locate_code(".ram_code.rp2040_flash_cmd") +void rp2040_flash_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) +{ + rom_cif_fn connect_internal_flash = (rom_cif_fn)rf_lookup(ROM_FUNC_CIF); + rom_fex_fn flash_exit_xip = (rom_fex_fn)rf_lookup(ROM_FUNC_FEX); + rom_ffc_fn flash_flush_cache = (rom_ffc_fn)rf_lookup(ROM_FUNC_FFC); + + uint32_t boot2_copyout[BOOT2_SIZE_WORDS]; + for (int i = 0; i < BOOT2_SIZE_WORDS; ++i) + boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i]; + __compiler_memory_barrier(); + connect_internal_flash(); + flash_exit_xip(); + + flash_cs_force(0); + size_t tx_cnt = count; + size_t rx_cnt = count; + + const size_t max_in_flight = 16 - 2; + while (tx_cnt || rx_cnt) + { + uint32_t flags = *((uint32_t *)XIP_SSI_SR); + + bool can_put = !!(flags & SSI_SR_TFNF_BITS); + bool can_get = !!(flags & SSI_SR_RFNE_BITS); + + if (can_put && tx_cnt && rx_cnt - tx_cnt < max_in_flight) + { + *((uint8_t *)XIP_SSI_DR0) = *txbuf++; + --tx_cnt; + } + + if (can_get && rx_cnt) + { + *rxbuf++ = *((uint8_t *)XIP_SSI_DR0); + --rx_cnt; + } + } + + flash_cs_force(1); + flash_flush_cache(); + ((void (*)(void))((intptr_t)boot2_copyout + 1))(); /* re-enable xip */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp2040_uniqueid_initialize + * + * Description: + * The RP2040 doesn't have a unique ID, so we load the ID from the + * connected flash chip. We use the flash ID to seed a simple xorshift + * PRNG. The PRNG then generates CONFIG_BOARDCTL_UNIQUEID_SIZE bytes, + * which we will use as the board's unique ID. + * + * Retrieving the flash id is somewhat slow and complex, so we only do + * this during initialization and store the result for later use. + * + * Assumptions/Limitations: + * This uniqueid implementation requires a flash chip. It should not be + * used on boards without flash. + * + ****************************************************************************/ + +void rp2040_uniqueid_initialize(void) +{ + uint64_t x; + uint8_t txbuf[RP2040_FLASH_ID_BUFFER_SIZE]; + uint8_t rxbuf[RP2040_FLASH_ID_BUFFER_SIZE]; + + memset(g_uniqueid, 0xac, CONFIG_BOARDCTL_UNIQUEID_SIZE); + memset(txbuf, 0, RP2040_FLASH_ID_BUFFER_SIZE); + memset(rxbuf, 0, RP2040_FLASH_ID_BUFFER_SIZE); + txbuf[0] = RP2040_FLASH_RUID_CMD; + + rp2040_flash_cmd(txbuf, rxbuf, RP2040_FLASH_ID_BUFFER_SIZE); + + /* xorshift PRNG: */ + + x = *(uint64_t *)(rxbuf + RP2040_FLASH_ID_BUFFER_OFFSET); + for (int i = 0; i < CONFIG_BOARDCTL_UNIQUEID_SIZE; i++) + { + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + g_uniqueid[i] = (uint8_t)((x * 0x2545f4914f6cdd1dull) >> 32); + } +} + +/**************************************************************************** + * Name: board_uniqueid + * + * Description: + * Return a unique ID associated with the board. + * + * Input Parameters: + * uniqueid - A reference to a writable memory location provided by the + * caller to receive the board unique ID. The memory memory referenced + * by this pointer must be at least CONFIG_BOARDCTL_UNIQUEID_SIZE in + * length. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwize a negated errno value is + * returned indicating the nature of the failure. + * + ****************************************************************************/ + +int board_uniqueid(FAR uint8_t *uniqueid) +{ + memcpy(uniqueid, g_uniqueid, CONFIG_BOARDCTL_UNIQUEID_SIZE); + return OK; +}