diff --git a/src/examples/hardware-id/main.go b/src/examples/hardware-id/main.go new file mode 100644 index 0000000000..5276521c45 --- /dev/null +++ b/src/examples/hardware-id/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "encoding/hex" + "machine" + "time" +) + +func main() { + time.Sleep(2 * time.Second) + + id := machine.HardwareID() + + for { + println("Hardware ID:", hex.EncodeToString(id)) + time.Sleep(1 * time.Second) + } +} diff --git a/src/machine/hwid.go b/src/machine/hwid.go new file mode 100644 index 0000000000..8adf1b2469 --- /dev/null +++ b/src/machine/hwid.go @@ -0,0 +1,17 @@ +//go:build rp2040 || nrf || sam + +package machine + +// HardwareID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same HardwareID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) and 16 bytes (128 bits) are common. +var _ = (func() []byte)(HardwareID) diff --git a/src/machine/machine_atsam.go b/src/machine/machine_atsam.go new file mode 100644 index 0000000000..4b7de18375 --- /dev/null +++ b/src/machine/machine_atsam.go @@ -0,0 +1,31 @@ +//go:build sam + +package machine + +import ( + "runtime/volatile" + "unsafe" +) + +// HardwareID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same HardwareID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) and 16 bytes (128 bits) are common. +func HardwareID() []byte { + id := make([]byte, 16) + + for i := 0; i < len(id); i++ { + word := (*volatile.Register32)(unsafe.Pointer(hardwareIDAddr[i/4])).Get() + id[i] = byte(word >> ((i % 4) * 8)) + } + + return id +} diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index 3ad93dccf0..e918ba4cef 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -18,6 +18,9 @@ import ( const deviceName = sam.Device +// DS40001882F, Section 10.3.3: Serial Number +var hardwareIDAddr = []uintptr{0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048} + const ( PinAnalog PinMode = 1 PinSERCOM PinMode = 2 diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index bdc00c9aff..bfcfc40f7d 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -18,6 +18,9 @@ import ( const deviceName = sam.Device +// DS60001507, Section 9.6: Serial Number +var hardwareIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018} + func CPUFrequency() uint32 { return 120000000 } diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 3b79e376a3..a669276605 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -12,6 +12,34 @@ import ( const deviceName = nrf.Device +// HardwareID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same HardwareID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) is common. +func HardwareID() []byte { + id := make([]byte, 8) + + words := make([]uint32, 2) + words[0] = nrf.FICR.DEVICEID[0].Get() + words[1] = nrf.FICR.DEVICEID[1].Get() + + for i := 0; i < 8; i++ { + shift := (i % 4) * 8 + w := i / 4 + id[i] = byte(words[w] >> shift) + } + + return id +} + const ( PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) diff --git a/src/machine/machine_rp2040_flash.go b/src/machine/machine_rp2040_flash.go index f3d24e8e7d..d9d1d0def8 100644 --- a/src/machine/machine_rp2040_flash.go +++ b/src/machine/machine_rp2040_flash.go @@ -13,6 +13,33 @@ func EnterBootloader() { enterBootloader() } +// HardwareID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same HardwareID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) is common. +func HardwareID() []byte { + // 13 = 1 + FLASH_RUID_DUMMY_BYTES(4) + FLASH_RUID_DATA_BYTES(8) + txBuf := make([]byte, 13) + rxBuf := make([]byte, 13) + + txBuf[0] = 0x4b // FLASH_RUID_CMD + + err := doFlashCommand(txBuf, rxBuf) + if err != nil { + panic(err) + } + + return rxBuf[5:13] +} + // compile-time check for ensuring we fulfill BlockDevice interface var _ BlockDevice = flashBlockDevice{} diff --git a/src/machine/machine_rp2040_rom.go b/src/machine/machine_rp2040_rom.go index fdb05bb627..b2cd896f98 100644 --- a/src/machine/machine_rp2040_rom.go +++ b/src/machine/machine_rp2040_rom.go @@ -93,6 +93,33 @@ static ram_func void flash_enable_xip_via_boot2() { ((void (*)(void))boot2_copyout+1)(); } +#define IO_QSPI_BASE 0x40018000 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS 0x00000300 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_MSB 9 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB 8 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW 0x2 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH 0x3 + +#define XIP_SSI_BASE 0x18000000 +#define ssi_hw ((ssi_hw_t *)XIP_SSI_BASE) +#define SSI_SR_OFFSET 0x00000028 +#define SSI_DR0_OFFSET 0x00000060 +#define SSI_SR_TFNF_BITS 0x00000002 +#define SSI_SR_RFNE_BITS 0x00000008 + +void ram_func flash_cs_force(bool high) { + uint32_t field_val = high ? + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; + + // &ioqspi_hw->io[1].ctrl + uint32_t *addr = (uint32_t*)(IO_QSPI_BASE + (1 * 8) + 4); + + *addr = ((*addr) & !IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS) + | (field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB); + +} + // See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_flash/flash.c#L86 void ram_func flash_range_write(uint32_t offset, const uint8_t *data, size_t count) { @@ -132,6 +159,42 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count) flash_enable_xip_via_boot2(); } +void ram_func flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) { + flash_connect_internal_fn flash_connect_internal_func = (flash_connect_internal_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH); + flash_exit_xip_fn flash_exit_xip_func = (flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP); + flash_flush_cache_fn flash_flush_cache_func = (flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE); + + flash_init_boot2_copyout(); + + __compiler_memory_barrier(); + + flash_connect_internal_func(); + flash_exit_xip_func(); + + flash_cs_force(0); + size_t tx_remaining = count; + size_t rx_remaining = count; + // We may be interrupted -- don't want FIFO to overflow if we're distracted. + const size_t max_in_flight = 16 - 2; + while (tx_remaining || rx_remaining) { + uint32_t flags = *(uint32_t*)(XIP_SSI_BASE + SSI_SR_OFFSET); + bool can_put = !!(flags & SSI_SR_TFNF_BITS); + bool can_get = !!(flags & SSI_SR_RFNE_BITS); + if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) { + *(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET) = *txbuf++; + --tx_remaining; + } + if (can_get && rx_remaining) { + *rxbuf++ = (uint8_t)*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET); + --rx_remaining; + } + } + flash_cs_force(1); + + flash_flush_cache_func(); + flash_enable_xip_via_boot2(); +} + */ import "C" @@ -139,6 +202,19 @@ func enterBootloader() { C.reset_usb_boot(0, 0) } +func doFlashCommand(tx []byte, rx []byte) error { + if len(tx) != len(rx) { + return errFlashInvalidWriteLength + } + + C.flash_do_cmd( + (*C.uint8_t)(unsafe.Pointer(&tx[0])), + (*C.uint8_t)(unsafe.Pointer(&rx[0])), + C.ulong(len(tx))) + + return nil +} + // Flash related code const memoryStart = C.XIP_BASE // memory start for purpose of erase