Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *    Page Deallocation Table (PDT) support
  4 *
  5 *    The Page Deallocation Table (PDT) is maintained by firmware and holds a
  6 *    list of memory addresses in which memory errors were detected.
  7 *    The list contains both single-bit (correctable) and double-bit
  8 *    (uncorrectable) errors.
  9 *
 10 *    Copyright 2017 by Helge Deller <deller@gmx.de>
 11 *
 12 *    possible future enhancements:
 13 *    - add userspace interface via procfs or sysfs to clear PDT
 14 */
 15
 16#include <linux/memblock.h>
 17#include <linux/seq_file.h>
 18#include <linux/kthread.h>
 19#include <linux/initrd.h>
 20#include <linux/pgtable.h>
 21#include <linux/mm.h>
 22
 23#include <asm/pdc.h>
 24#include <asm/pdcpat.h>
 25#include <asm/sections.h>
 26
 27enum pdt_access_type {
 28	PDT_NONE,
 29	PDT_PDC,
 30	PDT_PAT_NEW,
 31	PDT_PAT_CELL
 32};
 33
 34static enum pdt_access_type pdt_type;
 35
 36/* PDT poll interval: 1 minute if errors, 5 minutes if everything OK. */
 37#define PDT_POLL_INTERVAL_DEFAULT	(5*60*HZ)
 38#define PDT_POLL_INTERVAL_SHORT		(1*60*HZ)
 39static unsigned long pdt_poll_interval = PDT_POLL_INTERVAL_DEFAULT;
 40
 41/* global PDT status information */
 42static struct pdc_mem_retinfo pdt_status;
 43
 44#define MAX_PDT_TABLE_SIZE	PAGE_SIZE
 45#define MAX_PDT_ENTRIES		(MAX_PDT_TABLE_SIZE / sizeof(unsigned long))
 46static unsigned long pdt_entry[MAX_PDT_ENTRIES] __page_aligned_bss;
 47
 48/*
 49 * Constants for the pdt_entry format:
 50 * A pdt_entry holds the physical address in bits 0-57, bits 58-61 are
 51 * reserved, bit 62 is the perm bit and bit 63 is the error_type bit.
 52 * The perm bit indicates whether the error have been verified as a permanent
 53 * error (value of 1) or has not been verified, and may be transient (value
 54 * of 0). The error_type bit indicates whether the error is a single bit error
 55 * (value of 1) or a multiple bit error.
 56 * On non-PAT machines phys_addr is encoded in bits 0-59 and error_type in bit
 57 * 63. Those machines don't provide the perm bit.
 58 */
 59
 60#define PDT_ADDR_PHYS_MASK	(pdt_type != PDT_PDC ? ~0x3f : ~0x0f)
 61#define PDT_ADDR_PERM_ERR	(pdt_type != PDT_PDC ? 2UL : 0UL)
 62#define PDT_ADDR_SINGLE_ERR	1UL
 63
 64/* report PDT entries via /proc/meminfo */
 65void arch_report_meminfo(struct seq_file *m)
 66{
 67	if (pdt_type == PDT_NONE)
 68		return;
 69
 70	seq_printf(m, "PDT_max_entries: %7lu\n",
 71			pdt_status.pdt_size);
 72	seq_printf(m, "PDT_cur_entries: %7lu\n",
 73			pdt_status.pdt_entries);
 74}
 75
 76static int get_info_pat_new(void)
 77{
 78	struct pdc_pat_mem_retinfo pat_rinfo;
 79	int ret;
 80
 81	/* newer PAT machines like C8000 report info for all cells */
 82	if (is_pdc_pat())
 83		ret = pdc_pat_mem_pdt_info(&pat_rinfo);
 84	else
 85		return PDC_BAD_PROC;
 86
 87	pdt_status.pdt_size = pat_rinfo.max_pdt_entries;
 88	pdt_status.pdt_entries = pat_rinfo.current_pdt_entries;
 89	pdt_status.pdt_status = 0;
 90	pdt_status.first_dbe_loc = pat_rinfo.first_dbe_loc;
 91	pdt_status.good_mem = pat_rinfo.good_mem;
 92
 93	return ret;
 94}
 95
 96static int get_info_pat_cell(void)
 97{
 98	struct pdc_pat_mem_cell_pdt_retinfo cell_rinfo;
 99	int ret;
100
101	/* older PAT machines like rp5470 report cell info only */
102	if (is_pdc_pat())
103		ret = pdc_pat_mem_pdt_cell_info(&cell_rinfo, parisc_cell_num);
104	else
105		return PDC_BAD_PROC;
106
107	pdt_status.pdt_size = cell_rinfo.max_pdt_entries;
108	pdt_status.pdt_entries = cell_rinfo.current_pdt_entries;
109	pdt_status.pdt_status = 0;
110	pdt_status.first_dbe_loc = cell_rinfo.first_dbe_loc;
111	pdt_status.good_mem = cell_rinfo.good_mem;
112
113	return ret;
114}
115
116static void report_mem_err(unsigned long pde)
117{
118	struct pdc_pat_mem_phys_mem_location loc;
119	unsigned long addr;
120	char dimm_txt[32];
121
122	addr = pde & PDT_ADDR_PHYS_MASK;
123
124	/* show DIMM slot description on PAT machines */
125	if (is_pdc_pat()) {
126		pdc_pat_mem_get_dimm_phys_location(&loc, addr);
127		sprintf(dimm_txt, "DIMM slot %02x, ", loc.dimm_slot);
128	} else
129		dimm_txt[0] = 0;
130
131	pr_warn("PDT: BAD MEMORY at 0x%08lx, %s%s%s-bit error.\n",
132		addr, dimm_txt,
133		pde & PDT_ADDR_PERM_ERR ? "permanent ":"",
134		pde & PDT_ADDR_SINGLE_ERR ? "single":"multi");
135}
136
137
138/*
139 * pdc_pdt_init()
140 *
141 * Initialize kernel PDT structures, read initial PDT table from firmware,
142 * report all current PDT entries and mark bad memory with memblock_reserve()
143 * to avoid that the kernel will use broken memory areas.
144 *
145 */
146void __init pdc_pdt_init(void)
147{
148	int ret, i;
149	unsigned long entries;
150	struct pdc_mem_read_pdt pdt_read_ret;
151
152	pdt_type = PDT_PAT_NEW;
153	ret = get_info_pat_new();
154
155	if (ret != PDC_OK) {
156		pdt_type = PDT_PAT_CELL;
157		ret = get_info_pat_cell();
158	}
159
160	if (ret != PDC_OK) {
161		pdt_type = PDT_PDC;
162		/* non-PAT machines provide the standard PDC call */
163		ret = pdc_mem_pdt_info(&pdt_status);
164	}
165
166	if (ret != PDC_OK) {
167		pdt_type = PDT_NONE;
168		pr_info("PDT: Firmware does not provide any page deallocation"
169			" information.\n");
170		return;
171	}
172
173	entries = pdt_status.pdt_entries;
174	if (WARN_ON(entries > MAX_PDT_ENTRIES))
175		entries = pdt_status.pdt_entries = MAX_PDT_ENTRIES;
176
177	pr_info("PDT: type %s, size %lu, entries %lu, status %lu, dbe_loc 0x%lx,"
178		" good_mem %lu MB\n",
179			pdt_type == PDT_PDC ? __stringify(PDT_PDC) :
180			pdt_type == PDT_PAT_CELL ? __stringify(PDT_PAT_CELL)
181						 : __stringify(PDT_PAT_NEW),
182			pdt_status.pdt_size, pdt_status.pdt_entries,
183			pdt_status.pdt_status, pdt_status.first_dbe_loc,
184			pdt_status.good_mem / 1024 / 1024);
185
186	if (entries == 0) {
187		pr_info("PDT: Firmware reports all memory OK.\n");
188		return;
189	}
190
191	if (pdt_status.first_dbe_loc &&
192		pdt_status.first_dbe_loc <= __pa((unsigned long)&_end))
193		pr_crit("CRITICAL: Bad memory inside kernel image memory area!\n");
194
195	pr_warn("PDT: Firmware reports %lu entries of faulty memory:\n",
196		entries);
197
198	if (pdt_type == PDT_PDC)
199		ret = pdc_mem_pdt_read_entries(&pdt_read_ret, pdt_entry);
200	else {
201#ifdef CONFIG_64BIT
202		struct pdc_pat_mem_read_pd_retinfo pat_pret;
203
204		if (pdt_type == PDT_PAT_CELL)
205			ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry,
206				MAX_PDT_ENTRIES);
207		else
208			ret = pdc_pat_mem_read_pd_pdt(&pat_pret, pdt_entry,
209				MAX_PDT_TABLE_SIZE, 0);
210#else
211		ret = PDC_BAD_PROC;
212#endif
213	}
214
215	if (ret != PDC_OK) {
216		pdt_type = PDT_NONE;
217		pr_warn("PDT: Get PDT entries failed with %d\n", ret);
218		return;
219	}
220
221	for (i = 0; i < pdt_status.pdt_entries; i++) {
222		unsigned long addr;
223
224		report_mem_err(pdt_entry[i]);
225
226		addr = pdt_entry[i] & PDT_ADDR_PHYS_MASK;
227		if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) &&
228			addr >= initrd_start && addr < initrd_end)
229			pr_crit("CRITICAL: initrd possibly broken "
230				"due to bad memory!\n");
231
232		/* mark memory page bad */
233		memblock_reserve(pdt_entry[i] & PAGE_MASK, PAGE_SIZE);
234		num_poisoned_pages_inc(addr >> PAGE_SHIFT);
235	}
236}
237
238
239/*
240 * This is the PDT kernel thread main loop.
241 */
242
243static int pdt_mainloop(void *unused)
244{
245	struct pdc_mem_read_pdt pdt_read_ret;
246	struct pdc_pat_mem_read_pd_retinfo pat_pret __maybe_unused;
247	unsigned long old_num_entries;
248	unsigned long *bad_mem_ptr;
249	int num, ret;
250
251	for (;;) {
252		set_current_state(TASK_INTERRUPTIBLE);
253
254		old_num_entries = pdt_status.pdt_entries;
255
256		schedule_timeout(pdt_poll_interval);
257		if (kthread_should_stop())
258			break;
259
260		/* Do we have new PDT entries? */
261		switch (pdt_type) {
262		case PDT_PAT_NEW:
263			ret = get_info_pat_new();
264			break;
265		case PDT_PAT_CELL:
266			ret = get_info_pat_cell();
267			break;
268		default:
269			ret = pdc_mem_pdt_info(&pdt_status);
270			break;
271		}
272
273		if (ret != PDC_OK) {
274			pr_warn("PDT: unexpected failure %d\n", ret);
275			return -EINVAL;
276		}
277
278		/* if no new PDT entries, just wait again */
279		num = pdt_status.pdt_entries - old_num_entries;
280		if (num <= 0)
281			continue;
282
283		/* decrease poll interval in case we found memory errors */
284		if (pdt_status.pdt_entries &&
285			pdt_poll_interval == PDT_POLL_INTERVAL_DEFAULT)
286			pdt_poll_interval = PDT_POLL_INTERVAL_SHORT;
287
288		/* limit entries to get */
289		if (num > MAX_PDT_ENTRIES) {
290			num = MAX_PDT_ENTRIES;
291			pdt_status.pdt_entries = old_num_entries + num;
292		}
293
294		/* get new entries */
295		switch (pdt_type) {
296#ifdef CONFIG_64BIT
297		case PDT_PAT_CELL:
298			if (pdt_status.pdt_entries > MAX_PDT_ENTRIES) {
299				pr_crit("PDT: too many entries.\n");
300				return -ENOMEM;
301			}
302			ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry,
303				MAX_PDT_ENTRIES);
304			bad_mem_ptr = &pdt_entry[old_num_entries];
305			break;
306		case PDT_PAT_NEW:
307			ret = pdc_pat_mem_read_pd_pdt(&pat_pret,
308				pdt_entry,
309				num * sizeof(unsigned long),
310				old_num_entries * sizeof(unsigned long));
311			bad_mem_ptr = &pdt_entry[0];
312			break;
313#endif
314		default:
315			ret = pdc_mem_pdt_read_entries(&pdt_read_ret,
316				pdt_entry);
317			bad_mem_ptr = &pdt_entry[old_num_entries];
318			break;
319		}
320
321		/* report and mark memory broken */
322		while (num--) {
323			unsigned long pde = *bad_mem_ptr++;
324
325			report_mem_err(pde);
326
327#ifdef CONFIG_MEMORY_FAILURE
328			if ((pde & PDT_ADDR_PERM_ERR) ||
329			    ((pde & PDT_ADDR_SINGLE_ERR) == 0))
330				memory_failure(pde >> PAGE_SHIFT, 0);
331			else
332				soft_offline_page(pde >> PAGE_SHIFT, 0);
333#else
334			pr_crit("PDT: memory error at 0x%lx ignored.\n"
335				"Rebuild kernel with CONFIG_MEMORY_FAILURE=y "
336				"for real handling.\n",
337				pde & PDT_ADDR_PHYS_MASK);
338#endif
339
340		}
341	}
342
343	return 0;
344}
345
346
347static int __init pdt_initcall(void)
348{
349	struct task_struct *kpdtd_task;
350
351	if (pdt_type == PDT_NONE)
352		return -ENODEV;
353
354	kpdtd_task = kthread_run(pdt_mainloop, NULL, "kpdtd");
355	if (IS_ERR(kpdtd_task))
356		return PTR_ERR(kpdtd_task);
357
358	return 0;
359}
360
361late_initcall(pdt_initcall);