1 /* -*- c -*- ------------------------------------------------------------- *
3 * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
4 * Portions copyright 2010 Shao Miller
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.
12 * ----------------------------------------------------------------------- */
17 * DOS program to check for the existence of a memdisk.
19 * This program can be compiled for DOS with the OpenWatcom compiler
20 * (http://www.openwatcom.org/):
22 * wcl -3 -osx -mt mdiskchk.c
28 #include <i86.h> /* For MK_FP() */
30 typedef unsigned long uint32_t;
31 typedef unsigned short uint16_t;
32 typedef unsigned char uint8_t;
34 /* Pull in MEMDISK common structures */
35 #include "../memdisk/mstructs.h"
40 /* We add our own fields at the end */
46 struct memdiskinfo *query_memdisk(int drive)
48 static struct memdiskinfo mm;
49 uint32_t _eax, _ebx, _ecx, _edx;
51 unsigned char _dl = drive;
70 if (_eax >> 16 != 0x4d21 ||
71 _ecx >> 16 != 0x4d45 || _edx >> 16 != 0x4944 || _ebx >> 16 != 0x4b53)
74 memset(&mm, 0, sizeof mm);
76 bytes = *(uint16_t far *) MK_FP(_es, _di);
78 /* 27 is the most we know how to handle */
82 _fmemcpy((void far *)&mm, (void far *)MK_FP(_es, _di), bytes);
84 mm.cylinders = ((_ecx >> 8) & 0xff) + ((_ecx & 0xc0) << 2) + 1;
85 mm.heads = ((_edx >> 8) & 0xff) + 1;
86 mm.sectors = (_ecx & 0x3f);
91 const char *bootloadername(uint8_t id)
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"}
113 for (lp = list;; lp++) {
114 if (((id ^ lp->id) & lp->mask) == 0)
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));
124 /* Show MEMDISK information for the passed structure */
125 static OUTPUT_FUNC_DECL(normal_output)
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"
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));
140 /* Yield DOS SET command(s) as output for each MEMDISK kernel argument */
141 static OUTPUT_FUNC_DECL(batch_output)
146 MK_FP(m->mdi.cmdline.seg_off.segment,
147 m->mdi.cmdline.seg_off.offset);
148 const char *have_equals, is_set[] = "=1";
151 /* Skip whitespace */
155 /* Trailing whitespace. That's enough processing */
157 /* Walk the kernel arguments while filling the buffer,
158 * looking for space or NUL or checking for a full buffer
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" */
166 /* "=1" not needed */
167 have_equals = &is_set[sizeof(is_set) - 1];
172 /* Found the end of the parameter and optional value sequence */
174 printf("set %s%s\n", buf, have_equals);
179 /* We do not output batch file output by default. We show MEMDISK info */
180 static output_func show_memdisk = normal_output;
182 /* A generic function type */
183 #define MDISKCHK_FUNC_DECL(x) \
185 typedef MDISKCHK_FUNC_DECL((*mdiskchk_func));
187 static MDISKCHK_FUNC_DECL(do_nothing)
192 static MDISKCHK_FUNC_DECL(show_usage)
194 printf("\nUsage: mdiskchk [--safe-hooks] [--mbfts] [--batch-output]\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");
203 /* Search memory for mBFTs and report them via the output method */
204 static MDISKCHK_FUNC_DECL(show_mbfts)
206 const uint16_t far * const free_base_mem =
207 MK_FP(0x0040, 0x0013);
211 const struct mBFT far *mbft;
212 struct memdiskinfo m;
213 struct patch_area far *patch_area;
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')
223 if (mbft->acpi.length != sizeof(struct mBFT))
227 for (i = 0; i < sizeof(struct mBFT); i++)
228 chksum += ((const uint8_t far *)mbft)[i];
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
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);
244 /* Walk the "safe hook" chain as far as possible
245 * and report MEMDISKs that we find via the output method
247 static MDISKCHK_FUNC_DECL(show_safe_hooks)
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);
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;
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
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);
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);
293 int main(int argc, char *argv[])
297 int sequential_scan = 1; /* Classic behaviour */
298 const struct memdiskinfo *m;
300 /* Default behaviour */
301 mdiskchk_func usage = do_nothing,
302 safe_hooks = do_nothing,
305 /* For each argument */
307 /* Argument should begin with one of these chars */
308 if ((*argv[argc] != '/') && (*argv[argc] != '-')) {
309 /* It doesn't. Print usage soon */
315 /* Next char might be '-' as in "--safe-hooks" */
316 if (*argv[argc] == '-')
319 switch (*argv[argc]) {
322 safe_hooks = show_safe_hooks;
330 show_memdisk = batch_output;
343 if (!sequential_scan)
344 goto skip_sequential;
345 for (d = 0; d <= 0xff; d++) {
346 m = query_memdisk(d);