\ ***************************************************************************** \ * Copyright (c) 2004, 2008 IBM Corporation \ * All rights reserved. \ * This program and the accompanying materials \ * are made available under the terms of the BSD License \ * which accompanies this distribution, and is available at \ * http://www.opensource.org/licenses/bsd-license.php \ * \ * Contributors: \ * IBM Corporation - initial implementation \ ****************************************************************************/ \ \ 26.06.2007 added: two devices (Master/Slave) per channel 1 encode-int s" #address-cells" property 0 encode-int s" #size-cells" property : decode-unit 1 hex-decode-unit ; : encode-unit 1 hex-encode-unit ; 0 VALUE >ata \ base address for command-block 0 VALUE >ata1 \ base address for control block true VALUE no-timeout \ flag that no timeout occurred 0c CONSTANT #cdb-bytes \ command descriptor block (12 bytes) 800 CONSTANT atapi-size 200 CONSTANT ata-size \ ***************************** \ Some register access helpers. \ ***************************** : ata-ctrl! 2 >ata1 + io-c! ; \ device control reg : ata-astat@ 2 >ata1 + io-c@ ; \ read alternate status : ata-data@ 0 >ata + io-w@ ; \ data reg : ata-data! 0 >ata + io-w! ; \ data reg : ata-err@ 1 >ata + io-c@ ; \ error reg : ata-feat! 1 >ata + io-c! ; \ feature reg : ata-cnt@ 2 >ata + io-c@ ; \ sector count reg : ata-cnt! 2 >ata + io-c! ; \ sector count reg : ata-lbal! 3 >ata + io-c! ; \ lba low reg : ata-lbal@ 3 >ata + io-c@ ; \ lba low reg : ata-lbam! 4 >ata + io-c! ; \ lba mid reg : ata-lbam@ 4 >ata + io-c@ ; \ lba mid reg : ata-lbah! 5 >ata + io-c! ; \ lba high reg : ata-lbah@ 5 >ata + io-c@ ; \ lba high reg : ata-dev! 6 >ata + io-c! ; \ device reg : ata-dev@ 6 >ata + io-c@ ; \ device reg : ata-cmd! 7 >ata + io-c! ; \ command reg : ata-stat@ 7 >ata + io-c@ ; \ status reg \ ********************************************************************** \ ATA / ATAPI Commands specifications: \ - AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) \ - ATA Packet Interface for CD-ROMs SFF-8020i \ - ATA/ATAPI Host Adapters Standard (T13/1510D) \ ********************************************************************** 00 CONSTANT cmd#nop \ ATA and ATAPI 08 CONSTANT cmd#device-reset \ ATAPI only (mandatory) 20 CONSTANT cmd#read-sector \ ATA and ATAPI 90 CONSTANT cmd#execute-device-diagnostic \ ATA and ATAPI a0 CONSTANT cmd#packet \ ATAPI only (mandatory) a1 CONSTANT cmd#identify-packet-device \ ATAPI only (mandatory) ec CONSTANT cmd#identify-device \ ATA and ATAPI \ ***************************** \ Setup Regs for ATA: \ BAR 0 & 1 : Device 0 \ BAR 2 & 3 : Device 1 \ ***************************** : set-regs ( n -- ) dup 01 and \ only Chan 0 or Chan 1 allowed 3 lshift dup 10 + config-l@ -4 and to >ata 14 + config-l@ -4 and to >ata1 02 ata-ctrl! \ disable interrupts 02 and IF 10 ELSE 00 THEN ata-dev! ; ata-size VALUE block-size 80000 VALUE max-transfer \ Arbitrary, really CREATE sector d# 512 allot CREATE packet-cdb #cdb-bytes allot CREATE return-buffer atapi-size allot scsi-open \ add scsi functions \ ******************************** \ show all ATAPI-registers \ data-register not read in order \ to not influence PIO mode \ ******************************** : show-regs cr cr ." alt. Status: " ata-astat@ . cr ." Status : " ata-stat@ . cr ." Device : " ata-dev@ . cr ." Error-Reg : " ata-err@ . cr ." Sect-Count : " ata-cnt@ . cr ." LBA-Low : " ata-lbal@ . cr ." LBA-Med : " ata-lbam@ . cr ." LBA-High : " ata-lbah@ . ; \ *************************************************** \ reads ATAPI-Status and displays it if check-bit set \ *************************************************** : status-check ( -- ) ata-stat@ dup 01 and \ is 'check' flag set ? IF cr ." - ATAPI-Status: " . ata-err@ \ retrieve sense code dup 60 = \ sense code = 6 ? IF ." ( media changed or reset )" \ 'unit attention' drop \ drop err-reg content ELSE dup ." (Err : " . \ show err-reg content space rshift 4 .sense-text \ show text string 29 emit THEN cr ELSE drop \ remove unused status THEN ; \ ************************************* \ Wait for interface ready condition \ Bit 7 of Status-Register is busy flag \ new version with abort after 5 sec. \ ************************************* : wait-for-ready get-msecs \ start timer BEGIN ata-stat@ 80 and 0<> \ busy flag still set ? no-timeout and WHILE \ yes dup get-msecs swap - \ calculate timer difference FFFF AND \ reduce to 65.5 seconds d# 5000 > \ difference > 5 seconds ? IF false to no-timeout THEN REPEAT drop ; \ ************************************* \ wait for specific status bits \ new version with abort after 5 sec. \ ************************************* : wait-for-status ( val mask -- ) get-msecs \ initial timer value (start) >r BEGIN 2dup \ val mask ata-stat@ and <> \ expected status ? no-timeout and \ and no timeout ? WHILE get-msecs r@ - \ calculate timer difference FFFF AND \ mask-off overflow bits d# 5000 > \ 5 seconds exceeded ? IF false to no-timeout \ set global flag THEN REPEAT r> \ clean return stack 3drop ; \ ********************************* \ remove extra spaces from string end \ ********************************* : cut-string ( saddr nul -- ) swap over + swap 1 rshift \ bytecount -> wordcount 0 do /w - dup ( addr -- addr addr ) w@ ( addr addr -- addr nuw ) dup ( addr nuw -- addr nuw nuw ) 2020 = IF drop 0 ELSE LEAVE THEN over w! LOOP drop drop ; \ **************************************************** \ prints model-string received by identify device \ **************************************************** : show-model ( dev# chan# -- ) 2dup ." CH " . \ channel 0 / 1 0= IF ." / MA" \ Master / Slave ELSE ." / SL" THEN swap 2 * + ." (@" . ." ) : " \ device number sector 1 + c@ 80 AND 0= IF ." ATA-Drive " ELSE ." ATAPI-Drive " THEN 22 emit \ start string display with " sector d# 54 + \ string starts 54 bytes from buffer start dup d# 40 \ and is 40 chars long cut-string \ remove all trailing spaces BEGIN dup w@ wbflip wbsplit dup 0<> \ first char IF emit dup 0<> \ second char IF emit wa1+ \ increment address for next false ELSE \ second char = EndOfString drop true THEN ELSE \ first char = EndOfString drop drop true THEN UNTIL \ end of string detected drop 22 emit \ end string display sector c@ \ get lower byte of first doublet 80 AND \ check bit 7 IF ." (removable media)" THEN sector 1 + c@ 80 AND 0= IF \ is this an ATA drive ? sector d# 120 + \ get word 60 + 61 rl@-le \ read 32-bit as little endian value d# 512 \ standard ATA block-size swap .capacity-text ( block-size #blocks -- ) THEN sector d# 98 + \ goto word 49 w@ wbflip 200 and 0= IF cr ." ** LBA is not supported " THEN sector c@ \ get lower byte of first doublet 03 AND 01 = \ we use 12-byte packet commands (=00b) IF cr ." packet size = 16 ** not supported ! **" THEN no-timeout not \ any timeout occurred so far ? IF cr ." ** timeout **" THEN ; \ **************************** \ ATA functions \ **************************** : pio-sector ( addr -- ) 100 0 DO ata-data@ over w! wa1+ LOOP drop ; : pio-sector ( addr -- ) wait-for-ready pio-sector ; : pio-sectors ( n addr -- ) swap 0 ?DO dup pio-sector 200 + LOOP drop ; : lba! lbsplit 0f and 40 or \ always set LBA-mode + LBA (27..24) ata-dev@ 10 and or \ add current device-bit (DEV) ata-dev! \ set LBA (27..24) ata-lbah! \ set LBA (23..16) ata-lbam! \ set LBA (15..8) ata-lbal! \ set LBA (7..0) ; : read-sectors ( lba count addr -- ) >r dup >r ata-cnt! lba! 20 ata-cmd! r> r> pio-sectors ; : read-sectors ( lba count addr dev-nr -- ) set-regs ( lba count addr ) \ Set ata regs BEGIN >r dup 100 > WHILE over 100 r@ read-sectors >r 100 + r> 100 - r> 20000 + REPEAT r> read-sectors ; : ata-read-blocks ( addr block# #blocks dev# -- #read ) swap dup >r swap >r rot r> ( addr block# #blocks dev # R: #blocks ) read-sectors r> ( R: #read ) ; \ ******************************* \ ATAPI functions \ preset LBA register with maximum \ allowed block-size (16-bits) \ ******************************* : set-lba ( block-length -- ) lbsplit ( quad -- b1.lo b2 b3 b4.hi ) drop \ skip upper two bytes drop ata-lbah! ata-lbam! ; \ ******************************************* \ gets byte-count and reads a block of words \ from data-register to a buffer \ ******************************************* : read-pio-block ( buff-addr -- buff-addr-new ) ata-lbah@ 8 lshift \ get block length High ata-lbam@ or \ get block length Low 1 rshift \ bcount -> wcount dup 0> IF \ any data to transfer? 0 DO \ words to read dup \ buffer-address ata-data@ swap w! \ write 16-bits wa1+ \ address of next entry LOOP ELSE drop ( buff-addr wcount -- buff-addr ) THEN wait-for-ready ; \ ******************************************** \ ATAPI support \ Send a command block (12 bytes) in PIO mode \ read data if requested \ ******************************************** : send-atapi-packet ( req-buffer -- ) >r ( R: req-buffer ) atapi-size set-lba \ set regs to length limit 00 ata-feat! cmd#packet ata-cmd! \ A0 = ATAPI packet command 48 C8 wait-for-status ( val mask -- ) \ BSY:0 DRDY:1 DRQ:1 6 0 do packet-cdb i 2 * + \ transfer command block (12 bytes) w@ ata-data! \ 6 doublets PIO transfer to device loop \ copy packet to data-reg status-check ( -- ) \ status err bit set ? -> display wait-for-ready ( -- ) \ busy released ? BEGIN ata-stat@ 08 and 08 = WHILE \ Data-Request-Bit set ? r> \ get last target buffer address read-pio-block \ only if from device requested >r \ start of next block REPEAT r> \ original value drop \ return clean ; : atapi-packet-io ( -- ) return-buffer atapi-size erase \ clear return buffer return-buffer send-atapi-packet \ send 'packet-cdb' , get 'return-buffer' ; \ ******************************** \ ATAPI packet commands \ ******************************** \ Methods to access atapi disk : atapi-test ( -- true|false ) packet-cdb scsi-build-test-unit-ready \ command-code: 00 atapi-packet-io ( ) \ send CDB, get return-buffer ata-stat@ 1 and IF false ELSE true THEN ; : atapi-sense ( -- ascq asc sense-key ) d# 252 packet-cdb scsi-build-request-sense ( alloc-len cdb -- ) atapi-packet-io ( ) \ send CDB, get return-buffer return-buffer scsi-get-sense-data ( cdb-addr -- ascq asc sense-key ) ; : atapi-read-blocks ( address block# #blocks dev# -- #read-blocks ) set-regs ( address block# #blocks ) dup >r ( address block# #blocks ) packet-cdb scsi-build-read-10 ( address block# #blocks cdb -- ) send-atapi-packet ( address -- ) r> \ return requested number of blocks ; \ *************************************** \ read capacity of drive medium \ use SCSI-Support Package \ *************************************** : atapi-read-capacity ( -- ) packet-cdb scsi-build-read-cap-10 \ fill block with command atapi-packet-io ( ) \ send CDB, get return-buffer return-buffer scsi-get-capacity-10 ( cdb -- block-size #blocks ) .capacity-text ( block-size #blocks -- ) status-check ( -- ) ; \ *************************************** \ read capacity of drive medium \ use SCSI-Support Package \ *************************************** : atapi-read-capacity-ext ( -- ) packet-cdb scsi-build-read-cap-16 \ fill block with command atapi-packet-io ( ) \ send CDB, get return-buffer return-buffer scsi-get-capacity-16 ( cdb -- block-size #blocks ) .capacity-text ( block-size #blocks -- ) status-check ( -- ) ; \ *********************************************** \ wait until media in drive is ready ( max 5 sec) \ *********************************************** : wait-for-media-ready ( -- true|false ) get-msecs \ initial timer value (start) >r BEGIN atapi-test \ unit ready? false if not not no-timeout and WHILE atapi-sense ( -- ascq asc sense-key ) 02 = \ sense key 2 = media error IF \ check add. sense code 3A = \ asc: device not ready ? IF false to no-timeout ." empty (" . 29 emit \ show asc qualifier ELSE drop \ discard asc qualifier THEN \ medium not present, abort waiting ELSE drop \ discard asc drop \ discard ascq THEN get-msecs r@ - \ calculate timer difference FFFF AND \ mask-off overflow bits d# 5000 > \ 5 seconds exceeded ? IF false to no-timeout \ set global flag THEN REPEAT r> drop no-timeout ; \ ****************************************************** \ Method pointer for read-blocks methods \ controller implements 2 channels (primary / secondary) \ for 2 devices each (master / slasve) \ ****************************************************** \ 2 channels (primary/secondary) per controller 2 CONSTANT #chan \ 2 devices (master/slave) per channel 2 CONSTANT #dev \ results in a total of devices \ connected to a controller with \ two separate channels (4) : #totaldev #dev #chan * ; CREATE read-blocks-xt #totaldev cells allot read-blocks-xt #totaldev cells erase \ Execute read-blocks of device : dev-read-blocks ( address block# #blocks dev# -- #read-blocks ) dup cells read-blocks-xt + @ execute ; \ ********************************************************** \ Read device type \ Signature ATAPI ATA \ --------------------------------------------- \ Sector Count 01h 01h \ Sector Number 01h 01h \ Cylinder Low 14h 00h \ Cylinder High EBh 00h \ Device/Head 00h or 10h 00h or 01h \ see also ATA/ATAPI errata at: \ http://suif.stanford.edu/~csapuntz/blackmagic.html \ ********************************************************** : read-ident ( -- true|false ) false 00 ata-lbal! \ clear previous signature 00 ata-lbam! 00 ata-lbah! cmd#identify-device ata-cmd! wait-for-ready \ first try ATA, ATAPI aborts command ata-stat@ CF and 48 = IF drop true \ cmd accepted, this is a ATA d# 512 set-lba \ set LBA to sector-length ELSE \ ATAPI sends signature instead ata-lbam@ 14 = IF \ cylinder low = 14 ? ata-lbah@ EB = IF \ cylinder high = EB ? cmd#device-reset ata-cmd! wait-for-ready \ only supported by ATAPI cmd#identify-packet-device ata-cmd! wait-for-ready \ first try ata ata-stat@ CF and 48 = IF drop true \ replace flag THEN THEN THEN THEN dup IF ata-stat@ 8 AND IF \ data requested (as expected) ? sector read-pio-block drop \ discard address end ELSE drop false THEN THEN no-timeout not IF \ check without any timeout ? drop false \ no, detection discarded THEN ; scsi-close \ remove scsi commands from word list \ ************************************************* \ Init controller ( chan 0 and 1 ) \ device 0 (= master) and device 1 ( = slave) \ #dev #chan Dev-ID \ ---------------------- \ 0 0 0 Master of Channel 0 \ 0 1 1 Master of Channel 1 \ 1 0 2 Slave of Channel 0 \ 1 1 3 Slave of Channel 1 \ ************************************************* : find-disks ( -- ) #chan 0 DO \ check 2 channels (primary & secondary) #dev 0 DO \ check 2 devices per channel (master / slave) i 2 * j + set-regs \ set base address and dev-register for register access ata-stat@ 7f and 7f <> \ Check, if device is connected IF true to no-timeout \ preset timeout-flag read-ident ( -- true|false ) IF i j show-model \ print manufacturer + device string sector 1+ c@ C0 and 80 = \ Check for ata or atapi IF wait-for-media-ready \ wait up to 5 sec if not ready no-timeout and IF atapi-read-capacity atapi-size to block-size \ ATAPI: 2048 bytes 80000 to max-transfer ['] atapi-read-blocks i 2 * j + cells read-blocks-xt + ! s" cdrom" strdup i 2 * j + s" generic-disk.fs" included ELSE ." -" \ show hint for not registered THEN ELSE ata-size to block-size \ ATA: 512 bytes 80000 to max-transfer ['] ata-read-blocks i 2 * j + cells read-blocks-xt + ! s" disk" strdup i 2 * j + s" generic-disk.fs" included THEN cr THEN THEN i 2 * j + 200 + cp LOOP LOOP ; find-disks