Initial code release
[external/syslinux.git] / dosutil / mdiskchk.c
1 /* -*- c -*- ------------------------------------------------------------- *
2  *
3  *   Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
4  *   Portions copyright 2010 Shao Miller
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  * mdiskchk.c
16  *
17  * DOS program to check for the existence of a memdisk.
18  *
19  * This program can be compiled for DOS with the OpenWatcom compiler
20  * (http://www.openwatcom.org/):
21  *
22  * wcl -3 -osx -mt mdiskchk.c
23  */
24
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <i86.h>                /* For MK_FP() */
29
30 typedef unsigned long uint32_t;
31 typedef unsigned short uint16_t;
32 typedef unsigned char uint8_t;
33
34 /* Pull in MEMDISK common structures */
35 #include "../memdisk/mstructs.h"
36
37 struct memdiskinfo {
38     struct mdi mdi;
39
40     /* We add our own fields at the end */
41     int cylinders;
42     int heads;
43     int sectors;
44 };
45
46 struct memdiskinfo *query_memdisk(int drive)
47 {
48     static struct memdiskinfo mm;
49     uint32_t _eax, _ebx, _ecx, _edx;
50     uint16_t _es, _di;
51     unsigned char _dl = drive;
52     uint16_t bytes;
53
54     __asm {
55         .386;
56         mov eax, 454d0800h;
57         mov ecx, 444d0000h;
58         mov edx, 53490000h;
59         mov dl, _dl;
60         mov ebx, 3f4b0000h;
61         int 13h;
62         mov _eax, eax;
63         mov _ecx, ecx;
64         mov _edx, edx;
65         mov _ebx, ebx;
66         mov _es, es;
67         mov _di, di;
68     }
69
70     if (_eax >> 16 != 0x4d21 ||
71         _ecx >> 16 != 0x4d45 || _edx >> 16 != 0x4944 || _ebx >> 16 != 0x4b53)
72         return NULL;
73
74     memset(&mm, 0, sizeof mm);
75
76     bytes = *(uint16_t far *) MK_FP(_es, _di);
77
78     /* 27 is the most we know how to handle */
79     if (bytes > 27)
80         bytes = 27;
81
82     _fmemcpy((void far *)&mm, (void far *)MK_FP(_es, _di), bytes);
83
84     mm.cylinders = ((_ecx >> 8) & 0xff) + ((_ecx & 0xc0) << 2) + 1;
85     mm.heads = ((_edx >> 8) & 0xff) + 1;
86     mm.sectors = (_ecx & 0x3f);
87
88     return &mm;
89 }
90
91 const char *bootloadername(uint8_t id)
92 {
93     static const struct {
94         uint8_t id, mask;
95         const char *name;
96     } *lp, list[] = {
97         {0x00, 0xf0, "LILO"}, 
98         {0x10, 0xf0, "LOADLIN"},
99         {0x31, 0xff, "SYSLINUX"},
100         {0x32, 0xff, "PXELINUX"},
101         {0x33, 0xff, "ISOLINUX"},
102         {0x34, 0xff, "EXTLINUX"},
103         {0x30, 0xf0, "SYSLINUX family"},
104         {0x40, 0xf0, "Etherboot"},
105         {0x50, 0xf0, "ELILO"},
106         {0x70, 0xf0, "GrUB"},
107         {0x80, 0xf0, "U-Boot"},
108         {0xA0, 0xf0, "Gujin"},
109         {0xB0, 0xf0, "Qemu"},
110         {0x00, 0x00, "unknown"}
111     };
112
113     for (lp = list;; lp++) {
114         if (((id ^ lp->id) & lp->mask) == 0)
115             return lp->name;
116     }
117 }
118
119 /* The function type for an output function */
120 #define OUTPUT_FUNC_DECL(x) \
121 void x(const int d, const struct memdiskinfo * const m)
122 typedef OUTPUT_FUNC_DECL((*output_func));
123
124 /* Show MEMDISK information for the passed structure */
125 static OUTPUT_FUNC_DECL(normal_output)
126 {
127     if (m == NULL)
128         return;
129     printf("Drive %02X is MEMDISK %u.%02u:\n"
130            "\tAddress = 0x%08lx, len = %lu sectors, chs = %u/%u/%u,\n"
131            "\tloader = 0x%02x (%s),\n"
132            "\tcmdline = %Fs\n",
133            d, m->mdi.version_major, m->mdi.version_minor,
134            m->mdi.diskbuf, m->mdi.disksize, m->cylinders, m->heads, m->sectors,
135            m->mdi.bootloaderid, bootloadername(m->mdi.bootloaderid),
136            MK_FP(m->mdi.cmdline.seg_off.segment,
137                  m->mdi.cmdline.seg_off.offset));
138 }
139
140 /* Yield DOS SET command(s) as output for each MEMDISK kernel argument */
141 static OUTPUT_FUNC_DECL(batch_output)
142 {
143     if (m != NULL) {
144         char buf[256], *bc;
145         const char far *c =
146             MK_FP(m->mdi.cmdline.seg_off.segment,
147                   m->mdi.cmdline.seg_off.offset);
148         const char *have_equals, is_set[] = "=1";
149
150         while (*c != '\0') {
151             /* Skip whitespace */
152             while (isspace(*c))
153                 c++;
154             if (*c == '\0')
155                 /* Trailing whitespace.  That's enough processing */
156                 break;
157             /* Walk the kernel arguments while filling the buffer,
158              * looking for space or NUL or checking for a full buffer
159              */
160             bc = buf;
161             have_equals = is_set;
162             while ((*c != '\0') && !isspace(*c) &&
163                    (bc < &buf[sizeof(buf) - 1])) {
164                 /* Check if the param is "x=y" */
165                 if (*c == '=')
166                     /* "=1" not needed */
167                     have_equals = &is_set[sizeof(is_set) - 1];
168                 *bc = *c;
169                 c++;
170                 bc++;
171             }
172             /* Found the end of the parameter and optional value sequence */
173             *bc = '\0';
174             printf("set %s%s\n", buf, have_equals);
175         }
176     }
177 }
178
179 /* We do not output batch file output by default.  We show MEMDISK info */
180 static output_func show_memdisk = normal_output;
181
182 /* A generic function type */
183 #define MDISKCHK_FUNC_DECL(x) \
184 void x(void)
185 typedef MDISKCHK_FUNC_DECL((*mdiskchk_func));
186
187 static MDISKCHK_FUNC_DECL(do_nothing)
188 {
189     return;
190 }
191
192 static MDISKCHK_FUNC_DECL(show_usage)
193 {
194     printf("\nUsage: mdiskchk [--safe-hooks] [--mbfts] [--batch-output]\n"
195            "\n"
196            "Action: --safe-hooks . . Will scan INT 13h \"safe hook\" chain\n"
197            "        --mbfts . . . .  Will scan memory for MEMDISK mBFTs\n"
198            "        --batch-output . Will output SET command output based\n"
199            "                         on MEMDISK kernel arguments\n"
200            "        --no-sequential  Suppresses probing all drive numbers\n");
201 }
202
203 /* Search memory for mBFTs and report them via the output method */
204 static MDISKCHK_FUNC_DECL(show_mbfts)
205 {
206     const uint16_t far * const free_base_mem =
207         MK_FP(0x0040, 0x0013);
208     int seg;
209     uint8_t chksum;
210     uint32_t i;
211     const struct mBFT far *mbft;
212     struct memdiskinfo m;
213     struct patch_area far *patch_area;
214
215     for (seg = *free_base_mem / 16; seg < 0x9FFF; seg++) {
216         mbft = MK_FP(seg, 0);
217         /* Check for signature */
218         if (mbft->acpi.signature[0] != 'm' ||
219             mbft->acpi.signature[1] != 'B' ||
220             mbft->acpi.signature[2] != 'F' ||
221             mbft->acpi.signature[3] != 'T')
222             continue;
223         if (mbft->acpi.length != sizeof(struct mBFT))
224             continue;
225         /* Check sum */
226         chksum = 0;
227         for (i = 0; i < sizeof(struct mBFT); i++)
228             chksum += ((const uint8_t far *)mbft)[i];
229         if (chksum)
230             continue;
231         /* Copy the MDI from the mBFT */
232         _fmemcpy((void far *)&m, &mbft->mdi, sizeof(struct mdi));
233         /* Adjust C/H/S since we actually know
234          * it directly for any MEMDISK with an mBFT
235          */
236         patch_area = (struct patch_area far *)&mbft->mdi;
237         m.cylinders = patch_area->cylinders;
238         m.heads = patch_area->heads;
239         m.sectors = patch_area->sectors;
240         show_memdisk(patch_area->driveno, &m);
241     }
242 }
243
244 /* Walk the "safe hook" chain as far as possible
245  * and report MEMDISKs that we find via the output method
246  */
247 static MDISKCHK_FUNC_DECL(show_safe_hooks)
248 {
249     const real_addr_t far * const int13 =
250         MK_FP(0x0000, 0x0013 * sizeof(real_addr_t));
251     const struct safe_hook far *hook =
252         MK_FP(int13->seg_off.segment, int13->seg_off.offset);
253
254     while ((hook->signature[0] == '$') &&
255            (hook->signature[1] == 'I') &&
256            (hook->signature[2] == 'N') &&
257            (hook->signature[3] == 'T') &&
258            (hook->signature[4] == '1') &&
259            (hook->signature[5] == '3') &&
260            (hook->signature[6] == 'S') &&
261            (hook->signature[7] == 'F')) {
262         /* Found a valid "safe hook" */
263         if ((hook->vendor[0] == 'M') &&
264             (hook->vendor[1] == 'E') &&
265             (hook->vendor[2] == 'M') &&
266             (hook->vendor[3] == 'D') &&
267             (hook->vendor[4] == 'I') &&
268             (hook->vendor[5] == 'S') &&
269             (hook->vendor[6] == 'K')) {
270             /* Found a valid MEMDISK "safe hook".  It will have an mBFT */
271             const struct mBFT far *mbft;
272             struct memdiskinfo m;
273             struct patch_area far *patch_area;
274
275             /* Copy the MDI from the mBFT.  Offset is a misnomer here */
276             mbft = MK_FP(hook->mbft >> 4, 0);   /* Always aligned */
277             _fmemcpy((void far *)&m, &mbft->mdi, sizeof(struct mdi));
278             /* Adjust C/H/S since we actually know
279              * it directly for any MEMDISK with an mBFT
280              */
281             patch_area = (struct patch_area far *)&mbft->mdi;
282             m.cylinders = patch_area->cylinders;
283             m.heads = patch_area->heads;
284             m.sectors = patch_area->sectors;
285             show_memdisk(patch_area->driveno, &m);
286         } /* if */
287         /* Step to the next hook in the "safe hook" chain */
288         hook = MK_FP(hook->old_hook.seg_off.segment,
289                      hook->old_hook.seg_off.offset);
290     } /* while */
291 }
292
293 int main(int argc, char *argv[])
294 {
295     int d;
296     int found = 0;
297     int sequential_scan = 1;    /* Classic behaviour */
298     const struct memdiskinfo *m;
299
300     /* Default behaviour */
301     mdiskchk_func usage = do_nothing,
302         safe_hooks = do_nothing,
303         mbfts = do_nothing;
304
305     /* For each argument */
306     while (--argc) {
307         /* Argument should begin with one of these chars */
308         if ((*argv[argc] != '/') && (*argv[argc] != '-')) {
309             /* It doesn't.  Print usage soon */
310             usage = show_usage;
311             break;
312         }
313         argv[argc]++;
314
315         /* Next char might be '-' as in "--safe-hooks" */
316         if (*argv[argc] == '-')
317             argv[argc]++;
318
319         switch (*argv[argc]) {
320             case 'S':
321             case 's':
322                 safe_hooks = show_safe_hooks;
323                 break;
324             case 'M':
325             case 'm':
326                 mbfts = show_mbfts;
327                 break;
328             case 'B':
329             case 'b':
330                 show_memdisk = batch_output;
331                 break;
332             case 'N':
333             case 'n':
334                 sequential_scan = 0;
335                 break;
336             default:
337                 usage = show_usage;
338         } /* switch */
339    } /* while */
340
341     safe_hooks();
342     mbfts();
343     if (!sequential_scan)
344         goto skip_sequential;
345     for (d = 0; d <= 0xff; d++) {
346         m = query_memdisk(d);
347         if (m != NULL) {
348             found++;
349             show_memdisk(d, m);
350         }
351     }
352 skip_sequential:
353     usage();
354
355     return found;
356 }
357