/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #if ENV_X86 && CONFIG(SSE2) /* Assembler in lib/ is ugly. */ static void write_phys(uintptr_t addr, u32 value) { asm volatile ( "movnti %1, (%0)" : /* outputs */ : "r" (addr), "r" (value) /* inputs */ ); } static void phys_memory_barrier(void) { // Needed for movnti asm volatile ("sfence" ::: "memory"); } #else static void write_phys(uintptr_t addr, u32 value) { write32p(addr, value); } static void phys_memory_barrier(void) { asm volatile ("" ::: "memory"); } #endif static u32 read_phys(uintptr_t addr) { return read32p(addr); } /** * Rotate ones test pattern that access every bit on a 128bit wide * memory bus. To test most address lines, addresses are scattered * using 256B, 4kB and 64kB increments. * * @param idx Index to test pattern (0=> 8) + 1; j = (idx >> 4) & 0x0f; *addr = idx & 0x0f; *addr |= j << (4*k); *value = 0x01010101 << (j & 7); if (j & 8) *value = ~(*value); } /** * Simple write-read-verify memory test. See console debug output for * any dislocated bytes. * * Tests 1MiB of memory starting from start. * * @param start System memory offset, aligned to 128bytes */ static int ram_bitset_nodie(uintptr_t start) { unsigned long addr, value, value2; unsigned short int idx; unsigned char failed, failures; uint8_t verbose = 0; printk(BIOS_DEBUG, "DRAM bitset write: 0x%08lx\n", start); for (idx = 0; idx < 0x400; idx += 4) { test_pattern(idx, &addr, &value); write_phys(start + addr, value); } /* Make sure we don't read before we wrote */ phys_memory_barrier(); printk(BIOS_DEBUG, "DRAM bitset verify: 0x%08lx\n", start); failures = 0; for (idx = 0; idx < 0x400; idx += 4) { test_pattern(idx, &addr, &value); value2 = read_phys(start + addr); failed = (value2 != value); failures |= failed; if (failed && !verbose) { printk(BIOS_ERR, "0x%08lx wr: 0x%08lx rd: 0x%08lx FAIL\n", start + addr, value, value2); } if (verbose) { if ((addr & 0x0f) == 0) printk(BIOS_DEBUG, "%08lx wr: %08lx rd:", start + addr, value); if (failed) printk(BIOS_DEBUG, " %08lx!", value2); else printk(BIOS_DEBUG, " %08lx ", value2); if ((addr & 0x0f) == 0xc) printk(BIOS_DEBUG, "\n"); } } if (failures) { post_code(POSTCODE_RAM_FAILURE); printk(BIOS_DEBUG, "\nDRAM did _NOT_ verify!\n"); return 1; } printk(BIOS_DEBUG, "\nDRAM range verified.\n"); return 0; } void ram_check(uintptr_t start) { /* * This is much more of a "Is my DRAM properly configured?" * test than a "Is my DRAM faulty?" test. Not all bits * are tested. -Tyson */ printk(BIOS_DEBUG, "Testing DRAM at: %08lx\n", start); if (ram_bitset_nodie(start)) die("DRAM ERROR"); printk(BIOS_DEBUG, "Done.\n"); } int ram_check_nodie(uintptr_t start) { int ret; /* * This is much more of a "Is my DRAM properly configured?" * test than a "Is my DRAM faulty?" test. Not all bits * are tested. -Tyson */ printk(BIOS_DEBUG, "Testing DRAM at : %08lx\n", start); ret = ram_bitset_nodie(start); printk(BIOS_DEBUG, "Done.\n"); return ret; } int ram_check_noprint_nodie(uintptr_t start) { unsigned long addr, value, value2; unsigned short int idx; unsigned char failed, failures; for (idx = 0; idx < 0x400; idx += 4) { test_pattern(idx, &addr, &value); write_phys(start + addr, value); } /* Make sure we don't read before we wrote */ phys_memory_barrier(); failures = 0; for (idx = 0; idx < 0x400; idx += 4) { test_pattern(idx, &addr, &value); value2 = read_phys(start + addr); failed = (value2 != value); failures |= failed; } return failures; } /* Assumption is 32-bit addressable UC memory at dst. This also executes * on S3 resume path so target memory must be restored. */ void quick_ram_check_or_die(uintptr_t dst) { int fail = 0; u32 backup; backup = read_phys(dst); write_phys(dst, 0x55555555); phys_memory_barrier(); if (read_phys(dst) != 0x55555555) fail = 1; write_phys(dst, 0xaaaaaaaa); phys_memory_barrier(); if (read_phys(dst) != 0xaaaaaaaa) fail = 1; write_phys(dst, 0x00000000); phys_memory_barrier(); if (read_phys(dst) != 0x00000000) fail = 1; write_phys(dst, 0xffffffff); phys_memory_barrier(); if (read_phys(dst) != 0xffffffff) fail = 1; write_phys(dst, backup); if (fail) { post_code(POSTCODE_RAM_FAILURE); die("RAM INIT FAILURE!\n"); } phys_memory_barrier(); }