• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1; -*- fundamental -*- (asm-mode sucks)
2; ****************************************************************************
3;
4;  memdisk.inc
5;
6;  A program to emulate an INT 13h disk BIOS from a "disk" in extended
7;  memory.
8;
9;   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
10;   Copyright 2009 Intel Corporation; author: H. Peter Anvin
11;   Portions copyright 2009 Shao Miller [El Torito code, mBFT, safe hook]
12;
13;  This program is free software; you can redistribute it and/or modify
14;  it under the terms of the GNU General Public License as published by
15;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16;  Boston MA 02111-1307, USA; either version 2 of the License, or
17;  (at your option) any later version; incorporated herein by reference.
18;
19; ****************************************************************************
20
21%include "../version.gen"
22
23; %define DEBUG_TRACERS			; Uncomment to get debugging tracers
24
25%ifdef DEBUG_TRACERS
26
27%macro TRACER	1
28	call debug_tracer
29	db %1
30%endmacro
31%macro WRITEHEX2 0-1 al
32%ifnidni %1,al
33	push ax
34	mov al,%1
35	call writehex2
36	pop ax
37%else
38	call writehex2
39%endif
40%endmacro
41%macro WRITEHEX4 0-1 ax
42%ifnidni %1,ax
43	push ax
44	mov ax,%1
45	call writehex4
46	pop ax
47%else
48	call writehex4
49%endif
50%endmacro
51%macro WRITEHEX8 0-1 eax
52%ifnidni %1,eax
53	push eax
54	mov eax,%1
55	call writehex8
56	pop eax
57%else
58	call writehex8
59%endif
60%endmacro
61
62%else	; DEBUG_TRACERS
63
64%macro	TRACER	1
65%endmacro
66%macro WRITEHEX2 0-1
67%endmacro
68%macro WRITEHEX4 0-1
69%endmacro
70%macro WRITEHEX8 0-1
71%endmacro
72
73%endif	; DEBUG_TRACERS
74
75; Flags we test our configuration against
76%define CONFIG_READONLY	0x01
77%define CONFIG_RAW	0x02
78%define CONFIG_SAFEINT	0x04
79%define CONFIG_BIGRAW	0x08		; MUST be 8!
80
81		org 0h
82
83%define	SECTORSIZE	(1 << SECTORSIZE_LG2)
84
85		; Parameter registers definition; this is the definition
86		; of the stack frame.
87%define		P_DS		word [bp+34]
88%define		P_ES		word [bp+32]
89%define		P_EAX		dword [bp+28]
90%define		P_HAX		word [bp+30]
91%define		P_AX		word [bp+28]
92%define		P_AL		byte [bp+28]
93%define		P_AH		byte [bp+29]
94%define		P_ECX		dword [bp+24]
95%define		P_HCX		word [bp+26]
96%define		P_CX		word [bp+24]
97%define		P_CL		byte [bp+24]
98%define		P_CH		byte [bp+25]
99%define		P_EDX		dword [bp+20]
100%define		P_HDX		word [bp+22]
101%define		P_DX		word [bp+20]
102%define		P_DL		byte [bp+20]
103%define		P_DH		byte [bp+21]
104%define		P_EBX		dword [bp+16]
105%define		P_HBX		word [bp+18]
106%define		P_HBXL		byte [bp+18]
107%define		P_BX		word [bp+16]
108%define		P_BL		byte [bp+16]
109%define		P_BH		byte [bp+17]
110%define		P_EBP		dword [bp+8]
111%define		P_BP		word [bp+8]
112%define		P_ESI		dword [bp+4]
113%define		P_SI		word [bp+4]
114%define		P_EDI		dword [bp]
115%define		P_DI		word [bp]
116
117		section .text
118		; These pointers are used by the installer and
119		; must be first in the binary
120Pointers:	dw Int13Start
121		dw Int15Start
122		dw MemDisk_Info		; Portions are patched by installer
123		dw TotalSize
124		dw IretPtr
125
126IretPtr		equ Int13Start.iret
127Int13Start:
128		jmp strict near .SafeHookEnd ; 3-byte jump
129		db '$INT13SF'		; Signature for "safe hook"
130		db 'MEMDISK '		; Vendor ID
131		dd 0			; SEG:OFF of previous INT 13h hook
132					; Must be filled in by installer
133		dd 0			; "Safe hook" flags
134; ---- "Safe hook" structure ends here ---
135
136; This next field should be guaranteed at this position after the
137; "safe hook" structure.  This allows for a MEMDISK OS driver to
138; immediately find out the particular parameters using the mBFT
139; and MDI structures.  This binary will have the offset to the mBFT
140; in this field to begin with, so the installer knows where the mBFT
141; is.  This is akin to the "Pointers" section above.  The installer
142; will refill this field with the physical address of the mBFT for
143; future consumers, such as OS drivers.
144		dd mBFT			; Offset from hook to the mBFT
145
146.SafeHookEnd:
147		cmp word [cs:Recursive],0
148		jne recursive
149
150		; Swap stack
151		mov [cs:Stack],esp
152		mov [cs:Stack+4],ss
153		mov [cs:SavedAX],ax
154		mov ax,cs
155		mov ss,ax
156		mov sp,[cs:MyStack]
157
158%if ELTORITO
159		cmp word [cs:SavedAX],4a00h	; El Torito function?
160		jae our_drive			; We grab it
161%endif
162		; See if DL points to our class of device (FD, HD)
163		push dx
164		push dx
165		xor dl,[cs:DriveNo]
166		pop dx
167		js .nomatch		; If SF=0, we have a class match here
168					; 0x00 the sign bit for FD
169					; 0x80 the sign bit for HD
170		jz our_drive		; If ZF=1, we have an exact match
171		cmp dl,[cs:DriveNo]
172		jb .nomatch		; Drive < Our drive
173		cmp dl,[cs:DriveShiftLimit]
174		jae .nomatch		; Drive > The maximum drive
175					; number that we will shift for.
176					; This leaves any higher-up BIOS
177					; drives alone, such as an optical
178					; disc drive 0xA0 or 0xE0
179		dec dl			; Drive > Our drive, adjust drive #
180.nomatch:
181		TRACER '!'
182		WRITEHEX2 dl
183		TRACER ','
184		mov ax,[cs:SavedAX]
185		WRITEHEX4
186		inc word [cs:Recursive]
187		pushf
188		call far [cs:OldInt13]
189		pushf
190		dec word [cs:Recursive]
191		push bp
192		mov bp,sp
193		cmp byte [cs:SavedAX+1],08h	; Get drive params function?
194		je .norestoredl			; DL = number of drives
195		cmp byte [cs:SavedAX+1],15h	; Get disk type function?
196		jne .restoredl
197		test byte [bp+4],80h		; Hard disk?
198		jnz .norestoredl		; CX:DX = size of device
199.restoredl:
200		mov dl,[bp+4]
201.norestoredl:
202		push ax
203		push ebx
204		push ds
205		mov ax,[bp+2]		; Flags
206		lds ebx,[cs:Stack]
207		mov [bx+4],al		; Arithmetic flags
208		pop ds
209		pop ebx
210		pop ax
211		pop bp
212		lss esp,[cs:Stack]
213.iret:		iret
214
215recursive:
216		TRACER '@'
217jmp_oldint13:
218		jmp far [cs:OldInt13]
219
220our_drive:
221		; Set up standard entry frame
222		push ds
223		push es
224		mov ds,ax
225		mov es,ax
226		mov ax,[SavedAX]
227		pushad
228		mov bp,sp		; Point BP to the entry stack frame
229		TRACER 'F'
230		WRITEHEX4
231		; Note: AX == P_AX here
232		cmp ah,Int13FuncsCnt-1
233		ja Invalid_jump
234%if ELTORITO
235		mov al,[CD_PKT.type]	; Check if we are in
236		cmp al,0		; El Torito no emulation mode
237		ja .emulation		; No.  We support the function
238		cmp ah,3fh		; Yes.  We must not support functions
239		jbe Invalid_jump	; 0 through 3Fh.  Check and decide
240.emulation:
241%endif
242		xor al,al		; AL = 0 is standard entry condition
243		mov di,ax
244		shr di,7		; Convert AH to an offset in DI
245		call [Int13Funcs+di]
246
247Done:		; Standard routine for return
248		mov P_AX,ax
249DoneWeird:
250		TRACER 'D'
251		xor bx,bx
252		mov es,bx
253		mov bx,[StatusPtr]
254		mov [es:bx],ah		; Save status
255		and ah,ah
256
257		lds ebx,[Stack]
258		; This sets the low byte (the arithmetic flags) of the
259		; FLAGS on stack to either 00h (no flags) or 01h (CF)
260		; depending on if AH was zero or not.
261		setnz [bx+4]		; Set CF iff error
262		popad
263		pop es
264		pop ds
265		lss esp,[cs:Stack]
266		iret
267
268Reset:
269		; Reset affects multiple drives, so we need to pass it on
270		TRACER 'R'
271		xor ax,ax		; Bottom of memory
272		mov es,ax
273		test dl,dl		; Always pass it on if we are
274					; resetting HD
275		js .hard_disk		; Bit 7 set
276		; Some BIOSes get very unhappy if we pass a reset floppy
277		; command to them and don't actually have any floppies.
278		; This is a bug, but we have to deal with it nontheless.
279		; Therefore, if we are the *ONLY* floppy drive, and the
280		; user didn't request HD reset, then just drop the command.
281		; BIOS equipment byte, top two bits + 1 == total # of floppies
282		test byte [es:0x410],0C0h
283		jz success
284		jmp .pass_on		; ... otherwise pass it to the BIOS
285.hard_disk:
286		; ... same thing for hard disks, sigh ...
287		cmp byte [es:0x475],1	; BIOS variable for number of hard
288					; disks
289		jbe success
290
291.pass_on:
292		pop ax			; Drop return address
293		popad			; Restore all registers
294		pop es
295		pop ds
296		lss esp,[cs:Stack]	; Restore the stack
297		and dl,80h		; Clear all but the type bit
298		jmp jmp_oldint13
299
300
301Invalid:
302		pop dx			; Drop return address
303Invalid_jump:
304		TRACER 'I'
305		mov ah,01h		; Unsupported function
306		jmp short Done
307
308GetDriveType:
309		test byte [DriveNo],80h
310		mov bl,02h		; Type 02h = floppy with changeline
311		jz .floppy
312		; Hard disks only!  DO NOT set CX:DX for floppies...
313		; it apparently causes Win98SE DOS to go into an loop
314		; resetting the drive over and over.  Sigh.
315		inc bx			; Type = 03h
316		mov dx,[DiskSize]	; Return the disk size in sectors
317		mov P_DX,dx
318		mov cx,[DiskSize+2]
319		mov P_CX,cx
320.floppy:
321		mov P_AH,bl		; 02h floppy, 03h hard disk
322		pop ax			; Drop return address
323		xor ax,ax		; Success...
324		jmp DoneWeird		; But don't stick it into P_AX
325
326GetStatus:
327		xor ax,ax
328		mov es,ax
329		mov bx,[StatusPtr]
330		mov ah,[bx]		; Copy last status
331		ret
332
333ReadMult:
334		TRACER 'm'
335Read:
336		TRACER 'R'
337		call setup_regs
338do_copy:
339		TRACER '<'
340		call bcopy
341		TRACER '>'
342		movzx ax,P_AL		; AH = 0, AL = transfer count
343		ret
344
345WriteMult:
346		TRACER 'M'
347Write:
348		TRACER 'W'
349		test byte [ConfigFlags],CONFIG_READONLY
350		jnz .readonly
351		call setup_regs
352		xchg esi,edi		; Opposite direction of a Read!
353		jmp short do_copy
354.readonly:	mov ah,03h		; Write protected medium
355		ret
356
357		; Verify integrity; just bounds-check
358Seek:
359Verify:
360		call setup_regs		; Returns error if appropriate
361		; And fall through to success
362
363CheckIfReady:				; These are always-successful noop functions
364Recalibrate:
365InitWithParms:
366DetectChange:
367EDDDetectChange:
368EDDLock:
369SetMode:
370success:
371		xor ax,ax		; Always successful
372		ret
373
374GetParms:
375		TRACER 'G'
376		mov dl,[DriveCnt]	; Cached data
377		mov P_DL,dl
378		test byte [DriveNo],80h
379		jnz .hd
380		mov P_DI,DPT
381		mov P_ES,cs
382		mov bl,[DriveType]
383		mov P_BL,bl
384.hd:
385		mov ax,[Cylinders]
386		dec ax			; We report the highest #, not the count
387		xchg al,ah
388		shl al,6
389		or al,[Sectors]
390		mov P_CX,ax
391		mov ax,[Heads]
392		dec ax
393		mov P_DH,al
394
395		;
396		; Is this MEMDISK installation check?
397		;
398		cmp P_HAX,'ME'
399		jne .notic
400		cmp P_HCX,'MD'
401		jne .notic
402		cmp P_HDX,'IS'
403		jne .notic
404		cmp P_HBX,'K?'
405		jne .notic
406
407		; MEMDISK installation check...
408		mov P_HAX,'!M'
409		mov P_HCX,'EM'
410		mov P_HDX,'DI'
411		mov P_HBX,'SK'
412		mov P_ES,cs
413		mov P_DI,MemDisk_Info
414
415.notic:
416		xor ax,ax
417		ret
418;
419; EDD functions -- only if enabled
420;
421%if EDD
422EDDPresence:
423		TRACER 'E'
424		TRACER 'c'
425
426		cmp P_BX,55AAh
427		jne Invalid
428		mov P_BX,0AA55h		; EDD signature
429		mov P_AX,03000h		; EDD 3.0
430		mov P_CX,0007h		; Bit 0 - Fixed disk access subset
431					; Bit 1 - Locking and ejecting subset
432					; Bit 2 - EDD subset
433		pop ax			; Drop return address
434		xor ax,ax		; Success
435		jmp DoneWeird		; Success, but AH != 0, sigh...
436
437EDDRead:
438		TRACER 'E'
439		TRACER 'r'
440
441		call edd_setup_regs
442		call bcopy
443		xor ax,ax
444		ret
445
446EDDWrite:
447		TRACER 'E'
448		TRACER 'w'
449
450		call edd_setup_regs
451		xchg esi,edi		; Opposite direction of a Read!
452		call bcopy
453		xor ax,ax
454		ret
455
456EDDVerify:
457EDDSeek:
458		call edd_setup_regs	; Just bounds checking
459		xor ax,ax
460		ret
461
462EDDGetParms:
463		TRACER 'E'
464		TRACER 'p'
465
466		mov es,P_DS
467		mov di,P_SI
468		mov si,EDD_DPT
469
470		lodsw			; Length of our DPT
471		mov cx,[es:di]
472		cmp cx,26		; Minimum size
473		jb .overrun
474
475		cmp cx,ax
476		jb .oksize
477		mov cx,ax
478
479.oksize:
480		mov ax,cx
481		stosw
482		dec cx
483		dec cx
484		rep movsb
485
486		xor ax,ax
487		ret
488
489.overrun:
490		mov ax,0100h
491		ret
492%endif ; EDD
493
494		; Set up registers as for a "Read", and compares against disk
495		; size.
496		; WARNING: This fails immediately, even if we can transfer some
497		; sectors.  This isn't really the correct behaviour.
498setup_regs:
499
500		; Convert a CHS address in P_CX/P_DH into an LBA in eax
501		; CH = cyl[7:0]
502		; CL[0:5] = sector (1-based)  CL[7:6] = cyl[9:8]
503		; DH = head
504		movzx ecx,P_CX
505		movzx ebx,cl		; Sector number
506		and bl,3Fh
507		dec ebx			; Sector number is 1-based
508		cmp bx,[Sectors]
509		jae .overrun
510		movzx edi,P_DH		; Head number
511		movzx eax,word [Heads]
512		cmp di,ax
513		jae .overrun
514		shr cl,6
515		xchg cl,ch		; Now (E)CX <- cylinder number
516		mul ecx			; eax <- Heads*cyl# (edx <- 0)
517		add eax,edi
518		mul dword [Sectors]
519		add eax,ebx
520		; Now eax = LBA, edx = 0
521
522		;
523		; setup_regs continues...
524		;
525		; Note: edi[31:16] and ecx[31:16] = 0 already
526		mov di,P_BX		; Get linear address of target buffer
527		mov cx,P_ES
528		shl ecx,4
529		add edi,ecx		; EDI = address to fetch to
530		movzx ecx,P_AL		; Sector count
531		mov esi,eax
532		add eax,ecx		; LBA of final sector + 1
533		shl esi,SECTORSIZE_LG2	; LBA -> byte offset
534		add esi,[DiskBuf]	; Get address in high memory
535		cmp eax,[DiskSize]	; Check the high mark against limit
536		ja .overrun
537		shl ecx,SECTORSIZE_LG2-2 ; Convert count to dwords
538		ret
539
540.overrun:	pop ax			; Drop setup_regs return address
541		mov ax,0200h		; Missing address mark
542		ret			; Return to Done
543
544		; Set up registers as for an EDD Read, and compares against disk size.
545%if EDD
546edd_setup_regs:
547		push es
548		mov si,P_SI		; DS:SI -> DAPA
549		mov es,P_DS
550
551		mov dx,[es:si]
552		cmp dx,16
553		jb .baddapa
554
555		cmp dword [es:si+4],-1
556		je .linear_address
557
558		movzx ebx,word [es:si+4]	; Offset
559		movzx edi,word [es:si+6]	; Segment
560		shl edi,4
561		add ebx,edi
562		jmp .got_address
563
564.linear_address:
565		cmp dx,24		; Must be large enough to hold
566					; linear address
567		jb .baddapa
568
569		cmp dword [es:si+20],0	; > 4 GB addresses not supported
570		mov ax,0900h		; "Data boundary error" - bogus, but
571					; no really better code available
572		jne .error
573
574		mov ebx,[es:si+16]
575
576.got_address:
577		cmp dword [es:si+12],0		; LBA too large?
578		jne .overrun
579
580		movzx ecx, word [es:si+2]	; Sectors to transfer
581		mov esi,[es:si+8]		; Starting sector
582		mov eax,esi
583		add eax,ecx
584		jc .overrun
585		cmp eax,[DiskSize]
586		ja .overrun
587
588		shl ecx,SECTORSIZE_LG2-2	; Convert to dwords
589		shl esi,SECTORSIZE_LG2		; Convert to an offset
590		add esi,[DiskBuf]
591		mov edi,ebx
592		pop es
593		ret
594
595.baddapa:
596		mov ax,0100h		; Invalid command
597		pop es
598		pop ax			; Drop setup_regs return address
599		ret
600
601.overrun:
602		mov ax,0200h		; "Address mark not found" =
603					; LBA beyond end of disk
604.error:
605		and word [es:si+2],0	; No sectors transferred
606		pop es
607		pop ax
608		ret
609
610EDDEject:
611		mov ax,0B200h		; Volume Not Removable
612		ret
613%if ELTORITO
614ElToritoTerminate:
615		TRACER 'T'
616		mov ax,[cs:SavedAX]
617		cmp al,1		; We only support query, not terminate
618		jne ElToritoErr		; Fail
619		cmp dl,7fh		; Terminate all?
620		je .doit
621		cmp dl,[cs:DriveNo]	; Terminate our drive?
622		je .doit
623		jmp ElToritoErr		; Fail
624.doit:		mov es,P_DS		; Caller's DS:SI pointed to packet
625		mov di,P_SI		; We'll use ES:DI
626		mov si,CD_PKT.size	; First byte is packet size
627		xor cx,0		; Empty our count
628		;mov cl,[ds:si]		; We'll copy that many bytes
629		mov cl,13h
630		rep movsb		; Copy until CX is zero
631		mov ax,0		; Success
632		ret
633ElToritoEmulate:
634ElToritoBoot:
635ElToritoCatalog:
636ElToritoErr:
637		TRACER '!'
638		mov ax,100h		; Invalid parameter
639		ret
640%endif ; ELTORITO
641%endif ; EDD
642
643;
644; INT 15h intercept routines
645;
646int15_e820:
647		cmp edx,534D4150h	; "SMAP"
648		jne oldint15
649		cmp ecx,20		; Need 20 bytes
650		jb err86
651		push ds
652		push cs
653		pop ds
654		push edx		; "SMAP"
655		and ebx,ebx
656		jne .renew
657		mov ebx,E820Table
658.renew:
659		add bx,12		; Advance to next
660		mov eax,[bx-4]		; Type
661		and eax,eax		; Null type?
662		jz .renew		; If so advance to next
663		mov [es:di+16],eax
664		mov eax,[bx-12]		; Start addr (low)
665		mov edx,[bx-8]		; Start addr (high)
666		mov [es:di],eax
667		mov [es:di+4],edx
668		mov eax,[bx]		; End addr (low)
669		mov edx,[bx+4]		; End addr (high)
670		sub eax,[bx-12]		; Derive the length
671		sbb edx,[bx-8]
672		mov [es:di+8],eax	; Length (low)
673		mov [es:di+12],edx	; Length (high)
674		cmp dword [bx+8],-1	; Type of next = end?
675		jne .notdone
676		xor ebx,ebx		; Done with table
677.notdone:
678		pop eax			; "SMAP"
679		mov edx,eax		; Some systems expect eax = edx = SMAP
680		mov ecx,20		; Bytes loaded
681		pop ds
682int15_success:
683		mov byte [bp+6], 02h	; Clear CF
684		pop bp
685		iret
686
687err86:
688		mov byte [bp+6], 03h	; Set CF
689		mov ah,86h
690		pop bp
691		iret
692
693Int15Start:
694		push bp
695		mov bp,sp
696		cmp ax,0E820h
697		je near int15_e820
698		cmp ax,0E801h
699		je int15_e801
700		cmp ax,0E881h
701		je int15_e881
702		cmp ah,88h
703		je int15_88
704oldint15:	pop bp
705		jmp far [cs:OldInt15]
706
707int15_e801:				; Get mem size for > 64 MB config
708		mov ax,[cs:Mem1MB]
709		mov cx,ax
710		mov bx,[cs:Mem16MB]
711		mov dx,bx
712		jmp short int15_success
713
714int15_e881:				; Get mem size for > 64 MB config
715					; 32-bit code
716		mov eax,[cs:Mem1MB]
717		mov ecx,eax
718		mov ebx,[cs:Mem16MB]
719		mov edx,ebx
720		jmp short int15_success
721
722int15_88:				; Get extended mem size
723		mov ax,[cs:MemInt1588]
724		jmp short int15_success
725
726;
727; Routine to copy in/out of high memory
728; esi = linear source address
729; edi = linear target address
730; ecx = 32-bit word count
731;
732; Assumes cs = ds = es
733;
734bcopy:
735		push eax
736		push ebx
737		push edx
738		push ebp
739
740		mov bx, real_int15_stub
741
742		test byte [ConfigFlags], CONFIG_RAW|CONFIG_SAFEINT
743		jz .anymode		; Always do the real INT 15h
744
745		smsw ax			; Unprivileged!
746		test al,01h
747		jnz .protmode		; Protmode -> do real INT 15h
748
749.realmode:
750		; Raw or Safeint mode, and we're in real mode...
751
752		test byte [ConfigFlags], CONFIG_SAFEINT
753		jnz .fakeint15
754
755.raw:
756		TRACER 'r'
757		; We're in real mode, do it outselves
758
759		pushfd			; <A>
760		push ds			; <B>
761		push es			; <C>
762
763		cli
764		cld
765
766		xor ebx,ebx
767		mov bx,cs
768		shl ebx,4
769		lea edx,[Shaker+ebx]
770		mov [Shaker+2],edx
771
772		; Test to see if A20 is enabled or not
773		xor ax,ax
774		mov ds,ax
775		dec ax
776		mov es,ax
777
778		mov ax,[0]
779		mov bx,ax
780		xor bx,[es:10h]
781		not ax
782		mov [0],ax
783		mov dx,ax
784		xor dx,[es:10h]
785		not ax
786		mov [0],ax
787
788		or dx,bx
789		push dx			; <D> Save A20 status
790		jnz .skip_a20e
791
792		mov ax,2401h		; Enable A20
793		int 15h
794.skip_a20e:
795		mov dl,[ConfigFlags]
796		and dx,CONFIG_BIGRAW
797		add dx,8
798		; DX = 16 for BIGRAW, 8 for RAW
799		;  8 is selector for a 64K flat segment,
800		; 16 is selector for a 4GB flat segment.
801
802		lgdt [cs:Shaker]
803		mov eax,cr0
804		or al,01h
805		mov cr0,eax
806
807		mov bx,16		; Large flat segment
808		mov ds,bx
809		mov es,bx
810
811		a32 rep movsd
812
813		; DX has the appropriate value to put in
814		; the registers on return
815		mov ds,dx
816		mov es,dx
817
818		and al,~01h
819		mov cr0,eax
820
821		pop dx			; <D> A20 status
822		pop es			; <C>
823		pop ds			; <B>
824
825		and dx,dx
826		jnz .skip_a20d
827		mov ax,2400h		; Disable A20
828		int 15h
829.skip_a20d:
830		popfd			; <A>
831		jmp .done
832
833.fakeint15:
834		; We're in real mode with CONFIG_SAFEINT, invoke the
835		; original INT 15h vector.  We used to test for the
836		; INT 15h vector being unchanged here, but that is
837		; *us*; however, the test was wrong for years (always
838		; negative) so instead of fixing the test do what we
839		; tested and don't bother probing.
840		mov bx, fake_int15_stub
841
842.protmode:
843		TRACER 'p'
844.anymode:
845
846.copy_loop:
847		push esi
848		push edi
849		push ecx
850		cmp ecx,4000h
851		jna .safe_size
852		mov ecx,4000h
853.safe_size:
854		push ecx	; Transfer size this cycle
855		mov eax, esi
856		mov [Mover_src1], si
857		shr eax, 16
858		mov [Mover_src1+2], al
859		mov [Mover_src2], ah
860		mov eax, edi
861		mov [Mover_dst1], di
862		shr eax, 16
863		mov [Mover_dst1+2], al
864		mov [Mover_dst2], ah
865		mov si,Mover
866		mov ah, 87h
867		shl cx,1	; Convert to 16-bit words
868		call bx		; INT 15h stub
869		pop eax		; Transfer size this cycle
870		pop ecx
871		pop edi
872		pop esi
873		jc .error
874		lea esi,[esi+4*eax]
875		lea edi,[edi+4*eax]
876		sub ecx, eax
877		jnz .copy_loop
878		; CF = 0
879.error:
880.done:
881		pop ebp
882		pop edx
883		pop ebx
884		pop eax
885		ret
886
887real_int15_stub:
888		int 15h
889		cli		; Some BIOSes enable interrupts on INT 15h
890		ret
891
892fake_int15_stub:
893		pushf
894		call far [OldInt15]
895		cli
896		ret
897
898%ifdef DEBUG_TRACERS
899debug_tracer:	pushad
900		pushfd
901		mov bp,sp
902		mov bx,[bp+9*4]
903		mov al,[cs:bx]
904		inc word [bp+9*4]
905		mov ah,0Eh
906		mov bx,7
907		int 10h
908		popfd
909		popad
910		ret
911
912writehex2:	pushad
913		pushfd
914		mov cx,2
915		ror eax,4
916		jmp writehex_common
917writehex4:	pushad
918		pushfd
919		mov cx,4
920		ror eax,12
921		jmp writehex_common
922writehex8:	pushad
923		pushfd
924		mov cx,8
925		ror eax,28
926writehex_common:
927.loop:		push cx
928		push eax
929		and al,0Fh
930		cmp al,10
931		jb .isdec
932		add al,'a'-'0'-10
933.isdec:		add al,'0'
934		mov ah,0Eh
935		mov bx,7
936		int 10h
937		pop eax
938		rol eax,4
939		pop cx
940		loop .loop
941		popfd
942		popad
943		ret
944%endif
945
946		section .data align=16
947		alignb 2
948Int13Funcs	dw Reset		; 00h - RESET
949		dw GetStatus		; 01h - GET STATUS
950		dw Read			; 02h - READ
951		dw Write		; 03h - WRITE
952		dw Verify		; 04h - VERIFY
953		dw Invalid		; 05h - FORMAT TRACK
954		dw Invalid		; 06h - FORMAT TRACK AND SET BAD FLAGS
955		dw Invalid		; 07h - FORMAT DRIVE AT TRACK
956		dw GetParms		; 08h - GET PARAMETERS
957		dw InitWithParms	; 09h - INITIALIZE CONTROLLER WITH
958					;	DRIVE PARAMETERS
959		dw Invalid		; 0Ah
960		dw Invalid		; 0Bh
961		dw Seek			; 0Ch - SEEK TO CYLINDER
962		dw Reset		; 0Dh - RESET HARD DISKS
963		dw Invalid		; 0Eh
964		dw Invalid		; 0Fh
965		dw CheckIfReady		; 10h - CHECK IF READY
966		dw Recalibrate		; 11h - RECALIBRATE
967		dw Invalid		; 12h
968		dw Invalid		; 13h
969		dw Invalid		; 14h
970		dw GetDriveType		; 15h - GET DRIVE TYPE
971		dw DetectChange		; 16h - DETECT DRIVE CHANGE
972%if EDD
973		dw Invalid		; 17h
974		dw Invalid		; 18h
975		dw Invalid		; 19h
976		dw Invalid		; 1Ah
977		dw Invalid		; 1Bh
978		dw Invalid		; 1Ch
979		dw Invalid		; 1Dh
980		dw Invalid		; 1Eh
981		dw Invalid		; 1Fh
982		dw Invalid		; 20h
983		dw ReadMult		; 21h - READ MULTIPLE
984		dw WriteMult		; 22h - WRITE MULTIPLE
985		dw SetMode		; 23h - SET CONTROLLER FEATURES
986		dw SetMode		; 24h - SET MULTIPLE MODE
987		dw Invalid		; 25h - IDENTIFY DRIVE
988		dw Invalid		; 26h
989		dw Invalid		; 27h
990		dw Invalid		; 28h
991		dw Invalid		; 29h
992		dw Invalid		; 2Ah
993		dw Invalid		; 2Bh
994		dw Invalid		; 2Ch
995		dw Invalid		; 2Dh
996		dw Invalid		; 2Eh
997		dw Invalid		; 2Fh
998		dw Invalid		; 30h
999		dw Invalid		; 31h
1000		dw Invalid		; 32h
1001		dw Invalid		; 33h
1002		dw Invalid		; 34h
1003		dw Invalid		; 35h
1004		dw Invalid		; 36h
1005		dw Invalid		; 37h
1006		dw Invalid		; 38h
1007		dw Invalid		; 39h
1008		dw Invalid		; 3Ah
1009		dw Invalid		; 3Bh
1010		dw Invalid		; 3Ch
1011		dw Invalid		; 3Dh
1012		dw Invalid		; 3Eh
1013		dw Invalid		; 3Fh
1014		dw Invalid		; 40h
1015		dw EDDPresence		; 41h - EDD PRESENCE DETECT
1016		dw EDDRead		; 42h - EDD READ
1017		dw EDDWrite		; 43h - EDD WRITE
1018		dw EDDVerify		; 44h - EDD VERIFY
1019		dw EDDLock		; 45h - EDD LOCK/UNLOCK MEDIA
1020		dw EDDEject		; 46h - EDD EJECT
1021		dw EDDSeek		; 47h - EDD SEEK
1022		dw EDDGetParms		; 48h - EDD GET PARAMETERS
1023		dw EDDDetectChange	; 49h - EDD MEDIA CHANGE STATUS
1024%if ELTORITO				; EDD El Torito Functions
1025					; ELTORITO _must_ also have EDD
1026		dw ElToritoEmulate	; 4Ah - Initiate Disk Emulation
1027		dw ElToritoTerminate	; 4Bh - Terminate Disk Emulation
1028		dw ElToritoBoot		; 4Ch - Initiate Disk Emu. and Reboot
1029		dw ElToritoCatalog	; 4Dh - Return Boot Catalog
1030%endif ; ELTORITO
1031%endif ; EDD
1032
1033Int13FuncsEnd	equ $
1034Int13FuncsCnt	equ (Int13FuncsEnd-Int13Funcs) >> 1
1035
1036
1037		alignb 8, db 0
1038Shaker		dw ShakerEnd-$-1	; Descriptor table limit
1039		dd 0			; Pointer to self
1040		dw 0
1041
1042Shaker_RMDS:	dd 0x0000ffff		; 64K data segment
1043		dd 0x00009300
1044
1045Shaker_DS:	dd 0x0000ffff		; 4GB data segment
1046		dd 0x008f9300
1047
1048ShakerEnd	equ $
1049
1050		alignb 8, db 0
1051
1052Mover		dd 0, 0, 0, 0		; Must be zero
1053		dw 0ffffh		; 64 K segment size
1054Mover_src1:	db 0, 0, 0		; Low 24 bits of source addy
1055		db 93h			; Access rights
1056		db 00h			; Extended access rights
1057Mover_src2:	db 0			; High 8 bits of source addy
1058		dw 0ffffh		; 64 K segment size
1059Mover_dst1:	db 0, 0, 0		; Low 24 bits of target addy
1060		db 93h			; Access rights
1061		db 00h			; Extended access rights
1062Mover_dst2:	db 0			; High 8 bits of source addy
1063Mover_dummy2:	dd 0, 0, 0, 0		; More space for the BIOS
1064
1065		alignb 16, db 0
1066mBFT:
1067; Fields common to all ACPI tables
1068		dd '    '		; ACPI signature ("mBFT")
1069					; This is filled-in by the installer
1070					; to avoid an accidentally valid mBFT
1071		dd mBFT_Len		; ACPI table length
1072		db 1			; ACPI revision
1073		db 0			; ACPI table checksum
1074		db 'MEMDSK'		; ACPI OEM ID
1075		db 'Syslinux'		; ACPI OEM table ID
1076		dd 0			; ACPI OEM revision
1077		dd 0			; ACPI ASL compiler vendor ID
1078		dd 0			; ACPI ASL compiler revision
1079; The next field is mBFT-specific and filled-in by the installer
1080		dd 0			; "Safe hook" physical address
1081
1082; Note that the above ends on a DWORD boundary.
1083; The MDI has always started at such a boundary.
1084; Portions of the MDI are patched by the installer
1085MemDisk_Info	equ $			; Pointed to by installation check
1086MDI_Bytes	dw MDI_Len		; Total bytes in MDI structure
1087MDI_Version	db VERSION_MINOR, VERSION_MAJOR	; MEMDISK version
1088
1089DiskBuf		dd 0			; Linear address of high memory disk
1090DiskSize	dd 0			; Size of disk in blocks
1091CommandLine	dw 0, 0			; Far pointer to saved command line
1092
1093OldInt13	dd 0			; INT 13h in chain
1094OldInt15	dd 0			; INT 15h in chain
1095
1096OldDosMem	dw 0			; Old position of DOS mem end
1097BootLoaderID	db 0			; Boot loader ID from header
1098		db 0			; pad
1099
1100DPT_ptr		dw 0			; If nonzero, pointer to DPT
1101					; Original DPT pointer follows
1102
1103MDI_Len		equ $-MemDisk_Info
1104mBFT_Len	equ $-mBFT		; mBFT includes the MDI
1105
1106; ---- MDI structure ends here ---
1107DriveShiftLimit	db 0ffh			; Installer will probe for
1108					; a range of contiguous drives.
1109					; Any BIOS drives above this region
1110					; shall not be impacted by our
1111					; shifting behaviour
1112		db 0			; pad to a DWORD
1113		dw 0			; pad to a QWORD
1114MemInt1588	dw 0			; 1MB-65MB memory amount (1K)
1115
1116Cylinders	dw 0			; Cylinder count
1117Heads		dw 0			; Head count
1118Sectors		dd 0			; Sector count (zero-extended)
1119
1120Mem1MB		dd 0			; 1MB-16MB memory amount (1K)
1121Mem16MB		dd 0			; 16MB-4G memory amount (64K)
1122
1123DriveNo		db 0			; Our drive number
1124DriveType	db 0			; Our drive type (floppies)
1125DriveCnt	db 0			; Drive count (from the BIOS)
1126
1127ConfigFlags	db 0			; Bit 0 - readonly
1128
1129MyStack		dw 0			; Offset of stack
1130StatusPtr	dw 0			; Where to save status (zeroseg ptr)
1131
1132DPT		times 16 db 0		; BIOS parameter table pointer (floppies)
1133OldInt1E	dd 0			; Previous INT 1E pointer (DPT)
1134
1135%if EDD
1136EDD_DPT:
1137.length		dw 30
1138.info		dw 0029h
1139		; Bit 0 - DMA boundaries handled transparently
1140		; Bit 3 - Device supports write verify
1141		; Bit 5 - Media is lockable
1142.cylinders	dd 0			; Filled in by installer
1143.heads		dd 0			; Filled in by installer
1144.sectors	dd 0			; Filled in by installer
1145.totalsize	dd 0, 0			; Filled in by installer
1146.bytespersec	dw SECTORSIZE
1147.eddtable	dw -1, -1		; Invalid DPTE pointer
1148.dpikey		dw 0BEDDh		; Device Path Info magic
1149.dpilen		db 2ch			; DPI len
1150.res1		db 0			; Reserved
1151.res2		dw 0			; Reserved
1152.bustype	dd 'MEM '		; Host bus type (4 bytes, space padded)
1153.inttype	dd 'MEMORY  '		; Interface type (8 bytes, spc. padded)
1154.intpath	dd 0, 0			; Interface path
1155.devpath	dd 0, 0, 0, 0		; Device path
1156.res3		db 0			; Reserved
1157.chksum		db 0			; DPI checksum
1158
1159%if ELTORITO
1160; El Torito CD Specification Packet - mostly filled in by installer
1161CD_PKT:
1162.size		db 13h	; Packet size (19 bytes)
1163.type		db 0	; Boot media type (flags)
1164.driveno	db 0E0h	; INT 13h drive number
1165.controller	db 0	; Controller index
1166.start		dd 0	; Starting LBA of image
1167.devno		dw 0	; Device number
1168.user_buf	dw 0	; User buffer segment
1169.load_seg	dw 0	; Load segment
1170.sect_count	dw 0	; Emulated sectors to load
1171.geom1		db 0	; Cylinders bits 0 thru 7
1172.geom2		db 0	; Sects/track 0 thru 5, cyls 8, 9
1173.geom3		db 0	; Heads
1174%endif ; ELTORITO
1175
1176%endif ; EDD
1177
1178; End patch area
1179
1180		alignb 4, db 0
1181Stack		dd 0			; Saved SS:ESP on invocation
1182		dw 0
1183SavedAX		dw 0			; AX saved on invocation
1184Recursive	dw 0			; Recursion counter
1185
1186		alignb 4, db 0		; We *MUST* end on a dword boundary
1187
1188E820Table	equ $			; The installer loads the E820 table here
1189TotalSize	equ $			; End pointer
1190