_UNDOCUMENTED CORNER_ by Taku Okazaki edited by Andrew Schulman Listing One /* emmimp.h, EMM import structure -- copyright (c) taQ 1994 */ typedef unsigned long uint32; /* double word */ typedef unsigned short uint16; /* word */ typedef unsigned char uint8; /* byte */ typedef struct { uint8 type; uint8 handle; uint16 logpage; uint8 physpg_num; uint8 flag; } emmimp_frame_desc_t; /* frame descriptors */ typedef struct { uint8 flag0; uint8 resv0; uint16 size; uint16 version; uint32 resv1; emmimp_frame_desc_t frame[64]; uint8 resv2; } emmimp_hdr_t; /* header */ typedef struct { uint32 physpgnum[4]; } emmimp_umb_desc_t; /* UMB descriptor */ typedef struct { uint8 handle; uint8 flag; uint8 hndlname[8]; uint16 pages; uint32 pgmap_phys_addr; } emmimp_ems_desc_t; /* EMS descriptor */ typedef struct { uint32 physpgnum; uint32 numpg; } free_page_list_t; /* free page list */ typedef struct { uint16 handle; uint16 flag; uint32 kb; uint32 baseaddr; } emmimp_xms_desc_t; /* XMS descriptor */ typedef struct { uint16 startseg; uint16 parasize; } free_umb_desc_t; /* free UMB descriptor */ Listing Two /* emmimp.c, dumping the EMM import structure -- copyright (c) taQ 1994 */ #include #include #include #include #include #include #include "emmimp.h" #include "moveext.h" #ifndef __TURBOC__ #define asm _asm #endif static int for_386max = 0; static int raw_dump = 0; static int machine; static void emulate_win_init(uint16 ver, void (far **entryp)(void)) { void (far *entry)(void); asm push ds asm push es asm mov dx, 0 asm mov ax, 0 asm mov es, ax asm mov ds, ax asm mov di, ver asm mov si, 0 asm mov cx, 0 asm mov bx, 0 asm mov ax, 1605h asm int 2fh asm mov word ptr entry, si asm mov word ptr entry + 2, ds asm pop es asm pop ds *entryp = entry; } static void emulate_win_term(void) { asm mov ax, 1606h asm int 2fh } static uint32 get_emm_imp_addr(void) { uint8 buf[6]; int fd; fd = open("EMMXXXX0", O_RDONLY | O_BINARY); if (fd < 0) { printf("can't open EMMXXXX0\n"); return 0; } memset(buf, 0, sizeof buf); buf[0] = 1; asm lea dx, word ptr buf asm mov cx, 6 asm mov bx, fd asm mov ax, 4402h asm int 21h asm jc fail printf("emm import structure address:%08lx\n", *(uint32 *) buf); printf("emm import structure version:%d.%02d\n", buf[4], buf[5]); close(fd); return *(uint32 *) buf; fail: printf("emm import struct address get failed\n"); close(fd); return 0; } static void switch_to_real(void (far *entry)(void)) { asm push ds asm push es asm push di asm push si asm push bp asm mov ax, 0 asm call dword ptr entry; asm pop bp asm pop si asm pop di asm pop es asm pop ds } static void switch_to_prot(void (far *entry)(void)) { asm push ds asm push es asm push di asm push si asm push bp asm mov ax, 1 asm call dword ptr entry; asm pop bp asm pop si asm pop di asm pop es asm pop ds } static uint16 winver = 0x30a; /* Windows version */ main(int ac, uint8 **av) { void (far *entry)(void); int i; uint32 emm_imp_addr; static uint8 buf[8192]; get_options(ac, av); // not shown if (!for_386max) machine = machine_type(); emulate_win_init(winver, &entry); if (!entry) { printf("illegal mode switch entry\n"); emulate_win_term(); return 1; } printf("mode switch entry:%08lx\n", entry); emm_imp_addr = get_emm_imp_addr(); if (!emm_imp_addr) { emulate_win_term(); return 1; } if (!for_386max) { disable(); /* disable interrupts while in real mode */ switch_to_real(entry); switch_to_prot(entry); enable(); /* now Ok to enable interrupts */ emulate_win_term(); if (machine == MC_98) move_ext_mem_98(emm_imp_addr, buf, sizeof buf); else move_ext_mem_pc(emm_imp_addr, buf, sizeof buf); dump_emm_imp(emm_imp_addr, buf); } else { disable(); switch_to_real(entry); move_ext_mem_real(emm_imp_addr, buf, sizeof buf); switch_to_prot(entry); enable(); emulate_win_term(); dump_emm_imp(emm_imp_addr, buf); } return 0; } Listing Three /* moveext.c, extended memory move -- copyright (c) taQ 1994 */ #pragma inline #include #include #include #include "moveext.h" #ifndef __TURBOC__ #define asm _asm #endif typedef unsigned short uint16; /* word */ typedef unsigned char uint8; /* byte */ /* segment descriptor */ typedef struct { uint16 lim0_15; /* Bits 0-15 of the segment limit */ uint16 base0_15; /* Bits 0-15 of the segment base */ uint8 base16_23; /* Bits 16-23 of the segment base */ uint8 acc_byte; /* Access byte */ uint8 lim16_19; /* Bits 16-19 of the segment limit plus some flags */ uint8 base24_31; /* Bits 24-31 of the segment base */ } desc_t; /* convert far pointer to address */ static uint32 fp_to_addr(void far *p) { uint32 addr = FP_SEG(p); addr <<= 4; addr += FP_OFF(p); return addr; } static void set_data_gdt(desc_t *gdtp, uint32 addr, uint16 limit) { gdtp->lim0_15 = limit; gdtp->base0_15 = addr; gdtp->base16_23 = (addr >> 16) & 0xff; gdtp->base24_31 = (addr >> 24) & 0xff; gdtp->acc_byte = 0x93; /* data, expand up, r/w, dpl = 0 */ gdtp->lim16_19 = 0; } /* move extended memory, PC-AT */ int move_ext_mem_pc(uint32 srcaddr, void far *dest, uint16 bytes) { void far *p; static desc_t gdt[6]; /* 10h: src descriptor, 18h: dest */ memset(gdt, 0, sizeof gdt); /* set src & dest descriptors */ set_data_gdt(gdt + 2, srcaddr, bytes); set_data_gdt(gdt + 3, fp_to_addr(dest), bytes); p = &gdt; asm les si, p asm mov ah, 87h asm mov cx, bytes asm inc cx asm shr cx, 1 asm int 15h /* error check omitted */ return 1; } /* move extended memory, PC-9800 */ int move_ext_mem_98(uint32 srcaddr, void far *dest, uint16 bytes) { desc_t far *p; static desc_t gdt[6]; /* 10h: src descriptor, 18h: dest */ memset(gdt, 0, sizeof gdt); /* set src & dest descriptors */ set_data_gdt(gdt + 2, srcaddr, 0xffff); set_data_gdt(gdt + 3, fp_to_addr(dest), 0xffff); p = gdt; asm mov cx, bytes asm les bx, p asm mov si, 0 asm mov di, 0 asm mov ah, 90h asm clc asm int 1fh asm jnc _OK return 0; _OK: return 1; } typedef struct { uint16 len; uint32 physaddr; uint16 filler; } gdt_val_t; /* move extended memory, generic, from real mode, assumes interrupts disabled*/ int move_ext_mem_real(uint32 srcaddr, void far *dest, uint16 bytes) { gdt_val_t gdt_val; static desc_t gdt[3]; /* 8h: src descriptor, 10h: dest */ memset(gdt, 0, sizeof gdt); /* set src & dest descriptors, limit must be 0xffff */ set_data_gdt(gdt + 1, srcaddr, 0xffff); set_data_gdt(gdt + 2, fp_to_addr(dest), 0xffff); gdt_val.len = sizeof gdt; gdt_val.physaddr = fp_to_addr(&gdt); gdt_val.filler = 0; asm push ds asm push es asm push si asm push di asm .386p /* load gdt */ asm lgdt fword ptr gdt_val /* enter protected mode */ asm mov eax, cr0 asm or eax, 1 asm mov cr0, eax /* ds <- src descriptor */ asm mov ax, 8h asm mov ds, ax asm xor si, si /* es <- dest descriptor */ asm mov ax, 10h asm mov es, ax asm xor di, di asm mov cx, bytes asm cld asm rep movsb /* back to real mode */ asm mov eax, cr0 asm and eax, not 1 asm mov cr0, eax asm .8086 asm pop di asm pop si asm pop es asm pop ds return 1; } int machine_type(void) { uint16 rc; union REGS regs; rc = MC_UNKNOWN; /* unknown */ /* do PC-AT get realtime clock call, NOP interrupt in PC-9800 */ regs.x.cx = 0; regs.h.ah = 4; int86(0x1a, ®s, ®s); /* if ch contains current century, then PC-AT */ if (regs.h.ch == 0x19 || regs.h.ch == 0x20) rc = MC_PC; /* PC-AT */ else rc = MC_98; /* PC-98 */ return rc; } Figure 1: #pragma pack(1) typedef struct { unsigned long addr; unsigned char maj, min; } EMM_IMPORT_ADDR; // must be 6 bytes unsigned long get_emm_import_addr(void) { unsigned long addr = 0; EMM_IMPORT_ADDR impaddr; int emm = open("EMMXXXX0", O_RDONLY); if (emm == -1) return 0L; // fail: no EMM impaddr.addr = 1; #define IOCTLREAD 2 if (ioctl(emm, IOCTLREAD, (void far *) &impaddr, sizeof(impaddr)) != 0) addr = impaddr.addr; close(emm); return addr; // physical address } Figure 2: Header: (all versions) byte Flag0 byte ??? word Size of struct (bytes) word Version # dword ??? Snapshot of memory status below 1MB: (all versions) array of 64 frame descriptors a frame descriptor is: byte Type byte Handle # word Logical Page # byte Physical Page # byte Flag followed by: byte ??? UMB information: (all versions) byte # of UMB frame descriptors array of UMB frame descriptors a UMB frame descriptor is an array of 4 dwords each dword is 386 physical page number EMS handle information: (all versions) byte # of EMS handle descriptors array of EMS handle descriptors where an EMS handle descriptor is: byte Handle # byte Flag byte Handlename[8] word EMS pages owned by handle dword Pagemap Physical Address free memory information: (version 1.10, 1.11) dword Real mode INT 67h vector dword HMA page table physical address byte # of free page list array of free page list where a free page list is: dword Physical page # dword # of free pages XMS information: (version 1.10, 1.11) byte # of XMS handle descriptors array of XMS handle descriptors where an XMS handle descriptor is: word Handle # word Flag word Size (KB) dword Base Address free UMB information: (version 1.10, 1.11) byte # of free UMB descriptors array of free UMB descriptors where a free UMB descriptor is: word Segment word Paragraphs Product information: (version 1.11) vendor name of the memory manager product name of the memory manager Figure 3: (a) INT 2Fh AX=1605h -> Set device name to "EMMXXXX0" Return Mode Switch Entrypoint address IOCTL-read "EMMXXXX0" -> Return physical address of EMM import structure Call Mode Switch -> Setup EMM import structure Entrypoint (AX=0) Set CPU to real mode Windows Runs... (b) Set CPU to real mode Call Mode Switch -> Setup the EMM import structure Entrypoint (AX=1) Regain protected mode INT 2Fh AX=1606h -> Restore device name Figure 4: C:\> emmimp mode switch entry:03af0992 emm import structure address:00119000 flag0:0x4 size:0x23c bytes version:0x10b frame[0x10] (4000:0):large EMS (phys page 04) ... frame[0x27] (9c00:0):large EMS (phys page 27) frame[0x32] (c800:0):UMB/c800/c900/ca00/cb00/umb desc index:0 frame[0x33] (cc00:0):UMB/cc00/cd00/ce00/cf00/umb desc index:1 frame[0x34] (d000:0):UMB/d000/d100/d200/d300/umb desc index:2 frame[0x35] (d400:0):UMB/d400/d500/d600/d700/umb desc index:3 frame[0x36] (d800:0):UMB/d800/d900/da00/db00/umb desc index:4 frame[0x37] (dc00:0):UMB/dc00/dd00/de00/df00/umb desc index:5 frame[0x38] (e000:0):EMS (phys page 00)/mapped to handle 1 page 1 frame[0x39] (e400:0):EMS (phys page 01) frame[0x3a] (e800:0):EMS (phys page 02) frame[0x3b] (ec00:0):EMS (phys page 03) resv2:0x74 # of umb desc:6 umbdesc[ 0]:00000120 00000121 00000122 00000123 umbdesc[ 1]:00000124 00000125 00000126 00000127 umbdesc[ 2]:00000128 00000129 0000012a 0000012b umbdesc[ 3]:0000012c 0000012d 0000012e 0000012f umbdesc[ 4]:00000130 00000131 00000132 00000133 umbdesc[ 5]:00000134 00000135 00000136 00000137 EMS handle 0: name="", 24 EMS pages, pagemap at 0011b000 log page[0]:00040267 00041067 00042067 00043067 ... log page[17]:0009c267 0009d067 0009e067 0009f067 EMS handle 1: name="test", 3 EMS pages, pagemap at 0011b180 log page[0]:00150267 00151067 00152067 00153067 log page[1]:00154267 00155067 00156067 00157067 log page[2]:00158267 00159067 0015a067 0015b067 realmode int 67 vector:03af:02b0 hma_page_table_addr:00000000 # of free page lists:1 free page list[00]:page 0000015c, 52 pages # of XMS info:0 # of umb free seg:1 umb free seg:c93a, 0x16c6 paragraphs maker:"MICROSOFT " product:"EMM386 4.45 "