syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / com32 / modules / chain.c
1 #ident "$Id: chain.c,v 1.4 2005/01/12 00:34:54 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2003-2005 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * chain.c
16  *
17  * Chainload a hard disk (currently rather braindead.)
18  *
19  * Usage: chain hd<disk#> [<partition>]
20  *        chain fd<disk#>
21  *
22  * ... e.g. "chain hd0 1" will boot the first partition on the first hard disk.
23  *
24  * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
25  */
26
27 #include <com32.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <console.h>
33
34 #define SECTOR 512              /* bytes/sector */
35
36 static inline void error(const char *msg)
37 {
38   fputs(msg, stderr);
39 }
40
41 /*
42  * Call int 13h, but with retry on failure.  Especially floppies need this.
43  */
44 int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
45 {
46   int retry = 6;                /* Number of retries */
47   com32sys_t tmpregs;
48   
49   if ( !outreg ) outreg = &tmpregs;
50
51   while ( retry-- ) {
52     __intcall(0x13, inreg, outreg);
53     if ( !(outreg->eflags.l & EFLAGS_CF) )
54       return 0;                 /* CF=0, OK */
55   }
56
57   return -1;                    /* Error */
58 }
59
60 /*
61  * Query disk parameters and EBIOS availability for a particular disk.
62  */
63 struct diskinfo {
64   int disk;
65   int ebios;                    /* EBIOS supported on this disk */
66   int cbios;                    /* CHS geometry is valid */
67   int head;
68   int sect;
69 } disk_info;
70
71 int get_disk_params(int disk)
72 {
73   static com32sys_t getparm, parm, getebios, ebios;
74
75   disk_info.disk = disk;
76
77   /* Get EBIOS support */
78   getebios.eax.w[0] = 0x4100;
79   getebios.ebx.w[0] = 0x55aa;
80   getebios.edx.b[0] = disk;
81   getebios.eflags.b[0] = 0x3;   /* CF set */
82
83   __intcall(0x13, &getebios, &ebios);
84
85   if ( !(ebios.eflags.l & EFLAGS_CF) &&
86        ebios.ebx.w[0] == 0xaa55 &&
87        (ebios.ecx.b[0] & 1) ) {
88     disk_info.ebios = 1;
89   }
90
91   /* Get disk parameters -- really only useful for
92      hard disks, but if we have a partitioned floppy
93      it's actually our best chance... */
94   getparm.eax.b[1] = 0x08;
95   getparm.edx.b[0] = disk;
96
97   __intcall(0x13, &getparm, &parm);
98
99   if ( parm.eflags.l & EFLAGS_CF )
100     return disk_info.ebios ? 0 : -1;
101   
102   disk_info.head = parm.edx.b[1]+1;
103   disk_info.sect = parm.ecx.b[0] & 0x3f;
104   if ( disk_info.sect == 0 ) {
105     disk_info.sect = 1;
106   } else {
107     disk_info.cbios = 1;        /* Valid geometry */
108   }
109
110   return 0;
111 }
112
113 /*
114  * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
115  */
116 struct ebios_dapa {
117   uint16_t len;
118   uint16_t count;
119   uint16_t off;
120   uint16_t seg;
121   uint64_t lba;
122 } *dapa;
123
124 int read_sector(void *buf, unsigned int lba)
125 {
126   com32sys_t inreg;
127
128   memset(&inreg, 0, sizeof inreg);
129
130   if ( disk_info.ebios ) {
131     dapa->len = sizeof(*dapa);
132     dapa->count = 1;            /* 1 sector */
133     dapa->off = OFFS(buf);
134     dapa->seg = SEG(buf);
135     dapa->lba = lba;
136     
137     inreg.esi.w[0] = OFFS(dapa);
138     inreg.ds       = SEG(dapa);
139     inreg.edx.b[0] = disk_info.disk;
140     inreg.eax.b[1] = 0x42;      /* Extended read */
141   } else {
142     unsigned int c, h, s, t;
143
144     if ( !disk_info.cbios ) {
145       /* We failed to get the geometry */
146
147       if ( lba )
148         return -1;              /* Can only read MBR */
149
150       s = 1;  h = 0;  c = 0;
151     } else {
152       s = (lba % disk_info.sect) + 1;
153       t = lba / disk_info.sect; /* Track = head*cyl */
154       h = t % disk_info.head;
155       c = t / disk_info.head;
156     }
157
158     if ( s > 63 || h > 256 || c > 1023 )
159       return -1;
160
161     inreg.eax.w[0] = 0x0201;    /* Read one sector */
162     inreg.ecx.b[1] = c & 0xff;
163     inreg.ecx.b[0] = s + (c >> 6);
164     inreg.edx.b[1] = h;
165     inreg.edx.b[0] = disk_info.disk;
166     inreg.ebx.w[0] = OFFS(buf);
167     inreg.es       = SEG(buf);
168   }
169   
170   return int13_retry(&inreg, NULL);
171 }
172
173 /* A DOS partition table entry */
174 struct part_entry {
175   uint8_t active_flag;          /* 0x80 if "active" */
176   uint8_t start_head;
177   uint8_t start_sect;
178   uint8_t start_cyl;
179   uint8_t ostype;
180   uint8_t end_head;
181   uint8_t end_sect;
182   uint8_t end_cyl;
183   uint32_t start_lba;
184   uint32_t length;
185 } __attribute__((packed));
186
187
188 /* Search for a logical partition.  Logical partitions are actually implemented
189    as recursive partition tables; theoretically they're supposed to form a linked
190    list, but other structures have been seen.
191
192    To make things extra confusing: data partition offsets are relative to where
193    the data partition record is stored, whereas extended partition offsets
194    are relative to the beginning of the extended partition all the way back
195    at the MBR... but still not absolute! */
196
197 int nextpart;                   /* Number of the next logical partition */
198
199 struct part_entry *find_logical_partition(int whichpart, char *table, struct part_entry *self, struct part_entry *root)
200 {
201   struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
202   struct part_entry *found;
203   int i;
204
205   if ( *(uint16_t *)(ptab + 0x1fe) != 0xaa55 )
206     return NULL;                /* Signature missing */
207
208   /* We are assumed to already having enumerated all the data partitions
209      in this table if this is the MBR.  For MBR, self == NULL. */
210
211   if ( self ) {
212     /* Scan the data partitions. */
213
214     for ( i = 0 ; i < 4 ; i++ ) {
215       if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
216            ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
217         continue;               /* Skip empty or extended partitions */
218
219       if ( !ptab[i].length )
220         continue;
221
222       /* Adjust the offset to account for the extended partition itself */
223       ptab[i].start_lba += self->start_lba;
224
225       /* Sanity check entry: must not extend outside the extended partition.
226          This is necessary since some OSes put crap in some entries. */
227       if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
228            ptab[i].start_lba >= self->start_lba + self->length )
229         continue;
230
231       /* OK, it's a data partition.  Is it the one we're looking for? */
232       if ( nextpart++ == whichpart )
233         return &ptab[i];
234     }
235   }
236
237   /* Scan the extended partitions. */
238   for ( i = 0 ; i < 4 ; i++ ) {
239     if ( ptab[i].ostype != 0x05 &&
240          ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
241       continue;         /* Skip empty or data partitions */
242     
243     if ( !ptab[i].length )
244       continue;
245     
246     /* Adjust the offset to account for the extended partition itself */
247     if ( root )
248       ptab[i].start_lba += root->start_lba;
249     
250     /* Sanity check entry: must not extend outside the extended partition.
251        This is necessary since some OSes put crap in some entries. */
252     if ( root )
253       if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
254            ptab[i].start_lba >= root->start_lba + root->length )
255         continue;
256     
257     /* Process this partition */
258     if ( read_sector(table+SECTOR, ptab[i].start_lba) )
259       continue;                 /* Read error, must be invalid */
260
261     if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
262                                          root ? root : &ptab[i])) )
263       return found;
264   }
265
266   /* If we get here, there ain't nothing... */
267   return NULL;
268 }
269
270
271 int main(int argc, char *argv[])
272 {
273   char *mbr, *boot_sector = NULL;
274   struct part_entry *partinfo;
275   char *drivename, *partition;
276   int hd, drive, whichpart;
277   static com32sys_t inreg;      /* In bss, so zeroed automatically */
278
279   openconsole(&dev_null_r, &dev_stdcon_w);
280
281   if ( argc < 2 ) {
282     error("Usage: chain.c32 (hd|fd)# [partition]\n");
283     goto bail;
284   }
285
286   drivename = argv[1];
287   partition = argv[2];          /* Possibly null */
288
289   hd = 0;
290   if ( (drivename[0] == 'h' || drivename[0] == 'f') &&
291        drivename[1] == 'd' ) {
292     hd = drivename[0] == 'h';
293     drivename += 2;
294   }
295   drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
296   whichpart = 0;                /* Default */
297
298   if ( partition )
299     whichpart = strtoul(partition, NULL, 0);
300
301   if ( !(drive & 0x80) && whichpart ) {
302     error("Warning: Partitions of floppy devices may not work\n");
303   }
304
305   /* Divvy up the bounce buffer.  To keep things sector-
306      aligned, give the EBIOS DAPA the first sector, then
307      the MBR next, and the rest is used for the partition-
308      chasing stack. */
309   dapa = (struct ebios_dapa *)__com32.cs_bounce;
310   mbr  = (char *)__com32.cs_bounce + SECTOR;
311
312   /* Get the disk geometry (not needed for MBR) */
313   if ( get_disk_params(drive) && whichpart ) {
314     error("Cannot get disk parameters\n");
315     goto bail;
316   }
317
318   /* Get MBR */
319   if ( read_sector(mbr, 0) ) {
320     error("Cannot read Master Boot Record\n");
321     goto bail;
322   }
323
324   if ( whichpart == 0 ) {
325     /* Boot the MBR */
326     partinfo = NULL;
327     boot_sector = mbr;
328   } else if ( whichpart <= 4 ) {
329     /* Boot a primary partition */
330     partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
331     if ( partinfo->ostype == 0 ) {
332       error("Invalid primary partition\n");
333       goto bail;
334     }
335   } else {
336     /* Boot a logical partition */
337
338     nextpart = 5;
339     partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
340
341     if ( !partinfo || partinfo->ostype == 0 ) {
342       error("Requested logical partition not found\n");
343       goto bail;
344     }
345   }
346
347   /* Do the actual chainloading */
348   if ( partinfo ) {
349     /* Actually read the boot sector */
350     /* Pick the first buffer that isn't already in use */
351     boot_sector = (char *)(((unsigned long)partinfo + 511) & ~511);
352     if ( read_sector(boot_sector, partinfo->start_lba) ) {
353       error("Cannot read boot sector\n");
354       goto bail;
355     }
356
357     /* 0x7BE is the canonical place for the first partition entry. */
358     inreg.esi.w[0] = 0x7be;
359     memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
360   }
361   
362   fputs("Booting...\n", stdout);
363
364   inreg.eax.w[0] = 0x000d;      /* Clean up and chain boot */
365   inreg.edx.w[0] = 0;           /* Should be 3 for "keeppxe" */
366   inreg.edi.l    = (uint32_t)boot_sector;
367   inreg.ecx.l    = SECTOR;      /* One sector */
368   inreg.ebx.b[0] = drive;       /* DL = drive no */
369
370   __intcall(0x22, &inreg, NULL);
371
372   /* If we get here, badness happened */
373   error("Chainboot failed!\n");
374
375 bail:
376   return 255;
377 }