1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1999,2001-2004, 2006 Silicon Graphics, Inc. All Rights Reserved. 7 * 8 * Module to export the system's Firmware Interface Tables, including 9 * PROM revision numbers and banners, in /proc 10 */ 11#include <linux/module.h> 12#include <linux/slab.h> 13#include <linux/proc_fs.h> 14#include <linux/seq_file.h> 15#include <linux/nodemask.h> 16#include <asm/io.h> 17#include <asm/sn/sn_sal.h> 18#include <asm/sn/sn_cpuid.h> 19#include <asm/sn/addrs.h> 20 21MODULE_DESCRIPTION("PROM version reporting for /proc"); 22MODULE_AUTHOR("Chad Talbott"); 23MODULE_LICENSE("GPL"); 24 25/* Standard Intel FIT entry types */ 26#define FIT_ENTRY_FIT_HEADER 0x00 /* FIT header entry */ 27#define FIT_ENTRY_PAL_B 0x01 /* PAL_B entry */ 28/* Entries 0x02 through 0x0D reserved by Intel */ 29#define FIT_ENTRY_PAL_A_PROC 0x0E /* Processor-specific PAL_A entry */ 30#define FIT_ENTRY_PAL_A 0x0F /* PAL_A entry, same as... */ 31#define FIT_ENTRY_PAL_A_GEN 0x0F /* ...Generic PAL_A entry */ 32#define FIT_ENTRY_UNUSED 0x7F /* Unused (reserved by Intel?) */ 33/* OEM-defined entries range from 0x10 to 0x7E. */ 34#define FIT_ENTRY_SAL_A 0x10 /* SAL_A entry */ 35#define FIT_ENTRY_SAL_B 0x11 /* SAL_B entry */ 36#define FIT_ENTRY_SALRUNTIME 0x12 /* SAL runtime entry */ 37#define FIT_ENTRY_EFI 0x1F /* EFI entry */ 38#define FIT_ENTRY_FPSWA 0x20 /* embedded fpswa entry */ 39#define FIT_ENTRY_VMLINUX 0x21 /* embedded vmlinux entry */ 40 41#define FIT_MAJOR_SHIFT (32 + 8) 42#define FIT_MAJOR_MASK ((1 << 8) - 1) 43#define FIT_MINOR_SHIFT 32 44#define FIT_MINOR_MASK ((1 << 8) - 1) 45 46#define FIT_MAJOR(q) \ 47 ((unsigned) ((q) >> FIT_MAJOR_SHIFT) & FIT_MAJOR_MASK) 48#define FIT_MINOR(q) \ 49 ((unsigned) ((q) >> FIT_MINOR_SHIFT) & FIT_MINOR_MASK) 50 51#define FIT_TYPE_SHIFT (32 + 16) 52#define FIT_TYPE_MASK ((1 << 7) - 1) 53 54#define FIT_TYPE(q) \ 55 ((unsigned) ((q) >> FIT_TYPE_SHIFT) & FIT_TYPE_MASK) 56 57struct fit_type_map_t { 58 unsigned char type; 59 const char *name; 60}; 61 62static const struct fit_type_map_t fit_entry_types[] = { 63 {FIT_ENTRY_FIT_HEADER, "FIT Header"}, 64 {FIT_ENTRY_PAL_A_GEN, "Generic PAL_A"}, 65 {FIT_ENTRY_PAL_A_PROC, "Processor-specific PAL_A"}, 66 {FIT_ENTRY_PAL_A, "PAL_A"}, 67 {FIT_ENTRY_PAL_B, "PAL_B"}, 68 {FIT_ENTRY_SAL_A, "SAL_A"}, 69 {FIT_ENTRY_SAL_B, "SAL_B"}, 70 {FIT_ENTRY_SALRUNTIME, "SAL runtime"}, 71 {FIT_ENTRY_EFI, "EFI"}, 72 {FIT_ENTRY_VMLINUX, "Embedded Linux"}, 73 {FIT_ENTRY_FPSWA, "Embedded FPSWA"}, 74 {FIT_ENTRY_UNUSED, "Unused"}, 75 {0xff, "Error"}, 76}; 77 78static const char *fit_type_name(unsigned char type) 79{ 80 struct fit_type_map_t const *mapp; 81 82 for (mapp = fit_entry_types; mapp->type != 0xff; mapp++) 83 if (type == mapp->type) 84 return mapp->name; 85 86 if ((type > FIT_ENTRY_PAL_A) && (type < FIT_ENTRY_UNUSED)) 87 return "OEM type"; 88 if ((type > FIT_ENTRY_PAL_B) && (type < FIT_ENTRY_PAL_A)) 89 return "Reserved"; 90 91 return "Unknown type"; 92} 93 94static int 95get_fit_entry(unsigned long nasid, int index, unsigned long *fentry, 96 char *banner, int banlen) 97{ 98 return ia64_sn_get_fit_compt(nasid, index, fentry, banner, banlen); 99} 100 101 102/* 103 * These two routines display the FIT table for each node. 104 */ 105static void dump_fit_entry(struct seq_file *m, unsigned long *fentry) 106{ 107 unsigned type; 108 109 type = FIT_TYPE(fentry[1]); 110 seq_printf(m, "%02x %-25s %x.%02x %016lx %u\n", 111 type, 112 fit_type_name(type), 113 FIT_MAJOR(fentry[1]), FIT_MINOR(fentry[1]), 114 fentry[0], 115 /* mult by sixteen to get size in bytes */ 116 (unsigned)(fentry[1] & 0xffffff) * 16); 117} 118 119 120/* 121 * We assume that the fit table will be small enough that we can print 122 * the whole thing into one page. (This is true for our default 16kB 123 * pages -- each entry is about 60 chars wide when printed.) I read 124 * somewhere that the maximum size of the FIT is 128 entries, so we're 125 * OK except for 4kB pages (and no one is going to do that on SN 126 * anyway). 127 */ 128static int proc_fit_show(struct seq_file *m, void *v) 129{ 130 unsigned long nasid = (unsigned long)m->private; 131 unsigned long fentry[2]; 132 int index; 133 134 for (index=0;;index++) { 135 BUG_ON(index * 60 > PAGE_SIZE); 136 if (get_fit_entry(nasid, index, fentry, NULL, 0)) 137 break; 138 dump_fit_entry(m, fentry); 139 } 140 return 0; 141} 142 143static int proc_fit_open(struct inode *inode, struct file *file) 144{ 145 return single_open(file, proc_fit_show, PDE_DATA(inode)); 146} 147 148static const struct file_operations proc_fit_fops = { 149 .open = proc_fit_open, 150 .read = seq_read, 151 .llseek = seq_lseek, 152 .release = single_release, 153}; 154 155static int proc_version_show(struct seq_file *m, void *v) 156{ 157 unsigned long nasid = (unsigned long)m->private; 158 unsigned long fentry[2]; 159 char banner[128]; 160 int index; 161 162 for (index = 0; ; index++) { 163 if (get_fit_entry(nasid, index, fentry, banner, 164 sizeof(banner))) 165 return 0; 166 if (FIT_TYPE(fentry[1]) == FIT_ENTRY_SAL_A) 167 break; 168 } 169 170 seq_printf(m, "%x.%02x\n", FIT_MAJOR(fentry[1]), FIT_MINOR(fentry[1])); 171 172 if (banner[0]) 173 seq_printf(m, "%s\n", banner); 174 return 0; 175} 176 177static int proc_version_open(struct inode *inode, struct file *file) 178{ 179 return single_open(file, proc_version_show, PDE_DATA(inode)); 180} 181 182static const struct file_operations proc_version_fops = { 183 .open = proc_version_open, 184 .read = seq_read, 185 .llseek = seq_lseek, 186 .release = single_release, 187}; 188 189/* module entry points */ 190int __init prominfo_init(void); 191void __exit prominfo_exit(void); 192 193module_init(prominfo_init); 194module_exit(prominfo_exit); 195 196#define NODE_NAME_LEN 11 197 198int __init prominfo_init(void) 199{ 200 struct proc_dir_entry *sgi_prominfo_entry; 201 cnodeid_t cnodeid; 202 203 if (!ia64_platform_is("sn2")) 204 return 0; 205 206 sgi_prominfo_entry = proc_mkdir("sgi_prominfo", NULL); 207 if (!sgi_prominfo_entry) 208 return -ENOMEM; 209 210 for_each_online_node(cnodeid) { 211 struct proc_dir_entry *dir; 212 unsigned long nasid; 213 char name[NODE_NAME_LEN]; 214 215 sprintf(name, "node%d", cnodeid); 216 dir = proc_mkdir(name, sgi_prominfo_entry); 217 if (!dir) 218 continue; 219 nasid = cnodeid_to_nasid(cnodeid); 220 proc_create_data("fit", 0, dir, 221 &proc_fit_fops, (void *)nasid); 222 proc_create_data("version", 0, dir, 223 &proc_version_fops, (void *)nasid); 224 } 225 return 0; 226} 227 228void __exit prominfo_exit(void) 229{ 230 remove_proc_subtree("sgi_prominfo", NULL); 231} 232