root/lm-sensors/trunk/prog/dump/isadump.c

Revision 5961, 8.2 KB (checked in by khali, 10 months ago)

isadump: Add support for word (16-bit) and long (32-bit) reads

Sometimes the hardware expects 16-bit or 32-bit reads rather than byte
reads. Add support to isadump so that the user can ask for such reads.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2    isadump.c - isadump, a user-space program to dump ISA registers
3    Copyright (C) 2000  Frodo Looijaard <frodol@dds.nl>, and
4                        Mark D. Studebaker <mdsxyz123@yahoo.com>
5    Copyright (C) 2004-2011  Jean Delvare <khali@linux-fr.org>
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20    MA 02110-1301 USA.
21*/
22
23/*
24        Typical usage:
25        isadump 0x295 0x296             Basic winbond dump using address/data registers
26        isadump 0x295 0x296 2           Winbond dump, bank 2
27        isadump 0x2e 0x2f 0x09          Super-I/O, logical device 9
28        isadump -f 0x5000               Flat address space dump like for Via 686a
29        isadump -f 0xecf0 0x10 1        PC87366, temperature channel 2
30*/
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <string.h>
36#include "util.h"
37#include "superio.h"
38
39
40/* To keep glibc2 happy */
41#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 0
42#include <sys/io.h>
43#else
44#include <asm/io.h>
45#endif
46
47#ifdef __powerpc__
48unsigned long isa_io_base = 0; /* XXX for now */
49#endif /* __powerpc__ */
50
51static void help(void)
52{
53        fprintf(stderr,
54                "Syntax for I2C-like access:\n"
55                "  isadump [OPTIONS] [-k V1,V2...] ADDRREG DATAREG [BANK [BANKREG]]\n"
56                "Syntax for flat address space:\n"
57                "  isadump -f [OPTIONS] ADDRESS [RANGE [BANK [BANKREG]]]\n"
58                "Options:\n"
59                "  -k   Super-I/O configuration access key\n"
60                "  -f   Enable flat address space mode\n"
61                "  -y   Assume affirmative answer to all questions\n"
62                "  -W   Read and display word (16-bit) values\n"
63                "  -L   Read and display long (32-bit) values\n");
64}
65
66static int default_bankreg(int flat, int addrreg, int datareg)
67{
68        if (flat) {
69                return 0x09; /* Works for National Semiconductor
70                                Super-IO chips */
71        }
72
73        if ((addrreg == 0x2e && datareg == 0x2f)
74         || (addrreg == 0x4e && datareg == 0x4f)) {
75                return 0x07; /* Works for all Super-I/O chips */
76        }
77       
78        return 0x4e; /* Works for Winbond ISA chips, default */
79}
80
81static int set_bank(int flat, int addrreg, int datareg, int bank, int bankreg)
82{
83        int oldbank;
84
85        if (flat) {
86                oldbank = inb(addrreg+bankreg);
87                outb(bank, addrreg+bankreg);
88        } else {
89                outb(bankreg, addrreg);
90                oldbank = inb(datareg);
91                outb(bank, datareg);
92        }
93
94        return oldbank;
95}
96
97int main(int argc, char *argv[])
98{
99        int addrreg;        /* address in flat mode */
100        int datareg = 0;    /* unused in flat mode */
101        int range = 256;    /* can be changed only in flat mode */
102        int bank = -1;      /* -1 means no bank operation */
103        int bankreg;
104        int oldbank = 0;
105        int i, j;
106        unsigned long res;
107        int flags = 0;
108        int flat = 0, yes = 0, width = 1;
109        char *end;
110        unsigned char enter_key[SUPERIO_MAX_KEY+1];
111
112        enter_key[0] = 0;
113
114        /* handle (optional) flags first */
115        while (1+flags < argc && argv[1+flags][0] == '-') {
116                switch (argv[1+flags][1]) {
117                case 'f': flat = 1; break;
118                case 'y': yes = 1; break;
119                case 'k':
120                        if (2+flags >= argc
121                         || superio_parse_key(enter_key, argv[2+flags]) < 0) {
122                                fprintf(stderr, "Invalid or missing key\n");
123                                help();
124                                exit(1);
125                        }
126                        flags++;
127                        break;
128                case 'W': width = 2; break;
129                case 'L': width = 4; break;
130                default:
131                        fprintf(stderr, "Warning: Unsupported flag "
132                                "\"-%c\"!\n", argv[1+flags][1]);
133                        help();
134                        exit(1);
135                }
136                flags++;
137        }
138
139        /* key is never needed in flat mode */
140        if (flat && enter_key[0]) {
141                fprintf(stderr, "Error: Cannot use key in flat mode\n");
142                exit(1);
143        }
144
145        /* verify that the argument count is correct */
146        if ((!flat && argc < 1+flags+2)
147         || (flat && argc < 1+flags+1)) {
148                help();
149                exit(1);
150        }
151
152        addrreg = strtol(argv[1+flags], &end, 0);
153        if (*end) {
154                fprintf(stderr, "Error: Invalid address!\n");
155                help();
156                exit(1);
157        }
158        if (addrreg < 0 || addrreg > (flat?0xffff:0x3fff)) {
159                fprintf(stderr, "Error: Address out of range "
160                        "(0x0000-0x%04x)!\n", flat?0xffff:0x3fff);
161                help();
162                exit(1);
163        }
164
165        if (flat) {
166                if (1+flags+1 < argc) {
167                        range = strtol(argv[1+flags+1], &end, 0);
168                        if (*end || range <= 0 || range > 0x100
169                         || range & 0xf) {
170                                fprintf(stderr, "Error: Invalid range!\n"
171                                        "Hint: Must be a multiple of 16 no "
172                                        "greater than 256.\n");
173                                help();
174                                exit(1);
175                        }
176                } else {
177                        addrreg &= 0xff00; /* Force alignment */
178                }
179        } else {
180                datareg = strtol(argv[1+flags+1], &end, 0);
181                if (*end) {
182                        fprintf(stderr, "Error: Invalid data register!\n");
183                        help();
184                        exit(1);
185                }
186                if (datareg < 0 || datareg > 0x3fff) {
187                        fprintf(stderr, "Error: Data register out of range "
188                                "(0x0000-0x3fff)!\n");
189                        help();
190                        exit(1);
191                }
192        }
193
194        bankreg = default_bankreg(flat, addrreg, datareg);
195
196        if (1+flags+2 < argc) {
197                bank = strtol(argv[1+flags+2], &end, 0);
198                if (*end) {
199                        fprintf(stderr, "Error: Invalid bank number!\n");
200                        help();
201                        exit(1);
202                }
203                if ((bank < 0) || (bank > 31)) {
204                        fprintf(stderr, "Error: bank out of range (0-31)!\n");
205                        help();
206                        exit(1);
207                }
208
209                if (1+flags+3 < argc) {
210                        bankreg = strtol(argv[1+flags+3], &end, 0);
211                        if (*end) {
212                                fprintf(stderr, "Error: Invalid bank "
213                                        "register!\n");
214                                help();
215                                exit(1);
216                        }
217                        if (bankreg < 0 || bankreg >= range) {
218                                fprintf(stderr, "Error: bank out of range "
219                                        "(0x00-0x%02x)!\n", range-1);
220                                help();
221                                exit(1);
222                        }
223                }
224        }
225
226        if (geteuid()) {
227                fprintf(stderr, "Error: Can only be run as root (or make it "
228                        "suid root)\n");
229                exit(1);
230        }
231
232        if (!yes) {
233                fprintf(stderr, "WARNING! Running this program can cause "
234                        "system crashes, data loss and worse!\n");
235
236                if (flat)
237                        fprintf(stderr, "I will probe address range 0x%x to "
238                                "0x%x.\n", addrreg, addrreg + range - 1);
239                else
240                        fprintf(stderr, "I will probe address register 0x%x "
241                                "and data register 0x%x.\n", addrreg, datareg);
242
243                if (bank>=0)   
244                        fprintf(stderr, "Probing bank %d using bank register "
245                                "0x%02x.\n", bank, bankreg);
246
247                fprintf(stderr, "Continue? [Y/n] ");
248                fflush(stderr);
249                if (!user_ack(1)) {
250                        fprintf(stderr, "Aborting on user request.\n");
251                        exit(0);
252                }
253        }
254
255#ifndef __powerpc__
256        if ((datareg < 0x400) && (addrreg < 0x400) && !flat) {
257                if (ioperm(datareg, 1, 1)) {
258                        fprintf(stderr, "Error: Could not ioperm() data "
259                                "register!\n");
260                        exit(1);
261                }
262                if (ioperm(addrreg, 1, 1)) {
263                        fprintf(stderr, "Error: Could not ioperm() address "
264                                "register!\n");
265                        exit(1);
266                }
267        } else {
268                if (iopl(3)) {
269                        fprintf(stderr, "Error: Could not do iopl(3)!\n");
270                        exit(1);
271                }
272        }
273#endif
274
275        /* Enter Super-I/O configuration mode */
276        if (enter_key[0])
277                superio_write_key(addrreg, enter_key);
278
279        if (bank >= 0)
280                oldbank = set_bank(flat, addrreg, datareg, bank, bankreg);
281
282        /* print column headers */
283        printf("%*s", flat ? 5 : 3, "");
284        for (j = 0; j < 16; j += width)
285                printf(" %*x", width * 2, j);
286        printf("\n");
287
288        for (i = 0; i < range; i += 16) {
289                if (flat)
290                        printf("%04x: ", addrreg + i);
291                else
292                        printf("%02x: ", i);
293
294                /* It was noticed that Winbond Super-I/O chips
295                   would leave the configuration mode after
296                   an arbitrary number of register reads,
297                   causing any subsequent read attempt to
298                   silently fail. Repeating the key every 16 reads
299                   prevents that. */
300                if (enter_key[0])
301                        superio_write_key(addrreg, enter_key);
302
303                for (j = 0; j < 16; j += width) {
304                        fflush(stdout);
305                        if (flat) {
306                                res = inx(addrreg + i + j, width);
307                        } else {       
308                                outb(i+j, addrreg);
309                                if (i+j == 0 && inb(addrreg) == 0x80) {
310                                        /* Bit 7 appears to be a busy flag */
311                                        range = 128;
312                                }
313                                res = inx(datareg, width);
314                        }
315                        printf("%0*lx ", width * 2, res);
316                }
317                printf("\n");
318        }
319
320        /* Restore the original bank value */
321        if (bank >= 0)
322                set_bank(flat, addrreg, datareg, oldbank, bankreg);
323
324        /* Exit Super-I/O configuration mode */
325        if (enter_key[0])
326                superio_reset(addrreg, datareg);
327
328        exit(0);
329}
Note: See TracBrowser for help on using the browser.