Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

parisc: Add Page Deallocation Table (PDT) support

The firmare in most parisc machines maintains a Page Deallocation Table (PDT)
which holds a list of physical memory addresses where hardware detected memory
errors (single bit and double bit errors).

This patch adds the missing PDC firmware calls and the logic to read the PDT
from firmware, report all current PDT entries and exclude the reported bad
memory from being used by Linux.

Signed-off-by: Helge Deller <deller@gmx.de>

+317 -16
+18
arch/parisc/include/asm/pdc.h
··· 6 6 #if !defined(__ASSEMBLY__) 7 7 8 8 extern int pdc_type; 9 + extern unsigned long parisc_cell_num; /* cell number the CPU runs on (PAT) */ 10 + extern unsigned long parisc_cell_loc; /* cell location of CPU (PAT) */ 9 11 10 12 /* Values for pdc_type */ 11 13 #define PDC_TYPE_ILLEGAL -1 ··· 144 142 }; 145 143 146 144 #endif /* !CONFIG_PA20 */ 145 + 146 + struct pdc_mem_retinfo { /* PDC_MEM/PDC_MEM_MEMINFO (return info) */ 147 + unsigned long pdt_size; 148 + unsigned long pdt_entries; 149 + unsigned long pdt_status; 150 + unsigned long first_dbe_loc; 151 + unsigned long good_mem; 152 + }; 153 + 154 + struct pdc_mem_read_pdt { /* PDC_MEM/PDC_MEM_READ_PDT (return info) */ 155 + unsigned long pdt_entries; 156 + }; 147 157 148 158 #ifdef CONFIG_64BIT 149 159 struct pdc_memory_table_raddr { /* PDC_MEM/PDC_MEM_TABLE (return info) */ ··· 315 301 int pdc_tod_read(struct pdc_tod *tod); 316 302 int pdc_tod_set(unsigned long sec, unsigned long usec); 317 303 304 + void pdc_pdt_init(void); /* in pdt.c */ 305 + int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo); 306 + int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *rpdt_read, 307 + unsigned long *pdt_entries_ptr); 318 308 #ifdef CONFIG_64BIT 319 309 int pdc_mem_mem_table(struct pdc_memory_table_raddr *r_addr, 320 310 struct pdc_memory_table *tbl, unsigned long entries);
+26 -9
arch/parisc/include/asm/pdcpat.h
··· 147 147 #define PDC_PAT_MEM_CELL_CLEAR 6L /* Clear PDT For Cell */ 148 148 #define PDC_PAT_MEM_CELL_READ 7L /* Read PDT entries For Cell */ 149 149 #define PDC_PAT_MEM_CELL_RESET 8L /* Reset clear bit For Cell */ 150 - #define PDC_PAT_MEM_SETGM 9L /* Set Golden Memory value */ 151 - #define PDC_PAT_MEM_ADD_PAGE 10L /* ADDs a page to the cell */ 152 - #define PDC_PAT_MEM_ADDRESS 11L /* Get Physical Location From */ 150 + #define PDC_PAT_MEM_SETGM 9L /* Set Good Memory value */ 151 + #define PDC_PAT_MEM_ADD_PAGE 10L /* ADDs a page to the cell */ 152 + #define PDC_PAT_MEM_ADDRESS 11L /* Get Physical Location From */ 153 153 /* Memory Address */ 154 154 #define PDC_PAT_MEM_GET_TXT_SIZE 12L /* Get Formatted Text Size */ 155 155 #define PDC_PAT_MEM_GET_PD_TXT 13L /* Get PD Formatted Text */ ··· 211 211 unsigned long cpu_num; 212 212 unsigned long cpu_loc; 213 213 }; 214 + 215 + struct pdc_pat_mem_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_INFO (return info) */ 216 + unsigned int ke; /* bit 0: memory inside good memory? */ 217 + unsigned int current_pdt_entries:16; 218 + unsigned int max_pdt_entries:16; 219 + unsigned long Cs_bitmap; 220 + unsigned long Ic_bitmap; 221 + unsigned long good_mem; 222 + unsigned long first_dbe_loc; /* first location of double bit error */ 223 + unsigned long clear_time; /* last PDT clear time (since Jan 1970) */ 224 + }; 225 + 226 + struct pdc_pat_mem_read_pd_retinfo { /* PDC_PAT_MEM/PDC_PAT_MEM_PD_READ */ 227 + unsigned long actual_count_bytes; 228 + unsigned long pdt_entries; 229 + }; 230 + 214 231 215 232 struct pdc_pat_pd_addr_map_entry { 216 233 unsigned char entry_type; /* 1 = Memory Descriptor Entry Type */ ··· 310 293 311 294 extern int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, unsigned long count, unsigned long offset); 312 295 313 - 314 296 extern int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *val); 315 297 extern int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val); 316 298 317 - 318 - /* Flag to indicate this is a PAT box...don't use this unless you 319 - ** really have to...it might go away some day. 320 - */ 321 - extern int pdc_pat; /* arch/parisc/kernel/inventory.c */ 299 + extern int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo); 300 + extern int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, 301 + unsigned long *pdt_entries_ptr, unsigned long max_entries); 302 + extern int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, 303 + unsigned long *pdt_entries_ptr, unsigned long count, 304 + unsigned long offset); 322 305 323 306 #endif /* __ASSEMBLY__ */ 324 307
+3
arch/parisc/include/asm/pgtable.h
··· 511 511 512 512 #define pte_same(A,B) (pte_val(A) == pte_val(B)) 513 513 514 + struct seq_file; 515 + extern void arch_report_meminfo(struct seq_file *m); 516 + 514 517 #endif /* !__ASSEMBLY__ */ 515 518 516 519
+6 -6
arch/parisc/include/uapi/asm/pdc.h
··· 131 131 #define PDC_TLB_SETUP 1 /* set up miss handling */ 132 132 133 133 #define PDC_MEM 20 /* Manage memory */ 134 - #define PDC_MEM_MEMINFO 0 135 - #define PDC_MEM_ADD_PAGE 1 136 - #define PDC_MEM_CLEAR_PDT 2 137 - #define PDC_MEM_READ_PDT 3 138 - #define PDC_MEM_RESET_CLEAR 4 139 - #define PDC_MEM_GOODMEM 5 134 + #define PDC_MEM_MEMINFO 0 /* Return PDT info */ 135 + #define PDC_MEM_ADD_PAGE 1 /* Add page to PDT */ 136 + #define PDC_MEM_CLEAR_PDT 2 /* Clear PDT */ 137 + #define PDC_MEM_READ_PDT 3 /* Read PDT entry */ 138 + #define PDC_MEM_RESET_CLEAR 4 /* Reset PDT clear flag */ 139 + #define PDC_MEM_GOODMEM 5 /* Set good_mem value */ 140 140 #define PDC_MEM_TABLE 128 /* Non contig mem map (sprockets) */ 141 141 #define PDC_MEM_RETURN_ADDRESS_TABLE PDC_MEM_TABLE 142 142 #define PDC_MEM_GET_MEMORY_SYSTEM_TABLES_SIZE 131
+1 -1
arch/parisc/kernel/Makefile
··· 4 4 5 5 extra-y := head.o vmlinux.lds 6 6 7 - obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \ 7 + obj-y := cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \ 8 8 pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \ 9 9 ptrace.o hardware.o inventory.o drivers.o \ 10 10 signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
+108
arch/parisc/kernel/firmware.c
··· 957 957 } 958 958 EXPORT_SYMBOL(pdc_tod_read); 959 959 960 + int pdc_mem_pdt_info(struct pdc_mem_retinfo *rinfo) 961 + { 962 + int retval; 963 + unsigned long flags; 964 + 965 + spin_lock_irqsave(&pdc_lock, flags); 966 + retval = mem_pdc_call(PDC_MEM, PDC_MEM_MEMINFO, __pa(pdc_result), 0); 967 + convert_to_wide(pdc_result); 968 + memcpy(rinfo, pdc_result, sizeof(*rinfo)); 969 + spin_unlock_irqrestore(&pdc_lock, flags); 970 + 971 + return retval; 972 + } 973 + 974 + int pdc_mem_pdt_read_entries(struct pdc_mem_read_pdt *pret, 975 + unsigned long *pdt_entries_ptr) 976 + { 977 + int retval; 978 + unsigned long flags; 979 + 980 + spin_lock_irqsave(&pdc_lock, flags); 981 + retval = mem_pdc_call(PDC_MEM, PDC_MEM_READ_PDT, __pa(pdc_result), 982 + __pa(pdc_result2)); 983 + if (retval == PDC_OK) { 984 + convert_to_wide(pdc_result); 985 + memcpy(pret, pdc_result, sizeof(*pret)); 986 + convert_to_wide(pdc_result2); 987 + memcpy(pdt_entries_ptr, pdc_result2, 988 + pret->pdt_entries * sizeof(*pdt_entries_ptr)); 989 + } 990 + spin_unlock_irqrestore(&pdc_lock, flags); 991 + 992 + return retval; 993 + } 994 + 960 995 /** 961 996 * pdc_tod_set - Set the Time-Of-Day clock. 962 997 * @sec: The number of seconds since epoch. ··· 1414 1379 spin_lock_irqsave(&pdc_lock, flags); 1415 1380 retval = mem_pdc_call(PDC_PAT_IO, PDC_PAT_IO_PCI_CONFIG_WRITE, 1416 1381 pci_addr, pci_size, val); 1382 + spin_unlock_irqrestore(&pdc_lock, flags); 1383 + 1384 + return retval; 1385 + } 1386 + 1387 + /** 1388 + * pdc_pat_mem_pdc_info - Retrieve information about page deallocation table 1389 + * @rinfo: memory pdt information 1390 + * 1391 + */ 1392 + int pdc_pat_mem_pdt_info(struct pdc_pat_mem_retinfo *rinfo) 1393 + { 1394 + int retval; 1395 + unsigned long flags; 1396 + 1397 + spin_lock_irqsave(&pdc_lock, flags); 1398 + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_INFO, 1399 + __pa(&pdc_result)); 1400 + if (retval == PDC_OK) 1401 + memcpy(rinfo, &pdc_result, sizeof(*rinfo)); 1402 + spin_unlock_irqrestore(&pdc_lock, flags); 1403 + 1404 + return retval; 1405 + } 1406 + 1407 + /** 1408 + * pdc_pat_mem_read_cell_pdt - Read PDT entries from (old) PAT firmware 1409 + * @pret: array of PDT entries 1410 + * @pdt_entries_ptr: ptr to hold number of PDT entries 1411 + * @max_entries: maximum number of entries to be read 1412 + * 1413 + */ 1414 + int pdc_pat_mem_read_cell_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, 1415 + unsigned long *pdt_entries_ptr, unsigned long max_entries) 1416 + { 1417 + int retval; 1418 + unsigned long flags, entries; 1419 + 1420 + spin_lock_irqsave(&pdc_lock, flags); 1421 + /* PDC_PAT_MEM_CELL_READ is available on early PAT machines only */ 1422 + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_CELL_READ, 1423 + __pa(&pdc_result), parisc_cell_num, __pa(&pdc_result2)); 1424 + 1425 + if (retval == PDC_OK) { 1426 + /* build up return value as for PDC_PAT_MEM_PD_READ */ 1427 + entries = min(pdc_result[0], max_entries); 1428 + pret->pdt_entries = entries; 1429 + pret->actual_count_bytes = entries * sizeof(unsigned long); 1430 + memcpy(pdt_entries_ptr, &pdc_result2, pret->actual_count_bytes); 1431 + } 1432 + 1433 + spin_unlock_irqrestore(&pdc_lock, flags); 1434 + WARN_ON(retval == PDC_OK && pdc_result[0] > max_entries); 1435 + 1436 + return retval; 1437 + } 1438 + /** 1439 + * pdc_pat_mem_read_pd_pdt - Read PDT entries from (newer) PAT firmware 1440 + * @pret: array of PDT entries 1441 + * @pdt_entries_ptr: ptr to hold number of PDT entries 1442 + * 1443 + */ 1444 + int pdc_pat_mem_read_pd_pdt(struct pdc_pat_mem_read_pd_retinfo *pret, 1445 + unsigned long *pdt_entries_ptr, unsigned long count, 1446 + unsigned long offset) 1447 + { 1448 + int retval; 1449 + unsigned long flags; 1450 + 1451 + spin_lock_irqsave(&pdc_lock, flags); 1452 + retval = mem_pdc_call(PDC_PAT_MEM, PDC_PAT_MEM_PD_READ, 1453 + __pa(&pret), __pa(pdt_entries_ptr), 1454 + count, offset); 1417 1455 spin_unlock_irqrestore(&pdc_lock, flags); 1418 1456 1419 1457 return retval;
+9
arch/parisc/kernel/inventory.c
··· 40 40 41 41 int pdc_type __read_mostly = PDC_TYPE_ILLEGAL; 42 42 43 + /* cell number and location (PAT firmware only) */ 44 + unsigned long parisc_cell_num __read_mostly; 45 + unsigned long parisc_cell_loc __read_mostly; 46 + 47 + 43 48 void __init setup_pdc(void) 44 49 { 45 50 long status; ··· 83 78 if (status == PDC_OK) { 84 79 pdc_type = PDC_TYPE_PAT; 85 80 pr_cont("64 bit PAT.\n"); 81 + parisc_cell_num = cell_info.cell_num; 82 + parisc_cell_loc = cell_info.cell_loc; 83 + pr_info("PAT: Running on cell %lu and location %lu.\n", 84 + parisc_cell_num, parisc_cell_loc); 86 85 return; 87 86 } 88 87 #endif
+143
arch/parisc/kernel/pdt.c
··· 1 + /* 2 + * Page Deallocation Table (PDT) support 3 + * 4 + * The Page Deallocation Table (PDT) holds a table with pointers to bad 5 + * memory (broken RAM modules) which is maintained by firmware. 6 + * 7 + * Copyright 2017 by Helge Deller <deller@gmx.de> 8 + * 9 + * TODO: 10 + * - check regularily for new bad memory 11 + * - add userspace interface with procfs or sysfs 12 + * - increase number of PDT entries dynamically 13 + */ 14 + 15 + #include <linux/memblock.h> 16 + #include <linux/seq_file.h> 17 + 18 + #include <asm/pdc.h> 19 + #include <asm/pdcpat.h> 20 + #include <asm/sections.h> 21 + #include <asm/pgtable.h> 22 + 23 + enum pdt_access_type { 24 + PDT_NONE, 25 + PDT_PDC, 26 + PDT_PAT_NEW, 27 + PDT_PAT_OLD 28 + }; 29 + 30 + static enum pdt_access_type pdt_type; 31 + 32 + /* global PDT status information */ 33 + static struct pdc_mem_retinfo pdt_status; 34 + 35 + #define MAX_PDT_TABLE_SIZE PAGE_SIZE 36 + #define MAX_PDT_ENTRIES (MAX_PDT_TABLE_SIZE / sizeof(unsigned long)) 37 + static unsigned long pdt_entry[MAX_PDT_ENTRIES] __page_aligned_bss; 38 + 39 + 40 + /* report PDT entries via /proc/meminfo */ 41 + void arch_report_meminfo(struct seq_file *m) 42 + { 43 + if (pdt_type == PDT_NONE) 44 + return; 45 + 46 + seq_printf(m, "PDT_max_entries: %7lu\n", 47 + pdt_status.pdt_size); 48 + seq_printf(m, "PDT_cur_entries: %7lu\n", 49 + pdt_status.pdt_entries); 50 + } 51 + 52 + /* 53 + * pdc_pdt_init() 54 + * 55 + * Initialize kernel PDT structures, read initial PDT table from firmware, 56 + * report all current PDT entries and mark bad memory with memblock_reserve() 57 + * to avoid that the kernel will use broken memory areas. 58 + * 59 + */ 60 + void __init pdc_pdt_init(void) 61 + { 62 + int ret, i; 63 + unsigned long entries; 64 + struct pdc_mem_read_pdt pdt_read_ret; 65 + 66 + if (is_pdc_pat()) { 67 + struct pdc_pat_mem_retinfo pat_rinfo; 68 + 69 + pdt_type = PDT_PAT_NEW; 70 + ret = pdc_pat_mem_pdt_info(&pat_rinfo); 71 + pdt_status.pdt_size = pat_rinfo.max_pdt_entries; 72 + pdt_status.pdt_entries = pat_rinfo.current_pdt_entries; 73 + pdt_status.pdt_status = 0; 74 + pdt_status.first_dbe_loc = pat_rinfo.first_dbe_loc; 75 + pdt_status.good_mem = pat_rinfo.good_mem; 76 + } else { 77 + pdt_type = PDT_PDC; 78 + ret = pdc_mem_pdt_info(&pdt_status); 79 + } 80 + 81 + if (ret != PDC_OK) { 82 + pdt_type = PDT_NONE; 83 + pr_info("PDT: Firmware does not provide any page deallocation" 84 + " information.\n"); 85 + return; 86 + } 87 + 88 + entries = pdt_status.pdt_entries; 89 + WARN_ON(entries > MAX_PDT_ENTRIES); 90 + 91 + pr_info("PDT: size %lu, entries %lu, status %lu, dbe_loc 0x%lx," 92 + " good_mem %lu\n", 93 + pdt_status.pdt_size, pdt_status.pdt_entries, 94 + pdt_status.pdt_status, pdt_status.first_dbe_loc, 95 + pdt_status.good_mem); 96 + 97 + if (entries == 0) { 98 + pr_info("PDT: Firmware reports all memory OK.\n"); 99 + return; 100 + } 101 + 102 + if (pdt_status.first_dbe_loc && 103 + pdt_status.first_dbe_loc <= __pa((unsigned long)&_end)) 104 + pr_crit("CRITICAL: Bad memory inside kernel image memory area!\n"); 105 + 106 + pr_warn("PDT: Firmware reports %lu entries of faulty memory:\n", 107 + entries); 108 + 109 + if (pdt_type == PDT_PDC) 110 + ret = pdc_mem_pdt_read_entries(&pdt_read_ret, pdt_entry); 111 + else { 112 + #ifdef CONFIG_64BIT 113 + struct pdc_pat_mem_read_pd_retinfo pat_pret; 114 + 115 + ret = pdc_pat_mem_read_cell_pdt(&pat_pret, pdt_entry, 116 + MAX_PDT_ENTRIES); 117 + if (ret != PDC_OK) { 118 + pdt_type = PDT_PAT_OLD; 119 + ret = pdc_pat_mem_read_pd_pdt(&pat_pret, pdt_entry, 120 + MAX_PDT_TABLE_SIZE, 0); 121 + } 122 + #else 123 + ret = PDC_BAD_PROC; 124 + #endif 125 + } 126 + 127 + if (ret != PDC_OK) { 128 + pdt_type = PDT_NONE; 129 + pr_debug("PDT type %d, retval = %d\n", pdt_type, ret); 130 + return; 131 + } 132 + 133 + for (i = 0; i < pdt_status.pdt_entries; i++) { 134 + if (i < 20) 135 + pr_warn("PDT: BAD PAGE #%d at 0x%08lx (error_type = %lu)\n", 136 + i, 137 + pdt_entry[i] & PAGE_MASK, 138 + pdt_entry[i] & 1); 139 + 140 + /* mark memory page bad */ 141 + memblock_reserve(pdt_entry[i] & PAGE_MASK, PAGE_SIZE); 142 + } 143 + }
+3
arch/parisc/mm/init.c
··· 381 381 request_resource(res, &data_resource); 382 382 } 383 383 request_resource(&sysram_resources[0], &pdcdata_resource); 384 + 385 + /* Initialize Page Deallocation Table (PDT) and check for bad memory. */ 386 + pdc_pdt_init(); 384 387 } 385 388 386 389 static int __init parisc_text_address(unsigned long vaddr)