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