xref: /aosp_15_r20/external/coreboot/src/lib/ramtest.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdint.h>
4 #include <lib.h>
5 #include <console/console.h>
6 #include <device/mmio.h>
7 
8 #if ENV_X86 && CONFIG(SSE2)
9 /* Assembler in lib/ is ugly. */
write_phys(uintptr_t addr,u32 value)10 static void write_phys(uintptr_t addr, u32 value)
11 {
12 	asm volatile (
13 		"movnti %1, (%0)"
14 		: /* outputs */
15 		: "r" (addr), "r" (value) /* inputs */
16 	);
17 }
18 
phys_memory_barrier(void)19 static void phys_memory_barrier(void)
20 {
21 	// Needed for movnti
22 	asm volatile ("sfence" ::: "memory");
23 }
24 #else
write_phys(uintptr_t addr,u32 value)25 static void write_phys(uintptr_t addr, u32 value)
26 {
27 	write32p(addr, value);
28 }
29 
phys_memory_barrier(void)30 static void phys_memory_barrier(void)
31 {
32 	asm volatile ("" ::: "memory");
33 }
34 #endif
35 
read_phys(uintptr_t addr)36 static u32 read_phys(uintptr_t addr)
37 {
38 	return read32p(addr);
39 }
40 
41 /**
42  * Rotate ones test pattern that access every bit on a 128bit wide
43  * memory bus. To test most address lines, addresses are scattered
44  * using 256B, 4kB and 64kB increments.
45  *
46  * @param idx	Index to test pattern (0=<idx<0x400)
47  * @param addr	Memory to access on idx
48  * @param value	Value to write or read at addr
49  */
test_pattern(unsigned short int idx,unsigned long * addr,unsigned long * value)50 static inline void test_pattern(unsigned short int idx,
51 	unsigned long *addr, unsigned long *value)
52 {
53 	uint8_t j, k;
54 
55 	k = (idx >> 8) + 1;
56 	j = (idx >> 4) & 0x0f;
57 	*addr = idx & 0x0f;
58 	*addr |= j << (4*k);
59 	*value = 0x01010101 << (j & 7);
60 	if (j & 8)
61 		*value = ~(*value);
62 }
63 
64 /**
65  * Simple write-read-verify memory test. See console debug output for
66  * any dislocated bytes.
67  *
68  * Tests 1MiB of memory starting from start.
69  *
70  * @param start   System memory offset, aligned to 128bytes
71  */
ram_bitset_nodie(uintptr_t start)72 static int ram_bitset_nodie(uintptr_t start)
73 {
74 	unsigned long addr, value, value2;
75 	unsigned short int idx;
76 	unsigned char failed, failures;
77 	uint8_t verbose = 0;
78 
79 	printk(BIOS_DEBUG, "DRAM bitset write: 0x%08lx\n", start);
80 	for (idx = 0; idx < 0x400; idx += 4) {
81 		test_pattern(idx, &addr, &value);
82 		write_phys(start + addr, value);
83 	}
84 
85 	/* Make sure we don't read before we wrote */
86 	phys_memory_barrier();
87 
88 	printk(BIOS_DEBUG, "DRAM bitset verify: 0x%08lx\n", start);
89 	failures = 0;
90 	for (idx = 0; idx < 0x400; idx += 4) {
91 		test_pattern(idx, &addr, &value);
92 		value2 = read_phys(start + addr);
93 
94 		failed = (value2 != value);
95 		failures |= failed;
96 		if (failed && !verbose) {
97 			printk(BIOS_ERR, "0x%08lx wr: 0x%08lx rd: 0x%08lx FAIL\n",
98 				 start + addr, value, value2);
99 		}
100 		if (verbose) {
101 			if ((addr & 0x0f) == 0)
102 				printk(BIOS_DEBUG, "%08lx wr: %08lx rd:",
103 					start + addr, value);
104 			if (failed)
105 				printk(BIOS_DEBUG, " %08lx!", value2);
106 			else
107 				printk(BIOS_DEBUG, " %08lx ", value2);
108 			if ((addr & 0x0f) == 0xc)
109 				printk(BIOS_DEBUG, "\n");
110 		}
111 	}
112 	if (failures) {
113 		post_code(POSTCODE_RAM_FAILURE);
114 		printk(BIOS_DEBUG, "\nDRAM did _NOT_ verify!\n");
115 		return 1;
116 	}
117 	printk(BIOS_DEBUG, "\nDRAM range verified.\n");
118 	return 0;
119 }
120 
121 
ram_check(uintptr_t start)122 void ram_check(uintptr_t start)
123 {
124 	/*
125 	 * This is much more of a "Is my DRAM properly configured?"
126 	 * test than a "Is my DRAM faulty?" test.  Not all bits
127 	 * are tested.   -Tyson
128 	 */
129 	printk(BIOS_DEBUG, "Testing DRAM at: %08lx\n", start);
130 	if (ram_bitset_nodie(start))
131 		die("DRAM ERROR");
132 	printk(BIOS_DEBUG, "Done.\n");
133 }
134 
135 
ram_check_nodie(uintptr_t start)136 int ram_check_nodie(uintptr_t start)
137 {
138 	int ret;
139 	/*
140 	 * This is much more of a "Is my DRAM properly configured?"
141 	 * test than a "Is my DRAM faulty?" test.  Not all bits
142 	 * are tested.   -Tyson
143 	 */
144 	printk(BIOS_DEBUG, "Testing DRAM at : %08lx\n", start);
145 
146 	ret = ram_bitset_nodie(start);
147 	printk(BIOS_DEBUG, "Done.\n");
148 	return ret;
149 }
150 
ram_check_noprint_nodie(uintptr_t start)151 int ram_check_noprint_nodie(uintptr_t start)
152 {
153 	unsigned long addr, value, value2;
154 	unsigned short int idx;
155 	unsigned char failed, failures;
156 
157 	for (idx = 0; idx < 0x400; idx += 4) {
158 		test_pattern(idx, &addr, &value);
159 		write_phys(start + addr, value);
160 	}
161 
162 	/* Make sure we don't read before we wrote */
163 	phys_memory_barrier();
164 
165 	failures = 0;
166 	for (idx = 0; idx < 0x400; idx += 4) {
167 		test_pattern(idx, &addr, &value);
168 		value2 = read_phys(start + addr);
169 
170 		failed = (value2 != value);
171 		failures |= failed;
172 	}
173 	return failures;
174 }
175 
176 /* Assumption is 32-bit addressable UC memory at dst. This also executes
177  * on S3 resume path so target memory must be restored.
178  */
quick_ram_check_or_die(uintptr_t dst)179 void quick_ram_check_or_die(uintptr_t dst)
180 {
181 	int fail = 0;
182 	u32 backup;
183 	backup = read_phys(dst);
184 	write_phys(dst, 0x55555555);
185 	phys_memory_barrier();
186 	if (read_phys(dst) != 0x55555555)
187 		fail = 1;
188 	write_phys(dst, 0xaaaaaaaa);
189 	phys_memory_barrier();
190 	if (read_phys(dst) != 0xaaaaaaaa)
191 		fail = 1;
192 	write_phys(dst, 0x00000000);
193 	phys_memory_barrier();
194 	if (read_phys(dst) != 0x00000000)
195 		fail = 1;
196 	write_phys(dst, 0xffffffff);
197 	phys_memory_barrier();
198 	if (read_phys(dst) != 0xffffffff)
199 		fail = 1;
200 
201 	write_phys(dst, backup);
202 	if (fail) {
203 		post_code(POSTCODE_RAM_FAILURE);
204 		die("RAM INIT FAILURE!\n");
205 	}
206 	phys_memory_barrier();
207 }
208