diff --git a/fel_lib.c b/fel_lib.c index b67238e..9a02a2d 100644 --- a/fel_lib.c +++ b/fel_lib.c @@ -368,6 +368,73 @@ void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count } } +/* + * Memory access to the SID (root) keys proved to be unreliable for certain + * SoCs. This function uses an alternative, register-based approach to retrieve + * the values. + */ +static void fel_get_sid_registers(feldev_handle *dev, uint32_t *result) +{ + uint32_t arm_code[] = { + htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */ + htole32(0xe3a01000), /* 4: mov r1, #0 */ + htole32(0xe28f303c), /* 8: add r3, pc, #60 */ + /* : */ + htole32(0xe1a02801), /* c: lsl r2, r1, #16 */ + htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */ + htole32(0xe3822002), /* 14: orr r2, r2, #2 */ + htole32(0xe5802040), /* 18: str r2, [r0, #64] */ + /* : */ + htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */ + htole32(0xe3120002), /* 20: tst r2, #2 */ + htole32(0x1afffffc), /* 24: bne 1c */ + htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */ + htole32(0xe7832001), /* 2c: str r2, [r3, r1] */ + htole32(0xe2811004), /* 30: add r1, r1, #4 */ + htole32(0xe3510010), /* 34: cmp r1, #16 */ + htole32(0x3afffff3), /* 38: bcc c */ + htole32(0xe3a02000), /* 3c: mov r2, #0 */ + htole32(0xe5802040), /* 40: str r2, [r0, #64] */ + htole32(0xe12fff1e), /* 44: bx lr */ + htole32(dev->soc_info->sid_base), /* SID base addr */ + /* retrieved SID values go here */ + }; + /* write and execute code */ + aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); + aw_fel_execute(dev, dev->soc_info->scratch_addr); + /* read back the result */ + aw_fel_read(dev, dev->soc_info->scratch_addr + sizeof(arm_code), + result, 4 * sizeof(uint32_t)); + for (unsigned i = 0; i < 4; i++) + result[i] = le32toh(result[i]); +} + +/* Read the SID "root" key (128 bits). You need to pass the device handle, + * a pointer to a result array capable of receiving at least four 32-bit words, + * and a flag specifying if the register-access workaround should be enforced. + * Return value indicates whether the result is expected to be usable: + * The function will return `false` (and zero the result) if it cannot access + * the SID registers. + */ +bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result, + bool force_workaround) +{ + if (!dev->soc_info->sid_base) { + /* SID unavailable */ + for (unsigned i = 0; i < 4; i++) result[i] = 0; + return false; + } + + if (dev->soc_info->sid_fix || force_workaround) + /* Work around SID issues by using ARM thunk code */ + fel_get_sid_registers(dev, result); + else + /* Read SID directly from memory */ + fel_readl_n(dev, dev->soc_info->sid_base + + dev->soc_info->sid_offset, result, 4); + return true; +} + /* general functions, "FEL device" management */ static int feldev_get_endpoint(feldev_handle *dev) @@ -601,10 +668,7 @@ feldev_list_entry *list_fel_devices(size_t *count) strncpy(entry->soc_name, dev->soc_name, sizeof(soc_name_t)); /* retrieve SID bits */ - if (dev->soc_info->sid_base) - aw_fel_readl_n(dev, - dev->soc_info->sid_base + dev->soc_info->sid_offset, - entry->SID, 4); + fel_get_root_key(dev, entry->SID, false); feldev_close(dev); free(dev); diff --git a/fel_lib.h b/fel_lib.h index 28d0884..ed43a82 100644 --- a/fel_lib.h +++ b/fel_lib.h @@ -67,4 +67,8 @@ void aw_fel_execute(feldev_handle *dev, uint32_t offset); void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count); void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count); +/* retrieve SID root key */ +bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result, + bool force_workaround); + #endif /* _SUNXI_TOOLS_FEL_LIB_H */ diff --git a/sid_read_root.S b/sid_read_root.S new file mode 100644 index 0000000..9fd04b1 --- /dev/null +++ b/sid_read_root.S @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 Bernhard Nortmann + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * ARM thunk code to read the SID root key using register-based access. + * + * This is necessary for certain SoCs (e.g. H3), as the values read via + * memory mapping might not be consistent. For background information see + * https://groups.google.com/forum/#!topic/linux-sunxi/ynyIP8c61Qs + */ + +SID_BASE .req r0 +sid_key_index .req r1 + +.set SID_PRCTL, 0x40 /* SID program/read control register */ +.set SID_PRKEY, 0x50 /* SID program key value register */ +.set SID_RDKEY, 0x60 /* SID read key value register */ + +.set SID_OP_LOCK, 0xAC /* Efuse operation lock value */ +.set SID_READ_START, (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */ +.set SID_PG_START, (1 << 0) /* bit 0 of SID_PRCTL, Software Program Start */ + +sid_read_root_key: + ldr SID_BASE, sid_base + mov sid_key_index, #0 + adr r3, sid_result /* result pointer */ +sid_read_loop: + mov r2, sid_key_index, lsl #16 /* PG_INDEX value */ + orr r2, #SID_OP_LOCK << 8 /* OP_LOCK to enable SID_READ_START */ + orr r2, #SID_READ_START + str r2, [SID_BASE, #SID_PRCTL] /* write SID_PRCTL */ +sid_read_wait: + ldr r2, [SID_BASE, #SID_PRCTL] /* read SID_PRCTL */ + tst r2, #SID_READ_START /* check if read operation completed */ + bne sid_read_wait /* loop while bit 1 still set */ + + ldr r2, [SID_BASE, #SID_RDKEY] /* read SID key value */ + str r2, [r3, sid_key_index] /* store SID value */ + + add sid_key_index, #4 + cmp sid_key_index, #16 + blo sid_read_loop /* loop while (sid_key_index < 0x10) */ + + mov r2, #0 + str r2, [SID_BASE, #SID_PRCTL] /* clear SID_PRCTL */ + bx lr + +sid_base: .word 0 +sid_result: .word 0 /* receives the four "root key" 32-bit words */ + .word 0 + .word 0 + .word 0 diff --git a/sid_read_root.h b/sid_read_root.h new file mode 100644 index 0000000..48596bb --- /dev/null +++ b/sid_read_root.h @@ -0,0 +1,24 @@ + /* : */ + htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */ + htole32(0xe3a01000), /* 4: mov r1, #0 */ + htole32(0xe28f303c), /* 8: add r3, pc, #60 */ + /* : */ + htole32(0xe1a02801), /* c: lsl r2, r1, #16 */ + htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */ + htole32(0xe3822002), /* 14: orr r2, r2, #2 */ + htole32(0xe5802040), /* 18: str r2, [r0, #64] */ + /* : */ + htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */ + htole32(0xe3120002), /* 20: tst r2, #2 */ + htole32(0x1afffffc), /* 24: bne 1c */ + htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */ + htole32(0xe7832001), /* 2c: str r2, [r3, r1] */ + htole32(0xe2811004), /* 30: add r1, r1, #4 */ + htole32(0xe3510010), /* 34: cmp r1, #16 */ + htole32(0x3afffff3), /* 38: bcc c */ + htole32(0xe3a02000), /* 3c: mov r2, #0 */ + htole32(0xe5802040), /* 40: str r2, [r0, #64] */ + htole32(0xe12fff1e), /* 44: bx lr */ + /* : */ + htole32(0x00000000), /* 48: .word 0x00000000 */ + /* : */