1 /* probe.c - BIOS probe utility
3 * Copyright 1999-2006 John Coffman
4 * Copyright 2009-2015 Joachim Wiedorn
7 * Licensed under the terms contained in the file 'COPYING'
8 * in the source directory.
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 */
24 #include <sys/types.h>
30 #include "partition.h"
38 #error "too many floppies in bdata.h"
41 #error "too many hard disks in bdata.h"
44 #error "video get level set too high in bdata.h"
50 unsigned char b[5*SECTOR_SIZE];
52 short checksum[2]; /* prevent alignment on *4 boundary */
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 */
67 short checksum[2]; /* prevent alignment on *4 boundary */
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 */
85 static video_t25 *v25; /* extension for PROBE_VERSION 5 */
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];
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);
106 static void do_bitmap(char *file);
109 static void do_volid(void);
111 static char dev[] = "<device>";
113 extern CHANGE_RULE *change_rules; /* defined in partition.c */
125 { "help", do_help, NULL, "Print list of -T(ell) options" },
126 { "bios", do_bios, NULL, "State of DL as passed to boot loader" },
128 { "bitmap=",do_bitmap,"<file>", "Display .bmp file X,Y/color/timer information"},
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" },
137 { "vol-ID", do_volid, NULL, "Volume ID check for uniqueness"},
139 { NULL, NULL, NULL, NULL}
143 static struct partitions {
147 } ptab [] = { /* Not complete, by any means */
149 { "DOS12", PART_DOS12, HIDDEN_OFF },
150 { "DOS16_small", PART_DOS16_SMALL, HIDDEN_OFF },
151 { "DOS16_big", PART_DOS16_BIG, HIDDEN_OFF },
152 { "NTFS", 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 }, */ /* obsolete system */
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 },
167 static char phead[] = "\t\t Type Boot Start End Sector #sectors";
168 static int dirty = -1; /* buffer is unread */
170 /* load the low memory bios data area */
171 /* 0 = no error, !0 = error on get */
176 int at = 0, seek = PROBESEG*16;
178 if (buf_valid>=0) return buf_valid;
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;
184 if (read(fd, &buf.b, get) != get) return buf_valid=1;
186 dirty = 0; /* buffer is unmodified */
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;
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))
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;
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);
210 if (buf.s5.vid > 2) v3 = (void*)(v25 + 1);
213 if (verbose>=5) printf("fetch: good return\n");
222 int seek = PROBESEG*16;
224 if (verbose>=6) printf("purge: called\n");
226 if (verbose>=6) {fetch(); dirty=1;} /* test of checksumming */
228 if (dirty <= 0) return 0; /* nothing to purge */
230 if ((i=fetch())) return i; /* return error from fetch */
233 *(int*)buf.s5.checksum = crc32((unsigned char*)&buf.s5 + 4, i-4, CRC_POLY1);
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: ");
238 if (write(fd, &buf.b, i) != i) pdie("purge: ");
241 if (verbose>=6) printf("purge: successful write\n");
243 return dirty = 0; /* buffer is unmodified */
247 static int notice(int needed)
251 if (f || buf.s5.version < needed) {
252 printf( f==1 ? "Only 'root' may do this.\n\n" :
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",
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",
266 needed==5 ? "22.5.1" :
267 needed==6 ? "22.5.7" :
268 SA(VERSION_MAJOR) "." SA(VERSION_MINOR) VERSION_EDIT );
275 /* print out the help page for -T flag */
276 static void do_help(void)
281 for (pr=list; pr->cmd; pr++) {
282 printf("\tlilo -T %s%s\t%s\n",
284 pr->str ? pr->str : " ",
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),
294 printf("fetch returns %d\n", fetch());
298 /* diagnostic output */
299 static void show_geom(char *who, int cyl, int head, int sect)
302 fprintf(errstd, " %s: %d cylinders, %d heads, %d sectors\n", who, cyl, head, sect);
306 /* get the old BIOS disk geometry */
307 static int get_geom(unsigned int drive, struct disk_geom *geom)
312 struct partition *pt_base;
319 printf("No drive geometry information is available.\n\n");
323 if (notice(4)) exit(0);
326 printf("get_geom: drive = 0x%02X\n", drive);
329 if (drive >= 0 && drive < buf.s5.mflp) {
330 fd = (floppy_t*)&buf.b[buf.s5.floppy] + drive;
333 else if (drive == 0x80) {
334 hdp[drive-0x80] = hd = (hard_t*)&buf.b[buf.s5.hard];
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);
341 printf("get_geom recursive return = %d AH=0x%02X\n", i, i-1);
346 hd = hdp[drive-0x80];
349 printf("get_geom: hd = %08X\n", (int)hd);
353 memset(geom, 0, sizeof(*geom));
357 hdp[drive-0x80 + 1] = hd + 1; /* simplest increment, but may be wrong */
359 /* regs.eax = 0x1500; check drive type */
360 /* regs.edx = drive; */
363 printf("get_geom: int13, fn=15\n");
367 if (hd->fn15.flags & 1) return 1; /* carry was set */
368 geom->type = hd->fn15.ah;
369 if (geom->type == 0) return 1;
371 geom->n_total_blocks = ((int)hd->fn15.cx << 16) + hd->fn15.dx;
373 /* regs.eax = 0x0800; */
374 /* regs.edx = drive; */
377 printf("get_geom: int13, fn=08\n");
381 if (hd->fn08.flags&1 || hd->fn08.ah || hd->fn08.cx==0)
382 return 1 + hd->fn08.ah;
384 if (!(i = hd->fn08.cx & 0x3F)) i = 64; /* BIOS bug if 0 */
388 geom->n_head = ((hd->fn08.dx>>8)&0xFF)+1;
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;
395 hd_err = (geom->n_head > 255);
396 sec_err = (geom->n_sect > 63);
398 if (drive < 4) return 0;
402 pt_base = (struct partition *)&buf.b[buf.s5.partitions];
404 if (pt_base && drive <= (int)buf.s5.disk) {
406 geom->pt = &pt_base[(drive&15)*4];
408 char *p = (char*)pt_base;
409 int i = buf.s5.version >= 4 ? 8 : 0;
411 p += (drive & 15) * (PART_TABLE_SIZE + i) + i;
412 geom->pt = (struct partition *)p;
413 if (i) geom->serial_no = *(int*)(p-6);
418 printf("get_geom: PT->%08X S/N=%08X\n", (int)geom->pt, geom->serial_no);
421 /* regs.eax = 0x4100; check EDD extensions present */
422 /* regs.edx = drive; */
423 /* regs.ebx = 0x55AA; */
425 printf("get_geom: int13, fn=41\n");
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;
433 if (((geom->EDD_flags) & EDD_SUBSET) || buf.s5.version >= 6) {
436 dp = (edd_t*)hdp[drive-0x80 + 1];
438 printf("get_geom: EDD dp = %08X\n", (int)dp);
441 /* update the pointer to the next drive */
442 hdp[drive-0x80 + 1] = (void*)(dp + 1);
444 /* regs.eax = 0x4800; */
445 /* regs.edx = drive; */
448 printf("get_geom: int13, fn=48\n");
452 if ((dp->reg.flags&1) == 0 && dp->reg.ah == 0) {
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;
465 /* prefer to return the fn 8 geometry */
467 geom->n_cyl = dp->cylinders;
468 geom->n_head = dp->heads;
469 geom->n_sect = dp->sectors;
473 total *= dp->cylinders;
474 if (total > geom->n_total_blocks) geom->n_total_blocks = total;
477 if (dp->total_sectors > geom->n_total_blocks)
478 geom->n_total_blocks = dp->total_sectors;
481 if (!(warned[drive-0x80]&4)) {
482 if (hd_err) warn("LILO is compensating for a BIOS bug: (drive 0x%02X) heads > 255",
485 warn("LILO will try to compensate for a BIOS bug: (drive 0x%02X) sectors > 63",
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);
492 warned[drive-0x80] |= 4;
499 /* get the conventional memory size in Kb */
500 static int get_conv_mem(void)
504 printf("No memory information is available.\n\n");
508 if (notice(4)) exit(0);
514 /* print the conventional memory size */
515 static void do_ebda(void)
518 static char EBDA[]="Extended BIOS Data Area (EBDA)";
520 m = get_conv_mem() - EBDA_EXTRA;
522 printf("*** BUGFIX - reported EBDA is increased by %dK - BUGFIX ***\n",
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);
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;
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",
540 printf(" The kernel cmdline is passed at: 0x%08X (%04X:%04X)\n",
541 m, init, m-(init<<4));
545 static char *number(unsigned int n)
547 unsigned int k = 1000000000UL; /* 10^9 */
551 while (n<k && k>1) k /= 1000UL;
552 sprintf(cp,"%u",n/k);
557 sprintf(cp,",%03u",n/k);
565 /* print the CHS geometry information for the specified disk */
566 static void print_geom(int dr, struct disk_geom geom)
568 char ch_ser[24] = { 0 };
574 if (!dr) geom.n_total_blocks = 4000000000UL; /* 2.09Tb */
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++; }
587 if (m) printf("\t(%3u.%02u%cb", n, m, *sp);
588 else printf("\t(%3u%cb", n, *sp);
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)");
599 /* print disk drive geometry for all drives */
600 static void do_geom_all(void)
603 struct disk_geom geom;
605 for (hd=0; hd<0x81; hd+=0x80)
606 for (d=0; d<16; d++) {
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);
617 /* print disk drive geometry information for a particular drive */
618 static void do_geom(char *bios)
621 struct disk_geom geom;
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);
630 /* print an individual partition table entry */
631 static void print_pt(int index, struct partition pt)
633 char bt[4], *ty, start[32], end[32], type[8];
637 for (x=i=0; i<sizeof(pt); i++) x |= ((char*)&pt)[i];
639 printf("%4d\t\t\t ** empty **\n", index);
643 sprintf(type, "0x%02x", (int)pt.sys_ind);
644 sprintf(start, "%4d:%d:%d",
645 (int)pt.cyl+((pt.sector&0xC0)<<2),
647 (int)pt.sector & 0x3f );
648 sprintf(end, "%4d:%d:%d",
649 (int)pt.end_cyl+((pt.end_sector&0xC0)<<2),
651 (int)pt.end_sector & 0x3f );
653 if (pt.boot_ind==0x80) bt[1]='*';
654 else if (pt.boot_ind==0) ; /* do nothing */
656 sprintf(bt+1,"%02x", (int)pt.boot_ind);
658 for (i=0; ptab[i].name; i++) {
659 if (ptab[i].type == pt.sys_ind) {
662 } else if ((ptab[i].type|ptab[i].hide) == pt.sys_ind) {
668 printf("%4d%18s%5s%11s%14s%12u%12u\n", index, ty, bt,
669 start, end, pt.start_sect, pt.nr_sects);
673 /* partition table display */
674 static void do_table(char *part)
676 struct partition pt [PART_MAX_MAX+1];
678 int64_t where[PART_MAX_MAX+1];
680 int extd = (extended_pt || verbose>0);
682 j = read_partitions(part, extd ? nelem(pt)-1 : 0, &volid, pt, verbose>=5 ? where : NULL);
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]);
690 while (pt[i].sys_ind) {
691 print_pt(i+1, pt[i]);
696 for (i=0; i<j; i++) printf("%4d%20" PRId64 "%12d\n", i+1, where[i], (int)(where[i]/SECTOR_SIZE));
701 /* list partition change-rules */
702 static void do_cr_pr(void)
707 printf("\t\tType Normal Hidden\n");
708 if (!cr) printf("\t **** no change-rules defined ****\n");
710 printf ("%20s 0x%02x 0x%02x\n", cr->type, (int)cr->normal, (int)cr->hidden);
716 static int mode, col, row, page;
718 enum {VIDEO_UNKNOWN, VIDEO_MDA, VIDEO_CGA, VIDEO_EGA, VIDEO_MCGA,
719 VIDEO_VGA, VIDEO_VESA, VIDEO_VESA_800};
722 int get_video(void) /* return -1 on error, or adapter type [0..7] */
724 int adapter = 0; /* 0=unknown, 1=MDA,HGC, 2=CGA, 3=EGA, 4=MCGA, 5=VGA,
725 6=VESA (640), 7=VESA (800) */
729 if(fetch()) return -1;
731 if ((okay=buf.s5.vid)) {
732 /* get current video mode */
733 /* reg.eax = 0x0F00; */
735 printf("get video mode\n");
739 row = v1->vid0F.bl + 1;
747 } else adapter=2; /* at least CGA */
750 /* determine video adapter type */
751 /* reg.eax = 0x1200; call valid on EGA/VGA */
752 /* reg.ebx = 0xFF10; */
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;
765 /* reg.eax = 0x1A00; get display combination */
767 printf("get display combination\n");
768 if ( v2->vid1A.al==0x1A ) {
769 monitor = (v2->vid1A.bx >> ((verbose>=9)*8) ) & 0xFF;
779 adapter = 5; /* VGA */
784 adapter = 4; /* MCGA */
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");
799 video_36_bug = 2; /* mark implemented */
801 if ((v25->vid36.ax & 0xFF) != 0x12) video_36_bug = 1;
803 if (v25->vid36.cx != 0x1234 || v25->vid36.bp != 0x4321)
805 if (v25->vid36.dx != 0x5680) video_36_bug |= 8;
809 /* check for VESA extensions */
811 printf("check VESA present\n");
813 if ((v3->vid4F00.ax == 0x4f) && strncmp("VESA", v3->vid4F00.sig, 4)==0) adapter++;
817 reg.ecx = 0x0101; 640x480x256 */
818 if ((v3->vid101.ax == 0x4f) && (v3->vid101.bits & 0x19) == 0x19) adapter ++;
823 reg.ecx = 0x0103; 800x600x256 */
824 if ((v3->vid103.ax == 0x4f) && (v3->vid103.bits & 0x19) == 0x19) ;
829 printf ("mode = 0x%02x, columns = %d, rows = %d, page = %d\n",
830 mode, col, row, page);
835 /* print VGA/VESA mode information */
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) */
843 if (notice(4)) exit(0);
845 adapter = get_video();
848 printf("No video mode information is available.\n");
852 printf("%s adapter:\n\n", display[adapter]);
853 if (adapter < 3 || (adapter == 3 && egamem < 1) ) {
854 printf("No graphic modes are supported\n");
857 printf(" 640x350x16 mode 0x0010\n");
859 printf(" 640x480x16 mode 0x0012\n\n");
860 printf(" 320x200x256 mode 0x0013\n");
863 printf(" 640x480x256 mode 0x0101\n");
865 printf(" 800x600x256 mode 0x0103\n");
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");
876 /* entry from lilo.c for the '-T' (tell) switch */
877 void probe_tell (char *cmd)
879 struct Probes *pr = list;
883 if (!(verbose>0)) printf("\n");
884 for (; pr->cmd; pr++) {
887 if (pr->cmd[n-1] == '=') arg = cmd+n;
888 if (!strncasecmp(cmd, pr->cmd, n)) {
894 printf("Unrecognized option to '-T' flag\n");
901 int bios_max_devs(void)
903 struct disk_geom geom;
906 if (!fetch() && !get_geom(0x80, &geom)) {
907 i = (buf.s5.disk & 0x7f) + 1;
908 if (geom.n_disks == i) return i;
910 return BIOS_MAX_DEVS;
914 static void dump_pt(unsigned char *pt)
917 for (j=0; j<4; j++) {
918 for (i=0; i<16; i++) {
919 printf(" %02X", (int)(*pt++));
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
933 int bios_device(GEOMETRY *geo, int device)
935 int bios1, bios, match, fd;
937 int mbios[BD_MAX_HARD];
938 struct disk_geom bdata;
940 unsigned char part[PART_TABLE_SIZE];
941 unsigned char extra[8];
945 if (fetch()) return -1;
947 if (verbose>=5) printf("bios_dev: device %04X\n", device);
951 if (!has_partitions(device)) return -1;
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) {
962 mbios[bios-0x80] = bios1 = bios;
966 if (verbose>=5) printf("bios_dev: match on geometry alone (0x%02X)\n",
968 return (geo->device = bios1);
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",
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");
988 printf("serial number = %08X\n", serial);
993 if (verbose>=5) printf("bios_dev: geometry check found %d matches\n", match);
998 /* 'bios' is set leaving the 'for' above */
999 while (--bios >= 0x80) {
1000 get_geom(bios, &bdata);
1002 printf("bios_dev: (0x%02X) vol-ID=%08X *PT=%0*" PRIXPTR "\n",
1003 bios, bdata.serial_no, PTR_WIDTH, (intptr_t)bdata.pt);
1005 dump_pt((void*)bdata.pt);
1008 if ( !memcmp(part,bdata.pt,sizeof(part)) ) {
1012 if ( bdata.serial_no && serial==bdata.serial_no ) {
1017 if (verbose>=5) printf("bios_dev: PT match found %d match%s (0x%02X)\n",
1018 match, match==1 ? "" : "es", bios1&255);
1022 if (verbose>=5) printf("bios_dev: S/N match found %d match%s (0x%02X)\n",
1023 match, match==1 ? "" : "es", bios1);
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;
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;
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);
1046 geo->cylinders = nblocks;
1049 return (geo->device = bios1);
1057 static void do_bitmap(char *file)
1059 printf("Color, Positioning, and Timer information for file: %s\n", file);
1060 printf("...<unimplemented>...\n");
1064 static unsigned char dl,dh;
1066 static int get_bios(void)
1068 if (fetch() || buf.s5.version<5) return -1;
1070 if (verbose>=5) printf("get_bios 1\n");
1074 if (verbose>=5) printf("get_bios 2\n");
1076 dh = eq->boot_dx >> 8;
1082 void check_bios(void)
1085 if (verbose>=5) printf("check_bios 1\n");
1087 if (bios_passes_dl == DL_NOT_SET) {
1088 bios_passes_dl = DL_UNKNOWN;
1090 if (get_bios() < 0) return;
1092 if (verbose>=5) printf("check_bios 2\n");
1095 if ( !((dh>=0x80 && dh<=DEV_MASK) || dh==0) )
1096 bios_passes_dl = DL_BAD;
1098 else if ( dl>=0x80 && dl<=DEV_MASK ) bios_passes_dl = DL_MAYBE;
1099 else if ( dl > 0 ) bios_passes_dl = DL_BAD;
1101 /* already set, leave alone */
1103 if (verbose>=5) printf("check_bios 3 bios_passes_dl=%d\n", (int)bios_passes_dl);
1112 static char *ord[] =
1113 { "first", "second", "3rd", "4th", "5th", "6th", "7th", "8th",
1114 "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th" };
1118 printf("No information available on the state of DL at boot.\n");
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;
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) {
1134 if (code>=0 && code<2) { cp="floppy"; cn=ord[code]; }
1135 if (code>=0x80 && code<=0x8f) { cp="hard"; cn=ord[code&15]; }
1137 printf(" If you\nactually booted from the %s %s drive, then this assumption is okay.",
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");
1149 static void do_volid(void)
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];
1156 if (notice(4)) exit(0);
1158 printf("\n BIOS Volume ID\n\n");
1161 for (bios=0x80; bios<0x80+MAX_BIOS_DEVICES && !get_geom(bios, &geom); bios++) {
1165 if (i==0 || i==2) geom.serial_no = 0;
1166 if (i==4) geom.serial_no = serial[2];
1169 serial[i] = geom.serial_no;
1170 valid[i] = sv = serial_valid(geom.serial_no, bios);
1172 if (sv) for (k=0; k<i; k++) uq &= (geom.serial_no != serial[k]);
1177 printf(" 0x%02X %08X %s%s\n", i+0x80, serial[i],
1178 uq ? "" : "*", sv ? "" : "-");
1181 printf("\nVolume ID's are%s unique.\n", unique ? " all" : " NOT");
1183 printf(" '-' marks an invalid Volume ID which will be automatically updated\n"
1184 "\tthe next time /sbin/lilo is executed.\n");
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"