393 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* ----------------------------------------------------------------------- *
 | |
|  *
 | |
|  *   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;
 | |
|     }
 | |
| }
 | |
| 
 |