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/nodemask.h> 15#include <asm/system.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 int dump_fit_entry(char *page, unsigned long *fentry) 106{ 107 unsigned type; 108 109 type = FIT_TYPE(fentry[1]); 110 return sprintf(page, "%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 129dump_fit(char *page, unsigned long nasid) 130{ 131 unsigned long fentry[2]; 132 int index; 133 char *p; 134 135 p = page; 136 for (index=0;;index++) { 137 BUG_ON(index * 60 > PAGE_SIZE); 138 if (get_fit_entry(nasid, index, fentry, NULL, 0)) 139 break; 140 p += dump_fit_entry(p, fentry); 141 } 142 143 return p - page; 144} 145 146static int 147dump_version(char *page, unsigned long nasid) 148{ 149 unsigned long fentry[2]; 150 char banner[128]; 151 int index; 152 int len; 153 154 for (index = 0; ; index++) { 155 if (get_fit_entry(nasid, index, fentry, banner, 156 sizeof(banner))) 157 return 0; 158 if (FIT_TYPE(fentry[1]) == FIT_ENTRY_SAL_A) 159 break; 160 } 161 162 len = sprintf(page, "%x.%02x\n", FIT_MAJOR(fentry[1]), 163 FIT_MINOR(fentry[1])); 164 page += len; 165 166 if (banner[0]) 167 len += snprintf(page, PAGE_SIZE-len, "%s\n", banner); 168 169 return len; 170} 171 172/* same as in proc_misc.c */ 173static int 174proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, 175 int len) 176{ 177 if (len <= off + count) 178 *eof = 1; 179 *start = page + off; 180 len -= off; 181 if (len > count) 182 len = count; 183 if (len < 0) 184 len = 0; 185 return len; 186} 187 188static int 189read_version_entry(char *page, char **start, off_t off, int count, int *eof, 190 void *data) 191{ 192 int len; 193 194 /* data holds the NASID of the node */ 195 len = dump_version(page, (unsigned long)data); 196 len = proc_calc_metrics(page, start, off, count, eof, len); 197 return len; 198} 199 200static int 201read_fit_entry(char *page, char **start, off_t off, int count, int *eof, 202 void *data) 203{ 204 int len; 205 206 /* data holds the NASID of the node */ 207 len = dump_fit(page, (unsigned long)data); 208 len = proc_calc_metrics(page, start, off, count, eof, len); 209 210 return len; 211} 212 213/* module entry points */ 214int __init prominfo_init(void); 215void __exit prominfo_exit(void); 216 217module_init(prominfo_init); 218module_exit(prominfo_exit); 219 220static struct proc_dir_entry **proc_entries; 221static struct proc_dir_entry *sgi_prominfo_entry; 222 223#define NODE_NAME_LEN 11 224 225int __init prominfo_init(void) 226{ 227 struct proc_dir_entry **entp; 228 struct proc_dir_entry *p; 229 cnodeid_t cnodeid; 230 unsigned long nasid; 231 int size; 232 char name[NODE_NAME_LEN]; 233 234 if (!ia64_platform_is("sn2")) 235 return 0; 236 237 size = num_online_nodes() * sizeof(struct proc_dir_entry *); 238 proc_entries = kzalloc(size, GFP_KERNEL); 239 if (!proc_entries) 240 return -ENOMEM; 241 242 sgi_prominfo_entry = proc_mkdir("sgi_prominfo", NULL); 243 244 entp = proc_entries; 245 for_each_online_node(cnodeid) { 246 sprintf(name, "node%d", cnodeid); 247 *entp = proc_mkdir(name, sgi_prominfo_entry); 248 nasid = cnodeid_to_nasid(cnodeid); 249 p = create_proc_read_entry("fit", 0, *entp, read_fit_entry, 250 (void *)nasid); 251 if (p) 252 p->owner = THIS_MODULE; 253 p = create_proc_read_entry("version", 0, *entp, 254 read_version_entry, (void *)nasid); 255 if (p) 256 p->owner = THIS_MODULE; 257 entp++; 258 } 259 260 return 0; 261} 262 263void __exit prominfo_exit(void) 264{ 265 struct proc_dir_entry **entp; 266 unsigned int cnodeid; 267 char name[NODE_NAME_LEN]; 268 269 entp = proc_entries; 270 for_each_online_node(cnodeid) { 271 remove_proc_entry("fit", *entp); 272 remove_proc_entry("version", *entp); 273 sprintf(name, "node%d", cnodeid); 274 remove_proc_entry(name, sgi_prominfo_entry); 275 entp++; 276 } 277 remove_proc_entry("sgi_prominfo", NULL); 278 kfree(proc_entries); 279} 280