| ; A loader for www.memtest.org images, by Eric Auer 2003. |
| ; This assumes that the image starts with the boot sector, |
| ; which has the size of setup.S in sectors in a byte at offset |
| ; 1f1h (497). Further, I assume setup.S directly after the boot |
| ; sector and the actual memtest head.S after setup.S ... |
| |
| ; This version is derived from memtestL loader, which loads |
| ; memtest.bin from a separate file. This version is meant to |
| ; be used like (DOS / Unix variants): |
| ; copy /b memteste.bin + memtest.bin memtest.exe |
| ; cat memteste.bin memtest.bin > memtest.exe |
| ; The good thing is that you get a single file which can be |
| ; compressed, for example with http://upx.sf.net/ (UPX). |
| |
| %define fullsize (150024 + buffer - exeh) |
| ; 150024 is the size of memtest86+ V5.01, adjust as needed! |
| |
| %define stacksize 2048 |
| %define stackpara ((stacksize + 15) / 16) |
| |
| ; the trick is that NASM believes the header would be part |
| ; of the loaded image, so we "org 20h bytes too early" to fix: |
| org 0e0h ; NASM thinks after header we have 100h |
| ; which is what we want it to think. |
| |
| exeh: db "MZ" |
| dw fullsize % 512 ; how much to load from |
| dw (fullsize + 511) / 512 ; .exe to RAM |
| dw 0 ; no relocations used |
| dw 2 ; header size is 2 * 16 bytes |
| dw stackpara ; minimum heap is 128 * 16 bytes, for stack |
| dw stackpara ; we do not need more heap either |
| dw (fullsize + 15) / 16 ; SS is after file |
| ; segment offsets are relative to PSPseg+10h |
| ; initial DS and ES point to PSPseg, and file |
| ; except headers is loaded to PSPseg+10h. |
| |
| dw stacksize-4 ; initial SP value |
| dw 0 ; no checksum |
| dw 100h ; initial IP |
| dw -10h ; initial CS relative to PSPseg+10h |
| dw 0 ; no relocation table, "offset 0 in file" |
| dw 0 ; this is not an overlay |
| db "MEMT" ; padding to a multiple of 16 bytes |
| |
| ; loaded part begins here (set CS so that IP is 100h here) |
| |
| start: ; entry point ; if you use obj + linker, use "..start:" |
| mov ah, 01h |
| mov bh, 00h |
| mov cx, 2000h |
| int 10h |
| |
| mov ax,cs ; *** |
| mov ds,ax ; *** |
| mov es,ax ; *** |
| |
| ; test if we have 386 or better: |
| pushf ; save flags |
| xor ax,ax |
| push ax |
| popf ; try to clear all bits |
| pushf |
| pop ax |
| and ax,0f000h |
| cmp ax,0f000h |
| jz noinst1 ; 4 msb stuck to 1: 808x or 80186 |
| mov ax,0f000h |
| push ax |
| popf ; try to set 4 msb |
| pushf |
| pop ax |
| test ax,0f000h |
| jz noinst1 ; 4 msb stuck to 0: 80286 |
| popf ; restore flags |
| jmp short found386 |
| |
| noinst1: |
| popf ; restore flags |
| mov dx,need386 |
| jmp generror |
| |
| |
| found386: ; now test if the system is in real mode: |
| smsw ax ; MSW is the low half of CR0 |
| ; (smsw is not priv'd, unlike mov eax,cr0) |
| test al,1 ; if the PE (protected mode) flag on? |
| %ifndef DEBUG ; ignore findings in debug mode |
| jnz foundprotected |
| %endif |
| jmp foundreal |
| |
| foundprotected: |
| mov dx,noreal |
| jmp generror |
| |
| ; ------------ |
| |
| need386 db "Sorry, you need at least a 386 CPU to use Memtest86+." |
| db 13,10,"$" |
| noreal db "You cannot run Memtest86+ if the system already is in" |
| db " protected mode.",13,10,"$" |
| |
| ; ------------ |
| |
| generror: ; generic error exit |
| push cs |
| pop ds |
| push cs |
| pop es |
| mov ah,9 |
| int 21h |
| mov ax,4c01h |
| int 21h |
| |
| ; ------------ |
| |
| foundreal: |
| mov cx,buffer+15 |
| shr cx,4 ; buffer offset in paragraphs |
| mov ax,cs |
| add ax,cx ; buffer offset in paragraphs |
| ; now AX is the buffer segment |
| mov [cs:bufsetup+2],ax ; far pointer to boot sector now |
| mov cx,20h ; size of boot sector in paragraphs |
| add [cs:bufsetup+2],cx ; far pointer to setup now |
| movzx eax,ax |
| shl eax,4 ; linear buffer offset |
| mov [cs:buflinear],eax |
| |
| findpoint: ; now patch the loader! |
| mov al,[buffer+1f1h] ; size of setup.S in sectors |
| ; should be 4 ... |
| inc al ; the boot sector itself |
| movzx eax,al |
| shl eax,9 ; log 2 of sector size |
| add [cs:buflinear],eax ; linear address of head.S now |
| mov ax,[buffer+251h] ; should be jmp far dword (ofs, seg) |
| cmp ax,0ea66h |
| jz foundpatch |
| patchbug: ; could not patch the jump |
| mov dx,nopatch |
| jmp generror |
| |
| gdtbug: |
| mov dx,nogdt |
| jmp generror |
| |
| foundpatch: |
| mov eax,[cs:buflinear] |
| mov [buffer+253h],eax ; patch the protected mode entry jump |
| ; (offset only - segment selector unchanged: flat linear CS) |
| |
| findgdt: |
| mov eax,[cs:buffer+20ch] ; should be lgdt offset |
| and eax,00ffffffh |
| cmp eax,0016010fh ; lgdt ... |
| jnz gdtbug |
| |
| mov ax,[cs:buffer+20fh] ; GDTR contents pointer |
| mov bx,ax |
| mov eax,[cs:buffer+200h+bx+2] ; GDT linear offset |
| and eax,1ffh ; assume GDT in first sector of setup.S |
| ; *** WARNING: this is needed because setup.S contains |
| ; *** HARDCODED offset of setup.S on linear 90200h, which |
| ; *** is 90000h + bootsect.S ... flaw in Memtest86! |
| |
| mov cx,[cs:bufsetup+2] ; setup.S segment |
| movzx ecx,cx |
| shl ecx,4 ; linear setup.S address |
| add eax,ecx ; fixed GDT linear offset |
| mov [cs:buffer+200h+bx+2],eax ; patch it |
| |
| ;mov dx,trying |
| ;mov ah,9 |
| ;int 21h |
| |
| ;xor ax,ax |
| ;int 16h ; wait for a keypress from the user |
| |
| mov ax,[cs:bufsetup+2] ; setup segment |
| mov ds,ax ; set nice data segments for setup.S ... |
| mov es,ax |
| xor ax,ax |
| mov fs,ax |
| mov gs,ax |
| |
| cli |
| lss sp,[cs:newstack] ; stack in first 64k now! |
| movzx esp,sp ; ensure 16bit stack pointer |
| ; Memtest86 head.S assumes that it can just turn SS to |
| ; linear. This would put the stack at 0:200h or so for us |
| ; if we fail to move the stack around ... |
| |
| %ifdef DEBUG |
| mov ebp,[cs:buflinear] ; will show up in debugging logs |
| mov esi,[cs:bufsetup] ; will show up in debugging logs |
| %endif |
| |
| jmp far [cs:bufsetup] |
| ; setup.S will enable the A20 (ignoring HIMEM, just using |
| ; the classic 8042 programming trick) and turn on protected |
| ; mode. Then it will jump to head.S, which luckily can run |
| ; from any offset inside the linear 4 GB CS ... |
| |
| ; ------------ |
| |
| buflinear dd 0 ; linear address of head.S entry point |
| bufsetup dw 0,0 ; far pointer to setup.S entry point |
| |
| newstack dw 03fch,0 ; beware, stack will overwrite IDT. |
| |
| ; ------------ |
| |
| nopatch db "jmp far dword not found at setup.S offset 37h,",13,10 |
| db "(file offset 237h is not 66h, 0eah)",13,10 |
| db "please adjust and recompile memtestl...",13,10,"$" |
| |
| nogdt db "lgdt [...] not found at setup.S offset 0ch,",13,10 |
| db "(file offset 20ch is not 0fh, 01h, 16h)",13,10 |
| db "please adjust and recompile memtestl...",13,10,"$" |
| |
| trying db "Now trying to start Memtest86...",13,10 |
| db "You have to reboot to leave Memtest86 again.",13,10 |
| db "Press a key to go on.",13,10,"$" |
| |
| ; ------------ |
| |
| align 16 |
| buffer: ; a label pointing to where in the file memtest.bin will be. |
| |