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
);