/* ----------------------------------------------------------------------- *
*
* Copyright 2011 Shao Miller - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston MA 02110-1301, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/****
* @file ifmemdsk.c
*
* This COM32 module detects if there are MEMDISKs established.
*/
static const char usage_text[] = "\
Usage:\n\
ifmemdsk.c32 [<option> [...]] --info [<option> [...]]\n\
ifmemdsk.c32 [<option> [...]] [<detected_cmd>] -- [<not_detected_cmd>]\n\
\n\
Options:\n\
--info . . . . . Displays info about MEMDISK(s)\n\
--safe-hooks . . Will scan INT 13h \"safe hook\" chain\n\
--mbfts . . . . . Will scan memory for MEMDISK mBFTs\n\
--no-sequential Suppresses probing all drive numbers\n\
\n\
If a MEMDISK is found, or if a particular MEMDISK is sought by the options\n\
and is found, then the 'detected_cmd' action will be taken, else the\n\
'not_detected_cmd' action will be taken.\n\
\n";
#include <stdio.h>
#include <string.h>
#include <alloca.h>
#include <com32.h>
#include <console.h>
#include <syslinux/boot.h>
/* Pull in MEMDISK common structures */
#include "../../memdisk/mstructs.h"
/*** Macros */
#define M_GET_DRIVE_PARAMS (0x08)
#define M_SEGOFFTOPTR(seg, off) (((seg) << 4) + (off))
#define M_INT13H M_SEGOFFTOPTR(0x0000, 0x0013 * 4)
#define M_FREEBASEMEM M_SEGOFFTOPTR(0x0040, 0x0013)
#define M_TOP M_SEGOFFTOPTR(0x9FFF, 0x0000)
/*** Object types */
typedef struct mdi s_mdi;
typedef real_addr_t u_segoff;
typedef struct safe_hook s_safe_hook;
typedef struct mBFT s_mbft;
/*** Function types */
typedef int f_find(void);
/*** Function declarations */
static const s_mdi * installation_check(int);
static f_find scan_drives;
static f_find walk_safe_hooks;
static const s_safe_hook * is_safe_hook(const void *);
static const s_mdi * is_memdisk_hook(const s_safe_hook *);
static f_find scan_mbfts;
static const s_mbft * is_mbft(const void *);
static f_find do_nothing;
static void memdisk_info(const s_mdi *);
static void boot_args(char **);
static const char * bootloadername(uint8_t);
/*** Structure/union definitions */
/*** Objects */
static int show_info = 0;
/*** Function definitions */
int main(int argc, char ** argv) {
static f_find * do_scan_drives = scan_drives;
static f_find * do_walk_safe_hooks = do_nothing;
static f_find * do_scan_mbfts = do_nothing;
char ** detected_cmd;
char ** not_detected_cmd;
char ** cmd;
char ** cur_arg;
int show_usage;
int found;
(void) argc;
openconsole(&dev_null_r, &dev_stdcon_w);
detected_cmd = NULL;
not_detected_cmd = NULL;
show_usage = 1;
for (cur_arg = argv + 1; *cur_arg; ++cur_arg) {
/* Check for command divider */
if (!strcmp(*cur_arg, "--")) {
show_usage = 0;
*cur_arg = NULL;
not_detected_cmd = cur_arg + 1;
break;
}
/* Check for '--info' */
if (!strcmp(*cur_arg, "--info")) {
show_usage = 0;
show_info = 1;
continue;
}
/* Other options */
if (!strcmp(*cur_arg, "--no-sequential")) {
do_scan_drives = do_nothing;
continue;
}
if (!strcmp(*cur_arg, "--safe-hooks")) {
do_walk_safe_hooks = walk_safe_hooks;
continue;
}
if (!strcmp(*cur_arg, "--mbfts")) {
do_scan_mbfts = scan_mbfts;
continue;
}
/* Check for invalid option */
if (!memcmp(*cur_arg, "--", sizeof "--" - 1)) {
puts("Invalid option!");
show_usage = 1;
break;
}
/* Set 'detected_cmd' if it's null */
if (!detected_cmd)
detected_cmd = cur_arg;
continue;
}
if (show_usage) {
fprintf(stderr, usage_text);
return 1;
}
found = 0;
found += do_walk_safe_hooks();
found += do_scan_mbfts();
found += do_scan_drives();
cmd = found ? detected_cmd : not_detected_cmd;
if (cmd && *cmd)
boot_args(cmd);
return 0;
}
static const s_mdi * installation_check(int drive) {
com32sys_t params, results;
int found;
/* Set parameters for INT 0x13 call */
memset(¶ms, 0, sizeof params);
params.eax.w[0] = M_GET_DRIVE_PARAMS << 8;
params.edx.w[0] = drive;
/* 'ME' 'MD' 'IS' 'K?' */
params.eax.w[1] = 0x454D;
params.ecx.w[1] = 0x444D;
params.edx.w[1] = 0x5349;
params.ebx.w[1] = 0x3F4B;
/* Perform the call */
__intcall(0x13, ¶ms, &results);
/* Check result */
found = (
/* '!M' 'EM' 'DI' 'SK' */
results.eax.w[1] == 0x4D21 &&
results.ecx.w[1] == 0x4D45 &&
results.edx.w[1] == 0x4944 &&
results.ebx.w[1] == 0x4B53
);
if (found)
return MK_PTR(results.es, results.edi.w[0]);
return NULL;
}
static int scan_drives(void) {
int found, drive;
const s_mdi * mdi;
for (found = drive = 0; drive <= 0xFF; ++drive) {
mdi = installation_check(drive);
if (!mdi)
continue;
memdisk_info(mdi);
++found;
continue;
}
return found;
}
static int walk_safe_hooks(void) {
static const u_segoff * const int13 = (void *) M_INT13H;
const void * addr;
int found;
const s_safe_hook * hook;
const s_mdi * mdi;
/* INT 0x13 vector */
addr = MK_PTR(int13->seg_off.segment, int13->seg_off.offset);
found = 0;
while (addr) {
hook = is_safe_hook(addr);
if (!hook)
break;
mdi = is_memdisk_hook(hook);
if (mdi) {
memdisk_info(mdi);
++found;
}
addr = MK_PTR(
hook->old_hook.seg_off.segment,
hook->old_hook.seg_off.offset
);
continue;
}
return found;
}
static const s_safe_hook * is_safe_hook(const void * addr) {
static const char magic[] = "$INT13SF";
const s_safe_hook * const test = addr;
if (memcmp(test->signature, magic, sizeof magic - 1))
return NULL;
return test;
}
static const s_mdi * is_memdisk_hook(const s_safe_hook * hook) {
static const char magic[] = "MEMDISK";
const s_mbft * mbft;
if (memcmp(hook->vendor, magic, sizeof magic - 1))
return NULL;
/* An mBFT is always aligned */
mbft = MK_PTR(hook->mbft >> 4, 0);
return &mbft->mdi;
}
static int scan_mbfts(void) {
static const uint16_t * const free_base_mem = (void *) M_FREEBASEMEM;
static const void * const top = (void *) M_TOP;
const void * addr;
const s_mbft * mbft;
int found;
found = 0;
for (addr = MK_PTR(*free_base_mem << 4, 0); addr < top; addr += 1 << 4) {
if (!(mbft = is_mbft(addr)))
continue;
memdisk_info(&mbft->mdi);
++found;
continue;
}
return found;
}
static const s_mbft * is_mbft(const void * addr) {
static const char magic[] = "mBFT";
const s_mbft * const test = addr;
const uint8_t * ptr, * end;
uint8_t chksum;
if (memcmp(test->acpi.signature, magic, sizeof magic - 1))
return NULL;
if (test->acpi.length != sizeof *test)
return NULL;
end = (void *) (test + 1);
chksum = 0;
for (ptr = addr; ptr < end; ++ptr)
chksum += *ptr;
if (chksum)
return NULL;
/* Looks like it's an mBFT! */
return test;
}
static int do_nothing(void) {
return 0;
}
static void memdisk_info(const s_mdi * mdi) {
const char * cmdline;
if (!show_info)
return;
cmdline = MK_PTR(
mdi->cmdline.seg_off.segment,
mdi->cmdline.seg_off.offset
);
printf(
"Found MEMDISK version %u.%02u:\n"
" diskbuf == 0x%08X, disksize == %u sectors\n"
" bootloaderid == 0x%02X (%s),\n"
" cmdline: %s\n",
mdi->version_major,
mdi->version_minor,
mdi->diskbuf,
mdi->disksize,
mdi->bootloaderid,
bootloadername(mdi->bootloaderid),
cmdline
);
return;
}
/* This function copyright H. Peter Anvin */
static void boot_args(char **args)
{
int len = 0, a = 0;
char **pp;
const char *p;
char c, *q, *str;
for (pp = args; *pp; pp++)
len += strlen(*pp) + 1;
q = str = alloca(len);
for (pp = args; *pp; pp++) {
p = *pp;
while ((c = *p++))
*q++ = c;
*q++ = ' ';
a = 1;
}
q -= a;
*q = '\0';
if (!str[0])
syslinux_run_default();
else
syslinux_run_command(str);
}
/* This function copyright H. Peter Anvin */
static const char *bootloadername(uint8_t id)
{
static const struct {
uint8_t id, mask;
const char *name;
} *lp, list[] = {
{0x00, 0xf0, "LILO"},
{0x10, 0xf0, "LOADLIN"},
{0x31, 0xff, "SYSLINUX"},
{0x32, 0xff, "PXELINUX"},
{0x33, 0xff, "ISOLINUX"},
{0x34, 0xff, "EXTLINUX"},
{0x30, 0xf0, "Syslinux family"},
{0x40, 0xf0, "Etherboot"},
{0x50, 0xf0, "ELILO"},
{0x70, 0xf0, "GrUB"},
{0x80, 0xf0, "U-Boot"},
{0xA0, 0xf0, "Gujin"},
{0xB0, 0xf0, "Qemu"},
{0x00, 0x00, "unknown"}
};
for (lp = list;; lp++) {
if (((id ^ lp->id) & lp->mask) == 0)
return lp->name;
}
}