Home | History | Annotate | Download | only in dosutil
      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 
    358