[U-Boot] [PATCH] sunxi: Fix buggy sun6i/sun8i DRAM size detection logic
Hans de Goede
hdegoede at redhat.com
Sat Dec 27 18:05:25 CET 2014
Hi,
On 24-12-14 17:58, Siarhei Siamashka wrote:
> After reboot, reset or even short power off, DRAM typically retains
> the old stale data for some period of time (for this type of memory,
> the bits of data are stored in slowly discharging capacitors).
>
> The current sun6i/sun8i DRAM size detection logic, which is
> inherited from the Allwinner code, relies on using a large magic
> signature with the hope that it is unique enough and unlikely to
> ever accidentally match this leftover garbage data in RAM. But
> this approach is inherently unsafe, as can be demonstrated using
> the following test program:
>
> /***** A testcase for reproducing the problem ******/
> #include <stdlib.h>
> #include <stdio.h>
> #include <stdint.h>
>
> void main(int argc, char *argv[])
> {
> size_t size, i;
> uint32_t *buf;
> /* Allocate the buffer */
> if (argc < 2 || !(size = (size_t)atoi(argv[1]) * 1048576) ||
> !(buf = malloc(size))) {
> printf("Need buffer size in MiB as a cmdline argument\n");
> exit(1);
> }
> /* Fill it with the Allwinner DRAM "magic" values */
> for (i = 0; i < size / 4; i++)
> buf[i] = 0xaa55aa55 + ((uintptr_t)&buf[i] / 4) % 64;
> /* Try to reboot */
> system("reboot");
> /* And wait */
> for (;;) {}
> }
> /***************************************************/
>
> If this test program is run on the device (giving it a large
> chunk of memory), then the DRAM size detection logic in u-boot
> gets confused after reboot and fails to initialize DRAM properly.
>
> A better approach is not to rely on luck and abstain from making
> any assumptions about the properties of the leftover garbage
> data in RAM. Instead just use a more reliable code for testing
> whether two different addresses refer to the same memory location.
>
> Signed-off-by: Siarhei Siamashka <siarhei.siamashka at gmail.com>
Thanks for the patch. I've run various tests and it works well,
queued up for merging in u-boot-sunxi/next .
Regards,
Hans
> ---
>
> Done only very limited testing on MSI Primo81 tablet (Allwinner A31s),
> which is currently a rather impractical device for doing any sun6i code
> development due to having no access to the serial console, USB or other
> convenient ways to interact with the device.
>
> It might be a good idea to backup/restore the data in RAM when doing
> this check in the code.
>
> Using the standard u-boot 'get_ram_size' function could be also an
> option to replace the loops and simplify the sun6i/sun8i dram code
> in the future. The only inconvenience is that 'get_ram_size' returns
> 'size' instead of 'log2(size)'. This could be probably resolved by
> introducing a new 'get_ram_size_log2' common function. But since
> I don't have any Allwinner A23/A31 devboard for testing/debugging,
> don't expect me to do this work any time soon, or ever at all.
>
> arch/arm/cpu/armv7/sunxi/dram_sun6i.c | 1 -
> arch/arm/cpu/armv7/sunxi/dram_sun8i.c | 1 -
> arch/arm/include/asm/arch-sunxi/dram.h | 22 ++++++----------------
> 3 files changed, 6 insertions(+), 18 deletions(-)
>
> diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c
> index 4675c48..4518b80 100644
> --- a/arch/arm/cpu/armv7/sunxi/dram_sun6i.c
> +++ b/arch/arm/cpu/armv7/sunxi/dram_sun6i.c
> @@ -377,7 +377,6 @@ unsigned long sunxi_dram_init(void)
> MCTL_CR_BANK(1) | MCTL_CR_RANK(1));
>
> /* Detect and set page size */
> - mctl_mem_fill();
> for (columns = 7; columns < 20; columns++) {
> if (mctl_mem_matches(1 << columns))
> break;
> diff --git a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c
> index df9ff1f..ebba53b 100644
> --- a/arch/arm/cpu/armv7/sunxi/dram_sun8i.c
> +++ b/arch/arm/cpu/armv7/sunxi/dram_sun8i.c
> @@ -289,7 +289,6 @@ unsigned long sunxi_dram_init(void)
> writel(0x000310f4 | MCTL_CR_PAGE_SIZE(page_size),
> &mctl_com->cr);
> setbits_le32(&mctl_com->swonr, 0x0003ffff);
> - mctl_mem_fill();
> for (rows = 11; rows < 16; rows++) {
> offset = 1 << (rows + columns + bus);
> if (mctl_mem_matches(offset))
> diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h
> index 8d78029..7ff43e6 100644
> --- a/arch/arm/include/asm/arch-sunxi/dram.h
> +++ b/arch/arm/include/asm/arch-sunxi/dram.h
> @@ -24,8 +24,6 @@
> #include <asm/arch/dram_sun4i.h>
> #endif
>
> -#define MCTL_MEM_FILL_MATCH_COUNT 64
> -
> unsigned long sunxi_dram_init(void);
>
> /*
> @@ -42,24 +40,16 @@ static inline void mctl_await_completion(u32 *reg, u32 mask, u32 val)
> }
>
> /*
> - * Fill beginning of DRAM with "random" data for mctl_mem_matches()
> - */
> -static inline void mctl_mem_fill(void)
> -{
> - int i;
> -
> - for (i = 0; i < MCTL_MEM_FILL_MATCH_COUNT; i++)
> - writel(0xaa55aa55 + i, CONFIG_SYS_SDRAM_BASE + i * 4);
> -}
> -
> -/*
> * Test if memory at offset offset matches memory at begin of DRAM
> */
> static inline bool mctl_mem_matches(u32 offset)
> {
> - return memcmp((u32 *)CONFIG_SYS_SDRAM_BASE,
> - (u32 *)(CONFIG_SYS_SDRAM_BASE + offset),
> - MCTL_MEM_FILL_MATCH_COUNT * 4) == 0;
> + /* Try to write different values to RAM at two addresses */
> + writel(0, CONFIG_SYS_SDRAM_BASE);
> + writel(0xaa55aa55, CONFIG_SYS_SDRAM_BASE + offset);
> + /* Check if the same value is actually observed when reading back */
> + return readl(CONFIG_SYS_SDRAM_BASE) ==
> + readl(CONFIG_SYS_SDRAM_BASE + offset);
> }
>
> #endif /* _SUNXI_DRAM_H */
>
More information about the U-Boot
mailing list