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