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