| /* ----------------------------------------------------------------------- * |
| * |
| * 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; |
| } |
| } |
| |