91062d363cf8d9909b8415257eab4e05622b9e42
[rrq/maintain_lilo.git] / src / probe.c
1 /* probe.c -- BIOS probes */
2 /*
3 Copyright 1999-2006 John Coffman.
4 All rights reserved.
5
6 Licensed under the terms contained in the file 'COPYING' in the 
7 source directory.
8
9 */
10
11 /*#define DEBUG_PROBE*/
12 #define BITMAP 0        /* change to 1 when do_bitmap is filled in */
13 #define VOLID  1        /* change to 1 when do_volid is filled in */
14
15 #define _GNU_SOURCE
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include "config.h"
26 #include "lilo.h"
27 #include "common.h"
28 #include "device.h"
29 #include "geometry.h"
30 #include "partition.h"
31 #include "bsect.h"
32 #include "bdata.h"
33 #include "probe.h"
34
35
36 #ifdef LCF_BDATA
37 #if BD_MAX_FLOPPY > 4
38 #error "too many floppies in  bdata.h"
39 #endif
40 #if BD_MAX_HARD > 16
41 #error "too many hard disks in  bdata.h"
42 #endif
43 #if BD_GET_VIDEO > 3
44 #error "video get level set too high in  bdata.h"
45 #endif
46 #endif
47
48
49 static union Buf {
50    unsigned char b[5*SECTOR_SIZE];
51    struct {
52       short checksum[2];        /* prevent alignment on *4 boundary */
53       char signature[4];
54       short version;
55       short length;
56       unsigned char disk;       /* device code of last good disk */
57       unsigned char vid, mflp, mhrd;
58       short floppy;             /* byte offset to floppy data    */
59       short hard;               /* byte offset to hard disk data */
60       short partitions;         /* byte offset to partition info */
61       video_t v;
62       floppy_t f[4];
63       hard_t d;
64 /*      edd_t edd; */
65    } s4;
66    struct {
67       short checksum[2];        /* prevent alignment on *4 boundary */
68       char signature[4];
69       short version;
70       short length;
71       unsigned char disk;       /* device code of last good disk */
72       unsigned char vid, mflp, mhrd;
73       short floppy;             /* byte offset to floppy data    */
74       short hard;               /* byte offset to hard disk data */
75       short partitions;         /* byte offset to partition info */
76 /* version 5 additions  */
77       short equipment;          /* byte offset to the equipment information */
78       short video;              /* byte offset to the video information */
79    } s5;
80 } buf;
81
82 static equip_t *eq;
83 static video_t1 *v1;
84 static video_t2 *v2;
85 static video_t25 *v25;  /* extension for PROBE_VERSION 5 */
86 static video_t3 *v3;
87
88 static int video_36_bug;
89 static int buf_valid = -1;
90 static hard_t *hdp[16+1] =              /* pointers to all the hard disks */
91         {       NULL, NULL, NULL, NULL, 
92                 NULL, NULL, NULL, NULL, 
93                 NULL, NULL, NULL, NULL, 
94                 NULL, NULL, NULL, NULL,  NULL   };
95 static char warned[16];
96
97 static void do_ebda(void);
98 static void do_cr_pr(void);
99 static void do_help(void);
100 static void do_geom(char *bios);
101 static void do_geom_all(void);
102 static void do_table(char *part);
103 static void do_video(void);
104 static void do_bios(void);
105 #if BITMAP
106 static void do_bitmap(char *file);
107 #endif
108 #if VOLID
109 static void do_volid(void);
110 #endif
111 static char dev[] = "<device>";
112
113 extern CHANGE_RULE *change_rules;       /* defined in partition.c */
114
115
116
117 static
118 struct Probes {
119         char *cmd;
120         void (*prc)();
121         char *str;
122         char *help;
123         }
124         list[] = {
125 { "help",  do_help,  NULL,  "Print list of -T(ell) options"     },
126 { "bios",  do_bios,  NULL,  "State of DL as passed to boot loader"  },
127 #if BITMAP
128 { "bitmap=",do_bitmap,"<file>", "Display .bmp file X,Y/color/timer information"},
129 #endif
130 { "ChRul", do_cr_pr, NULL,  "List partition change-rules"  },
131 { "EBDA",  do_ebda,  NULL,  "Extended BIOS Data Area information" },
132 { "geom=", do_geom,  "<bios>", "Geometry CHS data for BIOS code 0x80, etc." },
133 { "geom" , do_geom_all, NULL, "Geometry for all BIOS drives" },
134 { "table=", do_table, dev, "Partition table information for " DEV_DISK_DIR "/hda, etc."},
135 { "video", do_video, NULL,  "Graphic mode information" },
136 #if VOLID
137 { "vol-ID", do_volid, NULL, "Volume ID check for uniqueness"},
138 #endif
139 { NULL,    NULL,     NULL,   NULL}
140         };
141
142
143 static struct partitions {
144         char *name;
145         unsigned char type;
146         unsigned char hide;
147         } ptab [] = {           /* Not complete, by any means */
148
149     { "DOS12", PART_DOS12, HIDDEN_OFF },
150     { "DOS16_small", PART_DOS16_SMALL, HIDDEN_OFF },
151     { "DOS16_big", PART_DOS16_BIG, HIDDEN_OFF },
152     { "NTFS or OS2_HPFS", PART_NTFS, HIDDEN_OFF },      /* same as HPFS; keep these two together */
153 /*  { "HPFS", PART_HPFS, HIDDEN_OFF },  */      /* same as NTFS */
154     { "FAT32", PART_FAT32, HIDDEN_OFF },
155     { "FAT32_lba", PART_FAT32_LBA, HIDDEN_OFF },
156     { "FAT16_lba", PART_FAT16_LBA, HIDDEN_OFF },
157     { "OS/2 BootMgr", PART_OS2_BOOTMGR, 0 },
158     { "DOS extended", PART_DOS_EXTD, 0 },
159     { "WIN extended", PART_WIN_EXTD_LBA, 0 },
160     { "Linux ext'd", PART_LINUX_EXTD, 0 },
161     { "Linux Swap", PART_LINUX_SWAP, 0 },
162     { "Linux Native", PART_LINUX_NATIVE, 0 },
163     { "Minix", PART_LINUX_MINIX, 0 },
164     { "Linux RAID", 0xfd, 0 },
165     { NULL, 0, 0 }   };
166
167 static char phead[] = "\t\t Type  Boot      Start           End      Sector    #sectors";
168 static int dirty = -1;  /* buffer is unread */
169
170 /* load the low memory bios data area */
171 /*  0 = no error, !0 = error on get */
172 int fetch(void)
173 {
174     int fd;
175     int got, get;
176     int at = 0, seek = PROBESEG*16;
177     
178     if (buf_valid>=0) return buf_valid;
179     
180     if ((fd=open(DEV_DIR "/mem", O_RDONLY)) < 0) return buf_valid=1;
181     at = lseek(fd, seek, SEEK_SET);
182     if (at != seek) return buf_valid=1;
183     get = sizeof(buf.b);
184     if (read(fd, &buf.b, get) != get) return buf_valid=1;
185     close(fd);
186     dirty = 0;  /* buffer is unmodified */
187     
188     if (strncmp(buf.s5.signature, PROBE_SIGNATURE,4)) return buf_valid=2;
189 /*    got = buf.s5.version; */  /* quiet GCC */
190     if (buf.s5.version < 3 ||
191         buf.s5.version > (short)(PROBE_VERSION)) return buf_valid=3;
192     got = buf.s5.length;
193     if (got > sizeof(buf.b) || got < sizeof(buf.s4)) return buf_valid=4;
194     if (*(int*)buf.s5.checksum != crc32((unsigned char*)&buf.s5 + 4, got-4, CRC_POLY1))
195         return buf_valid=5;
196
197     if (buf.s5.version == 4) {
198         eq = (void*)&buf.s4.v.equipment;
199         v1 = (void*)&buf.s4.v.vid0F;
200         if (buf.s4.vid > 1) v2 = (void*)&buf.s4.v.vid12;
201         if (buf.s4.vid > 2) v3 = (void*)&buf.s4.v.vid4F00;
202     }
203     if (buf.s5.version >= 5) {
204         eq = (void*)&buf.b[buf.s5.equipment];
205         v1 = (void*)&buf.b[buf.s5.video];
206         if (buf.s5.vid > 1) {
207             v2 = (void*)(v1 + 1);
208             v25 = (void*)(v2 + 1);
209         }
210         if (buf.s5.vid > 2) v3 = (void*)(v25 + 1);
211     }
212 #if BETA_TEST
213         if (verbose>=5) printf("fetch: good return\n");
214 #endif    
215     return buf_valid=0;
216 }
217
218
219 int purge(void)
220 {
221     int i, fd;
222     int seek = PROBESEG*16;
223     
224     if (verbose>=6) printf("purge: called\n");
225 #if 0
226     if (verbose>=6) {fetch(); dirty=1;}         /* test of checksumming */
227 #endif
228     if (dirty <= 0) return 0;           /* nothing to purge */
229     
230     if ((i=fetch())) return i;  /* return error from fetch */
231     
232     i = buf.s5.length;
233     *(int*)buf.s5.checksum = crc32((unsigned char*)&buf.s5 + 4, i-4, CRC_POLY1);
234     
235     if ((fd=open(DEV_DIR "/mem", O_WRONLY)) < 0) pdie("purge: can't open " DEV_DIR "/mem");
236     if (lseek(fd, seek, SEEK_SET) != seek) pdie("purge: ");
237     i = sizeof(buf.b);
238     if (write(fd, &buf.b, i) != i) pdie("purge: ");
239     close(fd);
240     
241     if (verbose>=6) printf("purge: successful write\n");
242     
243     return dirty = 0;                   /* buffer is unmodified */
244 }
245
246
247 static int notice(int needed)
248 {
249     int f = fetch();
250     
251     if (f || buf.s5.version < needed) {
252         printf( f==1 ?  "Only 'root' may do this.\n\n" :
253 #if 0
254                 "This information request requires that you previously booted your system\n"
255                 "using LILO version %s or later.  These versions of the LILO boot\n"
256                 "loader provide the BIOS data check information in low memory.  A boot floppy\n"
257                 "constructed with 'mkrescue' may help.\n\n",
258 #endif
259                 "The information you requested is not available.\n\n"
260                 "Booting your system with LILO version %s or later would provide the re-\n"
261                 "quested information as part of the BIOS data check.  Please install a more\n"
262                 "recent version of LILO on your hard disk, or create a bootable rescue floppy\n"
263                 "or rescue CD with the 'mkrescue' command.\n\n",
264                 
265                 needed==4 ? "22.0" :
266                 needed==5 ? "22.5.1" :
267                 needed==6 ? "22.5.7" :
268                 SA(VERSION_MAJOR) "." SA(VERSION_MINOR) VERSION_EDIT  );
269         return 1;
270     }
271     return 0;
272 }
273
274
275 /* print out the help page for -T flag */
276 static void do_help(void)
277 {
278     struct Probes *pr;
279     
280     printf("usage:");
281     for (pr=list; pr->cmd; pr++) {
282         printf("\tlilo -T %s%s\t%s\n", 
283                         pr->cmd, 
284                         pr->str ? pr->str : "        ",
285                         pr->help);
286     }
287 #ifdef DEBUG_PROBE
288     printf("    In some cases, the '-v' flag will produce more output.\n");
289     printf("sizeof(video_t) = %d  sizeof(floppy_t) = %d  sizeof(hard_t) = %d\n"
290            "sizeof(edd_t) = %d  sizeof(buf.s) = %d\n",
291             sizeof(video_t),  sizeof(floppy_t), sizeof(hard_t), sizeof(edd_t),
292             sizeof(buf.s5) );
293         
294     printf("fetch returns %d\n", fetch());            
295 #endif
296 }
297
298 /* diagnostic output */
299 static void show_geom(char *who, int cyl, int head, int sect)
300 {
301    if (nowarn) return;
302    fprintf(errstd, "    %s: %d cylinders, %d heads, %d sectors\n", who, cyl, head, sect);
303 }
304
305
306 /* get the old BIOS disk geometry */
307 static int get_geom(unsigned int drive, struct disk_geom *geom)
308 {
309     hard_t *hd;
310     floppy_t *fd;
311     int i;
312     struct partition *pt_base;
313     unsigned int total;
314     int sec_err = 0;
315     int hd_err = 0;
316
317 #if 0
318     if((i=fetch())) {
319         printf("No drive geometry information is available.\n\n");
320         exit(0);
321     }
322 #else
323     if (notice(4)) exit(0);
324 #endif
325 #ifdef DEBUG_PROBE
326         printf("get_geom: drive = 0x%02X\n", drive);
327         fflush(stdout);
328 #endif
329     if (drive >= 0 && drive < buf.s5.mflp) {
330         fd = (floppy_t*)&buf.b[buf.s5.floppy] + drive;
331         hd = (hard_t*)fd;
332     }
333     else if (drive == 0x80) {
334         hdp[drive-0x80] = hd = (hard_t*)&buf.b[buf.s5.hard];
335     }    
336     else if (drive >= 0x81 && drive < 0x80+buf.s5.mhrd) {
337         if (drive > buf.s5.disk) return 1;
338         if (!hdp[drive-0x80]) {
339             i = get_geom(drive-1, geom);
340 #ifdef DEBUG_PROBE
341                 printf("get_geom recursive return = %d  AH=0x%02X\n", i, i-1);
342                 fflush(stdout);
343 #endif
344             if (i) return i;
345         }
346         hd = hdp[drive-0x80];
347     } else return 1;
348 #ifdef DEBUG_PROBE
349         printf("get_geom:  hd = %08X\n", (int)hd);
350         fflush(stdout);
351 #endif
352     
353     memset(geom, 0, sizeof(*geom));
354
355
356     if (drive >= 0x80)
357         hdp[drive-0x80 + 1] = hd + 1;           /* simplest increment, but may be wrong */
358     
359     /* regs.eax = 0x1500;           check drive type */
360     /* regs.edx = drive;                        */
361
362 #ifdef DEBUG_PROBE
363         printf("get_geom: int13, fn=15\n");
364         fflush(stdout);
365 #endif
366    
367    if (hd->fn15.flags & 1)   return 1;  /* carry was set */
368    geom->type = hd->fn15.ah;
369    if (geom->type == 0) return 1;
370    if (geom->type == 3)
371      geom->n_total_blocks = ((int)hd->fn15.cx << 16) + hd->fn15.dx;
372    
373    /* regs.eax = 0x0800;                */
374    /* regs.edx = drive;                 */
375    
376 #ifdef DEBUG_PROBE
377         printf("get_geom: int13, fn=08\n");
378         fflush(stdout);
379 #endif
380    
381    if (hd->fn08.flags&1 || hd->fn08.ah || hd->fn08.cx==0)
382      return 1 + hd->fn08.ah;
383    
384    if (!(i = hd->fn08.cx & 0x3F))  i = 64;      /* BIOS bug if 0 */
385    geom->n_sect = i;
386    
387    i *=
388    geom->n_head = ((hd->fn08.dx>>8)&0xFF)+1;
389    i *=
390    geom->n_cyl  = (((hd->fn08.cx>>8)&0xFF)|((hd->fn08.cx&0xC0)<<2))+1;
391    if (i > geom->n_total_blocks) geom->n_total_blocks = i;
392    geom->n_disks = hd->fn08.dx & 0xFF;
393    geom->pt = NULL;
394
395    hd_err = (geom->n_head > 255);
396    sec_err = (geom->n_sect > 63);
397    
398    if (drive < 4)  return 0;
399    
400    pt_base = NULL;
401    if (buf.s5.disk) {
402         pt_base = (struct partition *)&buf.b[buf.s5.partitions];
403    }
404    if (pt_base && drive <= (int)buf.s5.disk) {
405 #if 0
406                                 geom->pt = &pt_base[(drive&15)*4];
407 #else
408         char *p = (char*)pt_base;
409         int i = buf.s5.version >= 4 ? 8 : 0;
410         
411         p += (drive & 15) * (PART_TABLE_SIZE + i) + i;
412         geom->pt = (struct partition *)p;
413         if (i) geom->serial_no = *(int*)(p-6);
414 #endif
415    }
416
417 #ifdef DEBUG_PROBE
418    printf("get_geom:  PT->%08X  S/N=%08X\n", (int)geom->pt, geom->serial_no);
419 #endif
420       
421    /* regs.eax = 0x4100;      check EDD extensions present */
422    /* regs.edx = drive;                         */
423    /* regs.ebx = 0x55AA;                        */
424 #ifdef DEBUG_PROBE
425         printf("get_geom: int13, fn=41\n");
426         fflush(stdout);
427 #endif
428    if ((hd->fn41.flags&1)==0 && (hd->fn41.bx)==(unsigned short)0xAA55) {
429       geom->EDD_flags = hd->fn41.cx;
430       geom->EDD_rev = hd->fn41.ah;
431    }
432    
433    if (((geom->EDD_flags) & EDD_SUBSET) || buf.s5.version >= 6) {
434       edd_t *dp;
435
436       dp = (edd_t*)hdp[drive-0x80 + 1];
437 #ifdef DEBUG_PROBE
438         printf("get_geom:  EDD  dp = %08X\n", (int)dp);
439         fflush(stdout);
440 #endif
441     /* update the pointer to the next drive */
442       hdp[drive-0x80 + 1] = (void*)(dp + 1);
443
444       /* regs.eax = 0x4800;             */
445       /* regs.edx = drive;              */
446       
447 #ifdef DEBUG_PROBE
448         printf("get_geom: int13, fn=48\n");
449         fflush(stdout);
450 #endif
451
452         if ((dp->reg.flags&1) == 0 && dp->reg.ah == 0) {
453
454               if ((dp->info) & EDD_PARAM_GEOM_VALID) {
455                  if ((geom->n_sect != dp->sectors || geom->n_head != dp->heads) &&
456                                 ((verbose>0 && !lba32) || verbose>=4) && 
457                                 !(warned[drive-0x80]&1) ) {
458                         warn("Int 0x13 function 8 and function 0x48 return different\n"
459                                        "head/sector geometries for BIOS drive 0x%02X", drive);
460                         show_geom("fn 08", geom->n_cyl, geom->n_head, geom->n_sect);
461                         show_geom("fn 48", dp->cylinders, dp->heads, dp->sectors);
462                         warned[drive-0x80] |= 1;
463                     }
464
465         /* prefer to return the fn 8 geometry */
466 #if 0
467                  geom->n_cyl  = dp->cylinders;
468                  geom->n_head = dp->heads;
469                  geom->n_sect = dp->sectors;
470 #endif
471                  total = dp->sectors;
472                  total *= dp->heads;
473                  total *= dp->cylinders;
474                  if (total > geom->n_total_blocks) geom->n_total_blocks = total;
475         
476               }
477               if (dp->total_sectors > geom->n_total_blocks)
478                         geom->n_total_blocks = dp->total_sectors;
479         }
480     }
481     if (!(warned[drive-0x80]&4)) {
482        if (hd_err) warn("LILO is compensating for a BIOS bug: (drive 0x%02X) heads > 255",
483                            drive);
484        if (sec_err) {
485            warn("LILO will try to compensate for a BIOS bug: (drive 0x%02X) sectors > 63",
486                            drive);
487            if ((geom->EDD_flags & EDD_PACKET) && !lba32)
488                die("LBA32 addressing should be used, not %s", linear ? "LINEAR" : "GEOMETRIC");
489            if (!(geom->EDD_flags & EDD_PACKET)   &&   !(lba32 | linear) )
490                warn("Drive 0x%02X may not be usable at boot-time.", drive);
491        }
492        warned[drive-0x80] |= 4;
493     }
494    
495    return 0;
496 }
497
498
499 /* get the conventional memory size in Kb */
500 static int get_conv_mem(void)
501 {
502 #if 0
503     if(fetch()) {
504         printf("No memory information is available.\n\n");
505         exit(0);
506     }
507 #else
508     if (notice(4)) exit(0);
509 #endif
510     return (int)eq->mem;
511 }
512
513
514 /* print the conventional memory size */
515 static void do_ebda(void)
516 {
517     int m, n, init;
518     static char EBDA[]="Extended BIOS Data Area (EBDA)";
519     
520     m = get_conv_mem() - EBDA_EXTRA;
521 #if EBDA_EXTRA
522     printf("*** BUGFIX - reported EBDA is increased by %dK - BUGFIX ***\n",
523         EBDA_EXTRA);
524 #endif
525     if (m==640) printf("    no %s\n", EBDA);
526     else printf("    %s = %dK\n", EBDA, 640-m);
527     printf("    Conventional Memory = %dK    0x%06X\n", m, m<<10);
528     m <<= 10;
529     m -= 0x200;
530     n = (select_loader()->size + SECTOR_SIZE - 1) / SECTOR_SIZE;
531     n = m - (n+4+MAX_DESCR_SECS+(COMMAND_LINE_SIZE>256))*SECTOR_SIZE;
532     init = (n - (MAX_SETUPSECS+1)*SECTOR_SIZE)>>4;
533     if (init > DEF_INITSEG) init = DEF_INITSEG;
534     printf("\n");
535
536     printf("    The First stage loader boots at:  0x%08X  (0000:%04X)\n",
537                         FIRSTSEG<<4, FIRSTSEG<<4);
538     printf("    The Second stage loader runs at:  0x%08X  (%04X:%04X)\n",
539                         n, n>>4, n&15);
540     printf("    The kernel cmdline is passed at:  0x%08X  (%04X:%04X)\n",
541                         m, init, m-(init<<4));
542
543 }
544
545 static char *number(unsigned int n)
546 {
547     unsigned int k = 1000000000UL;      /* 10^9 */
548     static char s[16];
549     char *cp = s;
550     
551     while (n<k && k>1) k /= 1000UL;
552     sprintf(cp,"%u",n/k);
553     n %= k;
554     k /= 1000UL;
555     while (k) {
556         while (*++cp) ;
557         sprintf(cp,",%03u",n/k);
558         n %= k;
559         k /= 1000UL;
560     }
561     return s;
562 }
563
564
565 /* print the CHS geometry information for the specified disk */
566 static void print_geom(int dr, struct disk_geom geom)
567 {
568     char ch_ser[24] = { 0 };
569     char *sz = "KMGT";
570     char *sp = sz;
571     unsigned int n, m=0;
572
573 #ifdef DEBUG_PROBE
574     if (!dr) geom.n_total_blocks = 4000000000UL;        /* 2.09Tb */
575 #endif
576
577     if (geom.serial_no) sprintf(ch_ser, "vol-ID: %08X", geom.serial_no);
578     printf("    bios=0x%02x, cylinders=%d, heads=%d, sectors=%d\t%s\n", 
579         dr, geom.n_cyl, geom.n_head, geom.n_sect, ch_ser);
580     n = geom.n_total_blocks/2;
581     while (n > 999999) { n/=1000UL; n*=1024UL; n/=1000UL; sp++; }
582     if (n > 999) {
583         m = (n%1000UL)/10;
584         n /= 1000UL;
585         sp++;
586     }
587     if (m) printf("\t(%3u.%02u%cb", n, m, *sp);
588     else printf("\t(%3u%cb", n, *sp);
589     
590     printf("%14s sectors)", number(geom.n_total_blocks));
591     if (geom.EDD_flags & EDD_PACKET) {
592 /*      printf("\tEDD packet calls allowed");   */
593         printf("\tLBA32 supported (EDD bios)");
594     } else printf("\tC:H:S supported (PC bios)");
595     printf("\n");
596 }
597
598
599 /* print disk drive geometry for all drives */
600 static void do_geom_all(void)
601 {
602    int d, hd, dr;
603    struct disk_geom geom;
604    
605    for (hd=0; hd<0x81; hd+=0x80)
606    for (d=0; d<16; d++) {
607       dr = d+hd;
608       if (get_geom(dr, &geom)==0) {
609          if (dr==0x80) printf("\nBIOS reports %d hard drive%s\n", (int) geom.n_disks,
610                                       (int)geom.n_disks==1 ? "" : "s");
611          print_geom(dr, geom);
612       }
613    }
614 }
615
616
617 /* print disk drive geometry information for a particular drive */
618 static void do_geom(char *bios)
619 {
620     int dr;
621     struct disk_geom geom;
622
623     dr = to_number(bios);
624     if (get_geom(dr, &geom)==0) print_geom(dr, geom);
625     else printf("Unrecognized BIOS device code 0x%02x\n", dr);
626     
627 }
628
629
630 /* print an individual partition table entry */
631 static void print_pt(int index, struct partition pt)
632 {
633     char bt[4], *ty, start[32], end[32], type[8];
634     char x;
635     int i;
636     
637     for (x=i=0; i<sizeof(pt); i++) x |= ((char*)&pt)[i];
638     if (!x) {
639         printf("%4d\t\t\t     ** empty **\n", index);
640         return;
641     }
642     strcpy(bt,"   ");
643     sprintf(type, "0x%02x", (int)pt.sys_ind);
644     sprintf(start, "%4d:%d:%d",
645         (int)pt.cyl+((pt.sector&0xC0)<<2),
646         (int)pt.head,
647         (int)pt.sector & 0x3f );
648     sprintf(end, "%4d:%d:%d",
649         (int)pt.end_cyl+((pt.end_sector&0xC0)<<2),
650         (int)pt.end_head,
651         (int)pt.end_sector & 0x3f );
652     ty = type;
653     if (pt.boot_ind==0x80) bt[1]='*';
654     else if (pt.boot_ind==0) ; /* do nothing */
655     else {
656         sprintf(bt+1,"%02x", (int)pt.boot_ind);
657     }
658     for (i=0; ptab[i].name; i++) {
659         if (ptab[i].type == pt.sys_ind) {
660             ty = ptab[i].name;
661             break;
662         } else if ((ptab[i].type|ptab[i].hide) == pt.sys_ind) {
663             bt[0] = 'H';
664             ty = ptab[i].name;
665             break;
666         }
667     }
668     printf("%4d%18s%5s%11s%14s%12u%12u\n", index, ty, bt,
669         start, end, pt.start_sect, pt.nr_sects);
670 }
671
672
673 /* partition table display */
674 static void do_table(char *part)
675 {
676     struct partition pt [PART_MAX_MAX+1];
677     int volid;
678     long long where[PART_MAX_MAX+1];
679     int i,j;
680     int extd = (extended_pt || verbose>0);
681     
682     j = read_partitions(part, extd ? nelem(pt)-1 : 0, &volid, pt, verbose>=5 ? where : NULL);
683     extd |= j>PART_MAX;
684     printf(" vol-ID: %08X\n\n%s\n", (int)volid, phead);
685     for (i=0; i<PART_MAX; i++) {
686         print_pt(i+1, pt[i]);
687     }
688     i=4;
689     if (extd)
690     while (pt[i].sys_ind) {
691         print_pt(i+1, pt[i]);
692         i++;
693     }
694     if (verbose>=5) {
695         printf("\n");
696         for (i=0; i<j; i++) printf("%4d%20lld%12d\n", i+1, where[i], (int)(where[i]/SECTOR_SIZE));
697     }
698 }
699
700
701 /* list partition change-rules */
702 static void do_cr_pr(void)
703 {
704     CHANGE_RULE *cr;
705     
706     cr = change_rules;
707     printf("\t\tType Normal Hidden\n");
708     if (!cr) printf("\t **** no change-rules defined ****\n");
709     while (cr) {
710         printf ("%20s  0x%02x  0x%02x\n", cr->type, (int)cr->normal, (int)cr->hidden);
711         cr = cr->next;
712     }
713 }
714
715 static int egamem;
716 static int mode, col, row, page;
717 /*
718 enum {VIDEO_UNKNOWN, VIDEO_MDA, VIDEO_CGA, VIDEO_EGA, VIDEO_MCGA,
719         VIDEO_VGA, VIDEO_VESA, VIDEO_VESA_800};
720  */
721
722 int get_video(void)     /* return -1 on error, or adapter type [0..7] */
723 {
724     int adapter = 0;    /* 0=unknown, 1=MDA,HGC, 2=CGA, 3=EGA, 4=MCGA, 5=VGA,
725                                          6=VESA (640), 7=VESA (800) */
726     int okay = 1;
727     int monitor;
728         
729     if(fetch()) return -1;
730
731     if ((okay=buf.s5.vid)) {
732     /* get current video mode */
733     /* reg.eax = 0x0F00;                */
734         if (verbose >= 6)
735             printf("get video mode\n");
736         mode = v1->vid0F.al;
737         col = v1->vid0F.ah;
738         page = v1->vid0F.bh;
739         row = v1->vid0F.bl + 1;
740         if (mode==7) {
741             adapter=1;
742             row = 25;
743             okay = 0;
744         } else if (col<80) {
745             adapter=2;
746             okay=0;
747         } else adapter=2;       /* at least CGA */
748     }
749     if (okay>=2) {
750     /* determine video adapter type */
751     /*    reg.eax = 0x1200;       call valid on EGA/VGA */
752     /*    reg.ebx = 0xFF10;                             */
753         if (verbose >= 6)
754             printf("determine adapter type\n");
755         if ((unsigned)(monitor = v2->vid12.bh) <= 1U)  {
756             adapter = 3; /* at least EGA */
757             egamem = v2->vid12.bl;
758         }
759         else {
760             okay = 0;
761         }
762     }
763     if (okay>=2) {
764         /* check for VGA */
765         /* reg.eax = 0x1A00;       get display combination */
766         if (verbose >= 6)
767             printf("get display combination\n");
768         if ( v2->vid1A.al==0x1A ) {
769             monitor = (v2->vid1A.bx >> ((verbose>=9)*8) ) & 0xFF;
770             switch (monitor) {
771               case 1:
772                 adapter = 1;
773                 break;
774               case 2:
775                 adapter = 2;
776                 break;
777               case 7:
778               case 8:
779                 adapter = 5;    /* VGA */
780                 break;
781               case 0xA:
782               case 0xB:
783               case 0xC:
784                 adapter = 4;    /* MCGA */
785                 break;
786               default:
787                 okay = 0;
788                 break;
789             }
790         } else {
791             okay = 0;
792         }
793     }
794     if (okay>=3 && adapter==5) {
795         /* check for BIOS bug (trashing DX) */
796         if (v25 && verbose >= 6)
797             printf("check Enable Screen Refresh\n");
798         if (v25) {
799             video_36_bug = 2;   /* mark implemented */
800             
801             if ((v25->vid36.ax & 0xFF) != 0x12)  video_36_bug = 1;
802             else {
803                 if (v25->vid36.cx != 0x1234 || v25->vid36.bp != 0x4321)
804                     video_36_bug |= 4;
805                 if (v25->vid36.dx != 0x5680) video_36_bug |= 8;
806             }
807         }
808         
809         /* check for VESA extensions */
810         if (verbose >= 6)
811             printf("check VESA present\n");
812             
813         if ((v3->vid4F00.ax == 0x4f) && strncmp("VESA", v3->vid4F00.sig, 4)==0)  adapter++;
814         
815         if (adapter > 5) {
816             /* reg.eax = 0x4f01;
817                reg.ecx = 0x0101;           640x480x256 */
818             if ((v3->vid101.ax == 0x4f) && (v3->vid101.bits & 0x19) == 0x19) adapter ++;
819             else adapter--;
820         }
821         if (adapter > 6) {
822             /* reg.eax = 0x4f01;
823                reg.ecx = 0x0103;           800x600x256 */
824             if ((v3->vid103.ax == 0x4f) && (v3->vid103.bits & 0x19) == 0x19) ;
825             else adapter--;
826         }
827     }
828     if (verbose>=2)
829         printf ("mode = 0x%02x,  columns = %d,  rows = %d,  page = %d\n",
830                        mode, col, row, page);
831     
832     return adapter;
833 }
834
835 /* print VGA/VESA mode information */
836 void do_video(void)
837 {
838 static char *display[] = { "unknown", "MDA", "CGA", "EGA", 
839                 "MCGA", "VGA", "VGA/VESA", "VGA/VESA" };
840     int adapter; /* 0=unknown, 1=MDA,HGC, 2=CGA, 3=EGA, 4=MCGA, 5=VGA,
841                          6=VESA (640), 7=VESA (800) */
842
843     if (notice(4)) exit(0);
844     
845     adapter = get_video();
846 #if 0
847     if(adapter<0) {
848         printf("No video mode information is available.\n");
849         return;
850     }
851 #endif
852     printf("%s adapter:\n\n", display[adapter]);
853     if (adapter < 3 || (adapter == 3 && egamem < 1) ) {
854         printf("No graphic modes are supported\n");
855     } else {
856         if (adapter != 4)
857             printf("    640x350x16    mode 0x0010\n");
858         if (adapter >= 5) {
859             printf("    640x480x16    mode 0x0012\n\n");
860             printf("    320x200x256   mode 0x0013\n");
861         }
862         if (adapter >= 6)
863             printf("    640x480x256   mode 0x0101\n");
864         if (adapter >= 7)
865             printf("    800x600x256   mode 0x0103\n");
866     }
867     if (video_36_bug && (verbose>0 || (video_36_bug&(8+4)))) {
868      /* setting video_36_bug is a side effect of get_video */
869         printf("\nEnable Screen Refresh %s.\n",
870             (video_36_bug & 4) ? "bugs are present" :
871             (video_36_bug & 8) ? "bug is present" :
872             (video_36_bug == 1) ? "is not supported" : "is supported");
873     }
874 }
875
876 /* entry from lilo.c for the '-T' (tell) switch */
877 void probe_tell (char *cmd)
878 {
879     struct Probes *pr = list;
880     int n;
881     char *arg;
882     
883     if (!(verbose>0)) printf("\n");
884     for (; pr->cmd; pr++) {
885         n = strlen(pr->cmd);
886         arg = NULL;
887         if (pr->cmd[n-1] == '=') arg = cmd+n;
888         if (!strncasecmp(cmd, pr->cmd, n)) {
889             pr->prc(arg);
890             printf("\n");
891             exit(0);
892         }
893     }
894     printf("Unrecognized option to '-T' flag\n");
895     do_help();
896     printf("\n");
897     exit(1);
898 }
899
900
901 int bios_max_devs(void)
902 {
903     struct disk_geom geom;
904     int i;
905     
906     if (!fetch() && !get_geom(0x80, &geom)) {
907         i = (buf.s5.disk & 0x7f) + 1;
908         if (geom.n_disks == i) return i;
909     }
910     return BIOS_MAX_DEVS;    
911 }
912
913 #ifdef DEBUG_PROBE
914 static void dump_pt(unsigned char *pt)
915 {
916     int i, j;
917     for (j=0; j<4; j++) {
918         for (i=0; i<16; i++) {
919             printf(" %02X", (int)(*pt++));
920         }
921         printf("\n");
922     }
923     printf("\n");
924 }
925 #endif
926
927 /* 
928  *  return the bios device code of the disk, based on the geometry
929  *      match with the probe data
930  *   side effect is to place the device code in geo->device
931  * return -1 if indeterminate
932  */
933 int bios_device(GEOMETRY *geo, int device)
934 {
935     int bios1, bios, match, fd;
936     int bios2, snmatch;
937     int mbios[BD_MAX_HARD];
938     struct disk_geom bdata;
939     DEVICE dev;
940     unsigned char part[PART_TABLE_SIZE];
941     unsigned char extra[8];
942     int serial;
943     
944         
945     if (fetch()) return -1;
946     
947     if (verbose>=5) printf("bios_dev:  device %04X\n", device);
948 #ifdef DEBUG_PROBE
949         fflush(stdout);
950 #endif    
951     if (!has_partitions(device)) return -1;
952         
953     match = 0;
954     bios1 = -1;         /* signal error */
955     for (bios=0x80; bios<=buf.s5.disk; bios++) {
956         mbios[bios-0x80] = 0;
957         if (get_geom(bios, &bdata)) break;
958         if (geo->cylinders == bdata.n_cyl &&
959             geo->heads == bdata.n_head &&
960             geo->sectors == bdata.n_sect) {
961                 match++;
962                 mbios[bios-0x80] = bios1 = bios;
963         }
964     }
965     if (match == 1) {
966         if (verbose>=5) printf("bios_dev: match on geometry alone (0x%02X)\n",
967                 bios1);
968         return (geo->device = bios1);
969     }
970
971     device &= D_MASK(device);   /* mask to primary device */
972     fd = dev_open(&dev,device,O_RDONLY);
973     if (verbose>=5) printf("bios_dev:  masked device %04X, which is %s\n", 
974                         device, dev.name);
975     if (lseek(fd, PART_TABLE_OFFSET-8, SEEK_SET)!=PART_TABLE_OFFSET-8)
976         die("bios_device: seek to partition table - 8");
977     if (read(fd,extra,sizeof(extra))!= sizeof(extra))
978         die("bios_device: read partition table - 8");
979     serial = *(int*)(extra+2);
980     if (lseek(fd, PART_TABLE_OFFSET, SEEK_SET)!=PART_TABLE_OFFSET)
981         die("bios_device: seek to partition table");
982     if (read(fd,part,sizeof(part))!= sizeof(part))
983         die("bios_device: read partition table");
984     dev_close(&dev);
985
986 #ifdef DEBUG_PROBE
987     if (verbose>=5) {
988         printf("serial number = %08X\n", serial);
989         dump_pt(part);
990     }
991 #endif
992
993     if (verbose>=5) printf("bios_dev: geometry check found %d matches\n", match);
994     
995     snmatch = match = 0;
996     bios2 = bios1 = -1;
997
998  /* 'bios' is set leaving the 'for' above */
999     while (--bios >= 0x80) {
1000         get_geom(bios, &bdata);
1001         if (verbose>=5) {
1002                 printf("bios_dev: (0x%02X)  vol-ID=%08X  *PT=%08lX\n",
1003                         bios, bdata.serial_no, (long)bdata.pt);
1004 #ifdef DEBUG_PROBE
1005                 dump_pt((void*)bdata.pt);
1006 #endif
1007         }
1008         if ( !memcmp(part,bdata.pt,sizeof(part)) ) {
1009             match++;
1010             bios1 = bios;
1011         }
1012         if ( bdata.serial_no && serial==bdata.serial_no ) {
1013             snmatch++;
1014             bios2 = bios;
1015         }
1016     }
1017     if (verbose>=5) printf("bios_dev: PT match found %d match%s (0x%02X)\n",
1018                 match, match==1 ? "" : "es", bios1&255);
1019     if (match != 1) {
1020         match = snmatch;
1021         bios1 = bios2;
1022         if (verbose>=5) printf("bios_dev: S/N match found %d match%s (0x%02X)\n",
1023                 match, match==1 ? "" : "es", bios1);
1024     }
1025
1026     if (match == 1) {
1027         get_geom(bios1, &bdata);
1028         if (  (geo->sectors && geo->sectors!=bdata.n_sect)  ||
1029                (geo->heads && geo->heads!=bdata.n_head)  )  {
1030             unsigned int nblocks = geo->cylinders * geo->heads * geo->sectors;
1031
1032             if (!(lba32 | linear) && !(warned[bios1-0x80]&2) ) {
1033                 warn("Kernel & BIOS return differing head/sector geometries for device 0x%02X", bios1);
1034                 show_geom("Kernel", geo->cylinders, geo->heads, geo->sectors);
1035                 show_geom("  BIOS", bdata.n_cyl, bdata.n_head, bdata.n_sect);
1036                 warned[bios1-0x80] |= 2;
1037             }
1038 #if 1
1039             geo->sectors = bdata.n_sect;
1040             geo->heads = bdata.n_head;
1041             if (bdata.n_total_blocks > nblocks) nblocks = bdata.n_total_blocks;
1042             /* If this is a buggy BIOS, make sure to avoid a division by zero */
1043             if (bdata.n_head*bdata.n_sect > 0) 
1044                 geo->cylinders = nblocks / (bdata.n_head*bdata.n_sect);
1045             else
1046                 geo->cylinders = nblocks;
1047 #endif
1048         }
1049         return (geo->device = bios1);
1050     }
1051
1052     return -1;
1053 }
1054
1055
1056 #if BITMAP
1057 static void do_bitmap(char *file)
1058 {
1059     printf("Color, Positioning, and Timer information for file:  %s\n", file);
1060     printf("...<unimplemented>...\n");
1061 }
1062 #endif
1063
1064 static unsigned char dl,dh;
1065
1066 static int get_bios(void)
1067 {
1068     if (fetch() || buf.s5.version<5) return -1;
1069 #if BETA_TEST
1070         if (verbose>=5) printf("get_bios 1\n");
1071 #endif
1072     dl = eq->boot_dx;
1073 #if BETA_TEST
1074         if (verbose>=5) printf("get_bios 2\n");
1075 #endif
1076     dh = eq->boot_dx >> 8;
1077     
1078     return eq->boot_dx;
1079 }
1080
1081
1082 void check_bios(void)
1083 {
1084 #if BETA_TEST
1085         if (verbose>=5) printf("check_bios 1\n");
1086 #endif
1087     if (bios_passes_dl == DL_NOT_SET) {
1088         bios_passes_dl = DL_UNKNOWN;
1089         
1090         if (get_bios() < 0) return;
1091 #if BETA_TEST
1092         if (verbose>=5) printf("check_bios 2\n");
1093 #endif
1094         if (dl==0xFE) {
1095             if ( !((dh>=0x80 && dh<=DEV_MASK) || dh==0) )
1096                 bios_passes_dl = DL_BAD;
1097         }
1098         else if ( dl>=0x80 && dl<=DEV_MASK ) bios_passes_dl = DL_MAYBE;
1099         else if ( dl > 0 ) bios_passes_dl = DL_BAD;
1100     }
1101     /* already set, leave alone */
1102 #if BETA_TEST
1103         if (verbose>=5) printf("check_bios 3  bios_passes_dl=%d\n", (int)bios_passes_dl);
1104 #endif
1105 }
1106
1107
1108 void do_bios(void)
1109 {
1110     int code=1;
1111     
1112 static char *ord[] =
1113 { "first", "second", "3rd", "4th", "5th", "6th", "7th", "8th",
1114   "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th" };
1115     
1116     notice(5);
1117     if (get_bios() < 0)
1118         printf("No information available on the state of DL at boot.\n");
1119     else
1120     {
1121         printf("BIOS provided boot device is 0x%02x  (DX=0x%04X).\n",
1122                 code=(dl==0xFE ? dh : dl), eq->boot_dx);
1123         bios_passes_dl = DL_NOT_SET;
1124         check_bios();
1125     }
1126     printf("\nUnless overridden, 'bios-passes-dl = %s' will be assumed.",
1127         bios_passes_dl == DL_BAD ? "no" :
1128         bios_passes_dl == DL_MAYBE ? "maybe" :
1129         bios_passes_dl == DL_GOOD ? "yes" : "unknown" );
1130     if (bios_passes_dl > DL_BAD) {
1131         char *cp = NULL;
1132         char *cn = NULL;
1133
1134         if (code>=0 && code<2) { cp="floppy"; cn=ord[code]; }
1135         if (code>=0x80 && code<=0x8f) { cp="hard"; cn=ord[code&15]; }
1136         if (cp)
1137         printf("  If you\nactually booted from the %s %s drive, then this assumption is okay.",
1138                 cn, cp);
1139 #if 0
1140         if (bios_passes_dl == DL_MAYBE)
1141             printf("\nIf the BIOS always gets DL set correctly, you might consider specifying\n"
1142                    "  'bios_passes_dl = yes' or '-Z1'.\n");
1143 #endif
1144     }
1145     printf("\n");
1146 }
1147
1148 #if VOLID
1149 static void do_volid(void)
1150 {
1151     int bios, n, i, k, sv, nv, unique;
1152     struct disk_geom geom;
1153     unsigned int serial[MAX_BIOS_DEVICES];
1154     int uniq[MAX_BIOS_DEVICES], valid[MAX_BIOS_DEVICES];
1155
1156     if (notice(4)) exit(0);
1157     
1158     printf("\n  BIOS     Volume ID\n\n");
1159     unique = 1;
1160     nv = n = 0;
1161     for (bios=0x80; bios<0x80+MAX_BIOS_DEVICES && !get_geom(bios, &geom); bios++) {
1162         int uq = 1;
1163         i = bios - 0x80;
1164 #if 0
1165         if (i==0 || i==2) geom.serial_no = 0;
1166         if (i==4) geom.serial_no = serial[2];
1167 #endif
1168
1169         serial[i] = geom.serial_no;
1170         valid[i] = sv = serial_valid(geom.serial_no, bios);
1171         nv |= !sv;
1172         if (sv) for (k=0; k<i; k++) uq &= (geom.serial_no != serial[k]);
1173         uniq[i] = uq;
1174         unique &= uq;
1175         n++;
1176
1177         printf("  0x%02X     %08X %s%s\n", i+0x80, serial[i],
1178                 uq ? "" : "*", sv ? "" : "-");
1179     }
1180
1181     printf("\nVolume ID's are%s unique.\n", unique ? " all" : " NOT");
1182     if (nv)
1183         printf("   '-' marks an invalid Volume ID which will be automatically updated\n"
1184                 "\tthe next time  /sbin/lilo  is executed.\n");
1185     if (!unique)
1186         printf("   '*' marks a volume ID which is duplicated.  Duplicated ID's must be\n"
1187                 "\tresolved before installing a new boot loader.  The volume ID may\n"
1188                 "\tbe cleared using the '-z' and '-M' switches.\n"
1189                 );
1190     printf("\n");
1191 }
1192 #endif
1193