RP2040: Implement board_uniqueid()

This commit is contained in:
Adam Comley 2024-03-14 21:55:30 -05:00 committed by Alan Carvalho de Assis
parent 7e07d23af1
commit b4407b529a
5 changed files with 420 additions and 0 deletions

View File

@ -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.

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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 <nuttx/config.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#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: <Static function 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;
}