-
Notifications
You must be signed in to change notification settings - Fork 1
/
AHCIWRAP.ASM
2007 lines (1725 loc) · 46.6 KB
/
AHCIWRAP.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.8086
.model large
?TESTWITHVBOX equ 0
?ONLYBUGFIXES equ 0
?INSERTBKPTS equ 0
?VDSAWARE equ 1
?BACKUP_STATE equ 1 ; backup port states before issuing IDENTIFY
?FIXUNALIGNED equ 1
; Structures
RqHdr struc ; DOS driver request header
bLen db ?
bUnit db ?
bCmd db ?
wStatus dw ?
_resd dq ?
RqHdr ends
InitRq struc ; Init request from DOS
sHdr RqHdr <?>
bUnits db ?
lpEnd dd ?
lpCmdLn dd ?
bDrvNum db ?
wErrMsg dw ?
InitRq ends
IOCTLRW struc ; IOCTL read/write request
sHdr RqHdr <?>
bMedDes db ?
wBufOff dw ?
wBufSeg dw ?
wCount dw ?
_resd2 dw ? ; starting sector number = 0 for MSCDEX
_resd3 dd ? ; volume ID = 0 for MSCDEX
IOCTLRW ends
ReadL struc
sHdr RqHdr <?>
bAMode db ? ; addressing mode (RedBook / High Sierra)
wBufOff dw ?
wBufSeg dw ?
wSectors dw ?
dwStart dd ? ; first sector
bRMode db ? ; read mode (cooked / raw) - use raw to get 930h
bISize db ? ; interleave size
bISkip db ? ; interleave skip factor
ReadL ends
PlayReq struc
sHdr RqHdr <?>
bAMode db ? ; addressing mode (RedBook / High Sierra)
dwStart dd ? ; first sector
dwSectors dd ?
PlayReq ends
LocHead struc
bCode db ? ; 1 for read
bAMode db ? ; addressing mode (RedBook / High Sierra)
dwLoc dd ? ; head location returned from driver
LocHead ends
sDiskInfo struc
bCode db ? ; 10 for read
bLTrack db ?
bHTrack db ?
dwLOut dd ?
sDiskInfo ends
sTnoInfo struc
bCode db ? ; 11 for read
bTrack db ?
dwStart dd ?
bCtlADR db ?
sTnoInfo ends
sAudStat struc
bCode db ? ; 15 for read
wStatus dw ? ; Bit 0 = paused, all others reserved
dwStart dd ?
dwEnd dd ?
sAudStat ends
QInfo struc
bCode db ? ; 12 for read
bCtlADR db ?
bTrack db ?
bPoint db ?
bMinute db ?
bSecond db ?
bFrame db ?
_resd db ? ; zero
bPMin db ?
bPSec db ?
bPFrame db ?
QInfo ends
if ?VDSAWARE
; Extra flags used if ?VDSAWARE:
; Bit 4 = FIS DMA region locked
EBIT_FIS_DDS_LOCKED equ 4
EFLAG_FIS_DDS_LOCKED equ 1 SHL EBIT_FIS_DDS_LOCKED
; Bit 5 = CmdList DMA region locked
EBIT_CL_DDS_LOCKED equ 5
EFLAG_CL_DDS_LOCKED equ 1 SHL EBIT_CL_DDS_LOCKED
; Bit 6 = CmdTable DMA region locked
EBIT_CTBL_DDS_LOCKED equ 6
EFLAG_CTBL_DDS_LOCKED equ 1 SHL EBIT_CTBL_DDS_LOCKED
; Bit 7 = PRDT DMA region locked
EBIT_PRDT_EDDS_LOCKED equ 7
EFLAG_PRDT_EDDS_LOCKED equ 1 SHL EBIT_PRDT_EDDS_LOCKED
endif
; This structure is at SS:[BP] during most of AHCI.SYS's operation
ahcidrv_stack struc
lpDataBase dd ?
qwLBA dq ?
dwExpectedCount dd ?
wDwordCount dw ?
wPortOffset dw ?
bCount db ?
bPort db ?
bDevice db ? ; For port multipliers
bFlags db ? ; We can use extra flags, as given above
bSlotMask db ?
bCurSlot db ?
if ?BACKUP_STATE
qwSavedFB dq ?
qwSavedCLB dq ?
dwSavedCmd dd ?
unused db 18 dup(?)
else
unused db 38 dup(?)
endif
pushad_edi dd ?
pushad_esi dd ?
pushad_ebp dd ?
pushad_esp dd ?
pushad_ebx dd ?
pushad_edx dd ?
pushad_ecx dd ?
pushad_eax dd ?
client_ds dw ?
client_es dw ?
client_fs dw ?
client_gs dw ?
ahcidrv_stack ends
if ?VDSAWARE
; The most data we can transfer is a single segment is 10000h bytes.
; This is 10h pages, but the beginning and end aren't necessarily page-aligned
; so the data may span a total of 11h pages.
?MAX_PRDT_ENTRIES equ 11h
HBA_PRDT_ENTRY struc
dba dq ?
rsv0 dd ?
dbc dd ? ; MSB = i = interrupt on completion
HBA_PRDT_ENTRY ends
HBA_CMD_TBL struc
cfis db 14h dup (?)
_resd0 db 2Ch dup (?)
acmd db 10h dup (?)
_resd1 db 30h dup (?)
prdt HBA_PRDT_ENTRY ?MAX_PRDT_ENTRIES dup (<?>)
HBA_CMD_TBL ends
DD_Struc struc
DD_size dd ?
DD_offset dd ?
DD_segment dw ?
DD_bufid dw ?
DD_physaddx dd ?
DD_Struc ends
EDD_Region struc
EDD_physaddx dd ?
EDD_physsize dd ?
EDD_Region ends
EDD_Struc struc
EDD_size dd ?
EDD_offset dd ?
EDD_segment dd ?
EDD_numavail dw ?
EDD_numused dw ?
EDD_regions EDD_Region ?MAX_PRDT_ENTRIES dup (<?>)
EDD_Struc ends
; A nice big structure to make better use of space lying idle in Intel's driver!
CmdTbl_plus_VDS struc
; The command table, as set up by Intel's driver itself
CmdTable HBA_CMD_TBL <?>
; DDSes to lock the DMA regions for command table, command list and FIS
CmdTbl_DDS DD_Struc <?>
CmdList_DDS DD_Struc <?>
FIS_DDS DD_Struc <?>
; An EDDS to lock the scatter/gather DMA region for the actual data transfer
; (used in turn to fill in the PRDT)
PRDT_EDDS EDD_Struc <?>
CmdTbl_plus_VDS ends
; Intel driver only gives us 1 kiB to play with...
.errnz ((sizeof CmdTbl_plus_VDS) gt 400h)
endif
; Code/data positions in Intel's AHCI driver
num_drives equ byte ptr 15h
; Screwed-up mov instruction
SwapLBA equ 20CAh ; function containing the instruction
badmov equ word ptr 20CBh
; Inappropriate shl instruction
badshl equ dword ptr 209Fh
; Dwords that are coded as little-endian which should be big-endian
;badendian0 equ dword ptr 1F3Eh ; they got it right for CloseTray
badendian1 equ dword ptr 1F74h
;badendian2 equ dword ptr 1F98h ; they got it right for Eject
badendian3 equ dword ptr 1FC2h
; References to CL that should be to CH
bad_ch0 equ byte ptr 1FE0h
bad_ch1 equ byte ptr 1FE3h
; Bad sequence of instructions in LockDoor
badlockdoor equ dword ptr 1FEAh
if ?FIXUNALIGNED
; where the driver sets a transfer "offset" in a structure on the stack
setxferoff equ word ptr 2320h
; where stopcmd gets called when a command is finished
call_stopcmd equ word ptr 2553h
else
; Bad jump on encountering unaligned transfer addx
badjmp_unaln equ byte ptr 2479h
endif
; Hardcoded PCI addresses
hard_pci_addx0 equ word ptr 2BEAh
hard_pci_addx1 equ word ptr 2C04h
hard_pci_addx2 equ word ptr 2C2Dh
hard_pci_addx3 equ word ptr 2C52h
if ?TESTWITHVBOX
; hardcoded offset of index port from base port
idxport_offset equ byte ptr 2C41h
endif
; Function tables
GeneralCmds equ word ptr 1536h
CdromCmds equ word ptr 1558h
IoctlReadCmds equ word ptr 156Ah
IoctlWriteCmds equ word ptr 158Ah
ife ?ONLYBUGFIXES
; Location to patch in ReadLong function
ReadLongBadCom equ word ptr 1EEBh
SeekBadCom equ word ptr 1F2Eh
; Last four bytes of function to build ATAPI packet
AtapiPktTail equ word ptr 2254h
; Function to send ATAPI command with transfer addx in ES:DI
send_pkt_addx equ 20AAh
; Same thing but without specifying addx (uses internal transfer buf instead)
send_pkt equ 208Ah
; speaking of which...
XferBuf equ 0D36h
; where the interrupt routine checks the status
intchecksts equ 1E09h
; places to inject near calls into the interrupt routine
intloadcmd equ word ptr 1DCEh
intsavests equ word ptr 1E0Dh
endif
if ?VDSAWARE
; where the driver fills a linear address into "CLB" field of the port
set_cmdlistbase equ word ptr 263Dh ; add eax,ebx
; where the driver fills a linear address into "FB" field of the port
set_fisbase equ word ptr 2661h ; add eax,ebx
; where the driver fills a linear address into "CTBA" field of command header
fill_ctba equ word ptr 2456h ; add eax,ebx
; where the driver starts setting up PRDT entries
setup_prdt equ word ptr 246Fh ; shl edx,4
setup_prdt_done equ 24AAh ; return to here to skip Intel's logic
; where the driver calls a function to wait for the CR bit to be unset
stop_cmd_wtcrus equ word ptr 25FFh ; call wait_cr_unset
; the function it calls
wait_cr_unset equ 2735h
pCmdTable equ word ptr 30h
endif
if ?BACKUP_STATE
; where the driver calls "stop_cmd" before starting the IDENTIFY sequence
idn_stop_cmd equ word ptr 22B3h
; where the driver calls "do_pio_cmd" to execute the IDENTIFY sequence
idn_do_pio_cmd equ word ptr 22D2h
; the functions it's actually calling
stop_cmd equ 25CCh
do_pio_cmd equ 24EBh
; helper functions that we can also use
read_hbaport equ 28ECh
write_hbaport equ 2926h
endif
; Endpoints
end_ahci_res equ 2AC0h
end_ahci_init equ 2F75h
; Relocation factors
S2FAC equ end_ahci_init - offset stage2
S2LEN equ end_stage3 - stage2 ; we have to relocate both at once!
S3FAC equ end_ahci_res - offset stage3
S3LEN equ end_stage3 - stage3
; Other miscellaneous equates
BDA_SEGMENT equ 40h ; BIOS Data Area segment
EBDA_OFFSET equ 0Eh ; Offset within BDA of pointer to Extended BDA segment
VDS_FLAG_OFFSET equ 7Bh ; Offset within BDA of Virtual DMA Spec flags
.code
driver segment use16
; STAGE 1 = code/data loaded and executed in-place
; ------- * does not get relocated
; ------- * gets overwritten almost immediately
org 0
stage1:
lpNextDriver dd -1
wAttrs dw 0C800h ; Same as Intel AHCI driver itself
pStrategy dw offset strat_stg1
pInterrupt dw offset inter_stg1
drvName db 'AHCIWRAP'
lpSavedReq label dword
SavedReq_off dw ?
SavedReq_seg dw ?
sataraid_idcs label dword
sata_idx dw -1 ; Index of SATA controller to try (if any)
raid_idx dw -1 ; Index of RAID controller to try (if any)
strat_stg1 proc far
assume ds:nothing,es:nothing,ss:nothing
mov [SavedReq_off],bx
mov [SavedReq_seg],es
ret
strat_stg1 endp
inter_stg1 proc far
assume ds:nothing,es:nothing,ss:nothing
push es
push bx
les bx,[lpSavedReq]
cmp es:[bx].RqHdr.bCmd,0 ; init - can't do owt else yet!
je @F
mov es:[bx].RqHdr.wStatus,8003h ; unknown command
pop bx
pop es
ret
@@:
push ax
pushf
pushf
pop ax
and ah,0Fh ; unset upper four flags
push ax
popf
pushf
pop ax
and ah,0F0h
cmp ah,0F0h ; all upper flags set?
je @F
or ah,0F0h ; set upper four flags
push ax
popf
pushf
pop ax
test ah,0F0h ; upper flags set?
; As I understand it, a 386 in Real Mode will have some of them set,
; but a 286 in Real Mode will have none of them...
jnz @@386ok
@@:
popf
push dx
mov dx,offset no386
call init_msg
mov word ptr es:[bx].InitRq.lpEnd,0 ; unload
mov word ptr es:[bx+2].InitRq.lpEnd,cs
mov es:[bx].RqHdr.wStatus,800Ch ; general failure
pop dx
pop ax
pop bx
pop es
ret
@@386ok:
popf
.386
assume fs:nothing,gs:nothing
; OK, now it's safe to save all the 32-bit registers
; Get rid of the 16-bit ones on the stack first...
pop ax
pop bx
pushad
; Reload the pointer to the init request
mov ax,3000h ; DOS version / OEM check
int 21h
les bp,[lpSavedReq]
cmp al,5
jb @F ; assume we have enough memory and hope for the best...
mov esi,es:[bp].InitRq.lpEnd
movzx edx,si ; get the offset into EDX
xor si,si ; keep the segment in the upper half of ESI
shr esi,0Ch ; convert (seg SHL 10h) to actual number of bytes
mov eax,cs
shl eax,4 ; convert segment to number of bytes
sub esi,eax
add edx,esi ; offset + segment*16 = total number of bytes
; This here is the largest amount of memory we'll need
; (after we relocate stage 2 but before we relocate stage 3)
cmp edx,offset end_stage3 + S2FAC
jnb @F
mov dx,offset insufmem
jmp @@genfailure
@@:
xor edi,edi
mov ax,0B101h ; PCI BIOS installation check
int 1Ah
test ah,ah
jnz @F
cmp edx," ICP"
je @@pciok
@@:
mov dx,offset nopcibios
jmp @@genfailure
@@pciok:
; Parse command line...
push ds
lds si,es:[bp].InitRq.lpCmdLn
cld
; Ignore first argument (our own filename)
xor cx,cx
not cx
mov al,' '
@@: ; any leading spaces
lodsb
cmp al,' '
loope @B
@@: ; our filename itself
lodsb
cmp al,' '
loopne @B
@@checkspaces:
xor cx,cx
not cx
@@:
lodsb
cmp al,' '
loope @B
dec si
lodsw
cmp al,'/'
jne @@havefilename
; We have a switch, make sure it's uppercase
and ah,not 20h
; Process the number we get
xor cx,cx
lodsb
@@:
sub al,'0'
jb @@nonnumeric
cmp al,9
ja @@nonnumeric
imul cx,cx,10 ; base 10
add cl,al
adc ch,0
lodsb
cmp al,' '
jne @B
; Switch is still in AH
cmp ah,'S' ; SATA controller
jne @F
mov [sata_idx],cx
jmp @@checkspaces
@@:
cmp ah,'R' ; RAID controller
jne @F
mov [raid_idx],cx
jmp @@checkspaces
@@:
mov dx,offset badswitch
mov [badswitchb],ah
call init_msg
jmp @@checkspaces
@@havefilename:
; Up to the filename...
call check_eol
jne @F
pop ds
mov dx,offset nofilename
jmp @@genfailure
@@:
push si
cmp [sataraid_idcs],-1
jne @F
; Neither SATA nor RAID controller idx specified,
; try zero for both
inc [sataraid_idcs]
@@:
mov si,[sata_idx]
cmp si,-1
je @F
mov ax,0B103h ; find PCI class code
mov ecx,10601h ; SATA controller
int 1Ah
jc @F
test ah,ah
jz @@devfound
@@:
mov si,[raid_idx]
cmp si,-1
je @@nodev
mov ax,0B103h ; find PCI class code
mov ecx,10400h ; RAID controller
int 1Ah
jc @@nodev
test ah,ah
jnz @@nodev
@@devfound:
pop si
; Get ready to ask DOS to open the file
mov dx,si
; this is now the cmdline for the AHCI driver
mov word ptr es:[bp].InitRq.lpCmdLn,si
@@:
lodsb
call check_eol
je @F
cmp al,' '
jne @B
@@:
; Temporarily null-terminate the filename
dec si
xor cx,cx
xchg ch,ds:[si]
; Is it illegal to do Unix-type file IO in init code??
; It seems to work though...
mov ax,3D00h ; OPEN read-only
xor cl,cl
int 21h
xchg ch,ds:[si] ; restore character that was there
pop ds
jnc @F
mov dx,offset cantopen
jmp @@genfailure
@@:
mov di,bx ; save PCI bus/device/function
mov bx,ax ; file handle
mov ax,4202h ; LSEEK from end
xor cx,cx ; end of file
mov dx,cx
int 21h
jc @@seekerr
test dx,dx
jnz @@badsize
cmp ax,end_ahci_init
jne @@badsize
mov ax,4200h ; LSEEK from beginning
mov dx,4 ; skip over the pointer to next driver
int 21h
jc @@seekerr
; Relocate Stage 2
push es
push di
push cs
pop es
assume es:driver
std ; start from the end since we're relocating forward
mov si,offset stage2
mov di,end_ahci_init
mov cx,S2LEN
add si,cx
add di,cx
dec si
dec di
rep movsb es:[di],es:[si]
pop di
pop es
assume es:nothing
; Stage 2 is relocated, let's jump into it with a retf...
push cs
push offset stage2_init + S2FAC
ret
@@nodev:
pop si
pop ds
mov dx,offset nodev
jmp @@genfailure
@@seekerr:
mov dx,offset seekerr
jmp @@genfailure
@@badsize:
mov dx,offset badsize
jmp @@genfailure
@@nonnumeric:
pop ds
mov dx,offset nonnumeric
@@genfailure:
call init_msg
mov es:[bp].InitRq.lpEnd,0 ; unload
mov word ptr es:[bp+2].InitRq.lpEnd,cs
mov es:[bp].RqHdr.wStatus,800Ch ; general failure
popad
pop es
ret
inter_stg1 endp
; Check if character in AL is a terminator (CR/LF/NUL) - sets ZF if so
check_eol proc near
cmp al,0Dh ; end of command line!
je @F
cmp al,0Ah ; end of command line!
je @F
test al,al
@@:
ret
check_eol endp
; Destroys AX, takes message in DX
init_msg proc near uses ds
push cs
pop ds
assume ds:driver
mov ah,9 ; write to stdout
int 21h
ret
assume ds:nothing
init_msg endp
; Messages
no386 db "No 386 CPU, AHCI driver cannot start!",0Dh,0Ah,'$'
nopcibios db "No PCI BIOS, AHCI driver cannot find SATA controller!",0Dh,0Ah,'$'
insufmem db "Insufficient memory for AHCI driver to relocate itself!",0Dh,0Ah,'$'
nonnumeric db "Invalid non-numeric option for switch",0Dh,0Ah,'$'
badswitch db "Ignoring unrecognized switch /"
badswitchb db ?,0Dh,0Ah,'$'
nodev db "Could not find AHCI controller!",0Dh,0Ah,'$'
nofilename db "No path to Intel AHCI driver given, wrapper cannot start!",0Dh,0Ah,'$'
cantopen db "Could not open Intel AHCI driver file!",0Dh,0Ah,'$'
seekerr db "Could not seek Intel AHCI driver file!",0Dh,0Ah,'$'
badsize db "File specified does not appear to be Intel AHCI driver file! (wrong size)",0Dh,0Ah,'$'
; STAGE 2 = code/data that needs to coexist with Intel's init code/data
; ------- * gets relocated beyond the end of the loaded driver
; ------- * gets discarded when we tell DOS how much to keep allocated
stage2:
; These can be in stage 2 since they're not needed after init
if ?BACKUP_STATE
SavePortState proc near
push bx
push di
mov di,read_hbaport ; need to call indirectly...
xor bx,bx ; CLB is at offset zero
call di
mov dword ptr [bp.ahcidrv_stack.qwSavedCLB],eax
add bx,4 ; upper half
call di
mov dword ptr [bp.ahcidrv_stack.qwSavedCLB+4],eax
mov bx,8 ; FB is at offset 8
call di
mov dword ptr [bp.ahcidrv_stack.qwSavedFB],eax
add bx,4 ; upper half
call di
mov dword ptr [bp.ahcidrv_stack.qwSavedFB+4],eax
mov bx,18h ; Cmd is at offset 18h
call di
mov dword ptr [bp.ahcidrv_stack.dwSavedCmd],eax
pop di
pop bx
; Jump back to the original function (have to do it indirectly)
mov ax,stop_cmd
jmp ax
SavePortState endp
RestPortState proc near
; Call through to the original function (have to do it indirectly)
mov ax,do_pio_cmd
call ax
push eax
push bx
push di
mov di,write_hbaport; need to call indirectly...
xor bx,bx ; CLB is at offset zero
mov eax,dword ptr [bp.ahcidrv_stack.qwSavedCLB]
call di
add bx,4 ; upper half
mov eax,dword ptr [bp.ahcidrv_stack.qwSavedCLB+4]
call di
mov bx,8 ; FB is at offset 8
mov eax,dword ptr [bp.ahcidrv_stack.qwSavedFB]
call di
add bx,4 ; upper half
mov eax,dword ptr [bp.ahcidrv_stack.qwSavedFB+4]
call di
mov bx,18h ; Cmd is at offset 8
mov eax,dword ptr [bp.ahcidrv_stack.dwSavedCmd]
call di
pop di
pop bx
pop eax
; set CF as appropriate for return from do_pio_cmd
cmp ah,1
cmc
ret
RestPortState endp
endif
stage2_init proc far
; At this point, we have the following:
; * File handle for Intel driver in BX
; * DOS "Init" device request in ES:BP
; * PCI bus/device/function in DI
; * Stack has "PUSHAD", ES, and the far return addx for the "interrupt"
push ds
push cs
pop ds
assume ds:driver
mov dx,4 ; skip over pointer to next driver
mov ah,3Fh ; READ
mov cx,end_ahci_init-4 ; exclude pointer to next driver
int 21h
jc @@readerr
cmp ax,cx
jb @@readerr
mov ah,3Eh ; CLOSE
int 21h
mov bx,bp ; get back our pointer to the init req
push cs ; since strategy is a far function
call [pStrategy] ; this is now a pointer to Intel's strat
; Fix hardcoded PCI addresses before calling "interrupt"
mov ds:[hard_pci_addx0],di
mov ds:[hard_pci_addx1],di
mov ds:[hard_pci_addx2],di
mov ds:[hard_pci_addx3],di
if ?VDSAWARE
; replace "add eax,ebx" with "call FixupCLB" / "call FixupFB"
mov ds:[set_cmdlistbase],0E8h ; call rel16
mov ds:[set_fisbase],0E8h ; call rel16
mov ds:[fill_ctba],0E8h ; call rel16
mov ds:[setup_prdt],0E8h ; call rel16
if ?INSERTBKPTS
mov ds:setup_prdt[2],0CC00h ; int3 after three bytes
else
mov ds:setup_prdt[2],9000h ; nop after three bytes
endif
; use S2FAC since this will be called from within init
; (i.e. before we relocate stage 3!)
mov si,offset FixupCLB + S2FAC - (set_cmdlistbase+3)
mov ds:set_cmdlistbase[1],si
mov si,offset FixupFB + S2FAC - (set_fisbase+3)
mov ds:set_fisbase[1],si
mov si,offset FixupCTBA + S2FAC - (fill_ctba+3)
mov ds:fill_ctba[1],si
mov si,offset FixupPRDT + S2FAC - (setup_prdt+3)
mov ds:setup_prdt[1],si
; this is already a call, so no need to insert 0E8h
mov si,offset stop_cmd_redir + S2FAC - (stop_cmd_wtcrus+3)
mov ds:stop_cmd_wtcrus[1],si
endif
if ?BACKUP_STATE
; Intel's AHCI CD driver is designed to operate in conjunction with its
; AHCI firmware, which handles int 13h requests. Intel's firmware and CD
; driver both reinitialize the FB, CLB and Cmd registers before every
; transaction, which means there is no conflict between them, even when
; the CD driver temporarily takes control of each port to identify the
; attached devices.
; HOWEVER, other firmware (e.g. SeaBIOS) naively assumes that nobody
; interferes with FB, CLB and Cmd, which results in all int 13h requests
; timing out after this driver attempts to identify the devices.
; To prevent this, we save FB, CLB and Cmd before each IDENTIFY command
; is issued, and restore them afterwards.
; these are already calls, so no need to insert 0E8h
mov si,offset SavePortState + S2FAC - (idn_stop_cmd+3)
mov ds:idn_stop_cmd[1],si
mov si,offset RestPortState + S2FAC - (idn_do_pio_cmd+3)
mov ds:idn_do_pio_cmd[1],si
; Note: we don't need to fix these up later, since the IDENTIFY sequence
; is only ever called from the init code!
endif
; Fix a messed-up mov instruction in Intel's code
; It is "mov ebx,0D3Eh" but should have been "mov ebx,dword ptr [0D3Eh]"
; I know, right??
; To make matters worse, this gets called from both LocHead and VolSize
; while for VolSize it should *actually* be "dword ptr [0D36h]"!
mov di,ds:badmov[1] ; get operand of "mov ebx,imm32"
mov ds:badmov[0],1E8Bh ; "mov ebx,dword ptr [imm16]"
mov ds:badmov[2],di ; put in the immediate
if ?INSERTBKPTS
or ds:badmov[4],0CCh ; spare byte: make it a breakpoint
else
or ds:badmov[4],90h ; spare byte: make it a nop
endif
; Fix a sequence "shl edx,10h ; mov dx,1" which results in ATAPI packet
; requests only transferring a single byte of data!
; The correct instruction is "imul edx,10001h", which replicates the
; same number in the upper and lower halves of EDX.
; Happily we can fix this by changing exactly four bytes in the code :)
mov ds:[badshl],1D269h
; Fix some integers that are passed as little-endian (usual for x86),
; forgetting that they will then be translated to big-endian during the
; construction of an ATAPI packet
mov eax,ds:[badendian1]
xchg al,ah
ror eax,10h
xchg al,ah
mov ds:[badendian1],eax
mov eax,ds:[badendian3]
xchg al,ah
ror eax,10h
xchg al,ah
mov ds:[badendian3],eax
; Fix references to CL that should be to CH, for locking/unlocking door
mov ds:[bad_ch0],6Ch ; mov ch,byte ptr ...
mov ds:[bad_ch1],0FDh ; cmp ch,imm8
; Right after this, there is a sequence of instructions that pass bad
; parameters to a function that is supposed to dispatch an ATAPI packet
; to the AXth unit of the driver.
; But, Intel set AX to 8003h in anticipation of bad input, and then they
; don't fix it back to the unit number.
; Fortunately we can change twelve bytes to fix it. We change:
; mov ebx,1Eh
; mov edx,0
; To:
; nop ; or int3
; mov ax,bx
; mov ebx,1Eh
; xor edx,edx
if ?INSERTBKPTS
mov ds:badlockdoor[0],66D889CCh
else
mov ds:badlockdoor[0],66D88990h
endif
mov ds:badlockdoor[4],1EBBh
mov ds:badlockdoor[8],0D2316600h
ife ?FIXUNALIGNED
; this JMP goes two bytes too far, pull it back
sub ds:[badjmp_unaln],2
endif
if ?TESTWITHVBOX
; VirtualBox has the index port at offset 8 from the base port,
; as opposed to 10h on QEMU and real hardware...
; TODO: Detect VirtualBox instead of compile-time switch!
int 3
mov ds:[idxport_offset],8
endif
; Patch the function tables
; VolSize patch is to fix a bug
mov si,offset OurVolSize + S3FAC
xchg ds:IoctlReadCmds[8*2],si
mov di,offset pVolSize + S2FAC
mov [di],si
ife ?ONLYBUGFIXES
; DevStat patch is to advertise new functionality
mov si,offset OurDevStat + S3FAC
xchg ds:IoctlReadCmds[6*2],si
mov di,offset pDevStat + S2FAC
mov [di],si
; New features - TODO: implement them!
; Objective is to reach feature parity with UDVD2
; mov si,offset rAudInfo + S3FAC ; UDVD doesn't have this
; mov ds:IoctlReadCmds[4*2],si
; mov si,offset rDrvBytes + S3FAC ; or this
; mov ds:IoctlReadCmds[5*2],si
mov si,offset DiskInfo + S3FAC
mov ds:IoctlReadCmds[10*2],si
mov si,offset TnoInfo + S3FAC
mov ds:IoctlReadCmds[11*2],si
; QInfo is handled directly in ChkStatus
; mov si,offset SubChanInfo + S3FAC ; UDVD doesn't have this
; mov ds:IoctlReadCmds[13*2],si
; mov si,offset UPCCode + S3FAC ; or this
; mov ds:IoctlReadCmds[14*2],si
mov si,offset AudStat + S3FAC
mov ds:IoctlReadCmds[15*2],si
; mov si,offset wAudInfo + S3FAC ; UDVD doesn't have this
; mov ds:IoctlWriteCmds[3*2],si
; mov si,offset wDrvBytes + S3FAC ; or this
; mov ds:IoctlWriteCmds[4*2],si
; Patch ReadLong function
; If it gets MSF addressing or Raw read mode, it returns "bad command"
; (i.e. mov ax,8003h). Replace this with a jump to our own function!
mov ds:ReadLongBadCom[0],0E9h ; jmp rel16
mov si,offset OurReadLong + S3FAC - (ReadLongBadCom+3)
mov ds:ReadLongBadCom[1],si
; Same story with Seek
mov ds:SeekBadCom[0],0E9h ; jmp rel16
mov si,offset OurSeek + S3FAC - (SeekBadCom+3)
mov ds:SeekBadCom[1],si
; Patch function to build ATAPI packet
; Replace the last three bytes before the RETN with a push instruction,
; pushing an address of our own function tail so it jumps there instead!
mov ds:AtapiPktTail[0],68h ; push imm16
mov si,offset OurAtapiPktTail + S3FAC
mov ds:AtapiPktTail[1],si
mov si,offset Play + S3FAC
mov ds:CdromCmds[4*2],si
mov si,offset StopPlay + S3FAC
mov ds:CdromCmds[5*2],si
mov si,offset Resume + S3FAC
mov ds:CdromCmds[8*2],si
endif
; call the actual init function
push cs ; since interrupt is a far function
call [pInterrupt] ; this is now a pointer to Intel's int
; check if it actually found any drives
cmp ds:[num_drives],0
jnz @F
mov ah,9 ; write to stdout
mov dx,offset nodrives + S2FAC
int 21h
mov es:[bx].InitRq.lpEnd,0 ; unload
mov word ptr es:[bx+2].InitRq.lpEnd,cs
mov es:[bx].RqHdr.wStatus,8002h ; drive not ready
jmp @@finished
@@:
; Relocate Stage 3