blob: 4b49bdaa84ed21d4d951d3258ec22c9ebfd0190b [file] [log] [blame]
\ *****************************************************************************
\ * Copyright (c) 2011 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
\ ****************************************************************************/
." Populating " pwd
0 CONSTANT vscsi-debug
0 VALUE vscsi-unit
\ -----------------------------------------------------------
\ Direct DMA conversion hack
\ -----------------------------------------------------------
: l2dma ( laddr - dma_addr)
;
\ -----------------------------------------------------------
\ CRQ related functions
\ -----------------------------------------------------------
0 VALUE crq-base
0 VALUE crq-dma
0 VALUE crq-offset
1000 CONSTANT CRQ-SIZE
CREATE crq 10 allot
: crq-alloc ( -- )
\ XXX We rely on SLOF alloc-mem being aligned
CRQ-SIZE alloc-mem to crq-base 0 to crq-offset
crq-base l2dma to crq-dma
;
: crq-free ( -- )
vscsi-unit hv-free-crq
crq-base CRQ-SIZE free-mem 0 to crq-base
;
: crq-init ( -- res )
\ Allocate CRQ. XXX deal with fail
crq-alloc
vscsi-debug IF
." VSCSI: allocated crq at " crq-base . cr
THEN
\ Clear buffer
crq-base CRQ-SIZE erase
\ Register with HV
vscsi-unit crq-dma CRQ-SIZE hv-reg-crq
\ Fail case
dup 0 <> IF
." VSCSI: Error " . ." registering CRQ !" cr
crq-free
THEN
;
: crq-cleanup ( -- )
crq-base 0 = IF EXIT THEN
vscsi-debug IF
." VSCSI: freeing crq at " crq-base . cr
THEN
crq-free
;
: crq-send ( msgaddr -- true | false )
vscsi-unit swap hv-send-crq 0 =
;
: crq-poll ( -- true | false)
crq-offset crq-base + dup
vscsi-debug IF
." VSCSI: crq poll " dup .
THEN
c@
vscsi-debug IF
." value=" dup . cr
THEN
80 and 0 <> IF
dup crq 10 move
0 swap c!
crq-offset 10 + dup CRQ-SIZE >= IF drop 0 THEN to crq-offset
true
ELSE drop false THEN
;
: crq-wait ( -- true | false)
\ FIXME: Add timeout
0 BEGIN drop crq-poll dup not WHILE d# 1 ms REPEAT
dup not IF
." VSCSI: Timeout waiting response !" cr EXIT
ELSE
vscsi-debug IF
." VSCSI: got crq: " crq dup l@ . ." " 4 + dup l@ . ." "
4 + dup l@ . ." " 4 + l@ . cr
THEN
THEN
;
\ -----------------------------------------------------------
\ CRQ encapsulated SRP definitions
\ -----------------------------------------------------------
01 CONSTANT VIOSRP_SRP_FORMAT
02 CONSTANT VIOSRP_MAD_FORMAT
03 CONSTANT VIOSRP_OS400_FORMAT
04 CONSTANT VIOSRP_AIX_FORMAT
06 CONSTANT VIOSRP_LINUX_FORMAT
07 CONSTANT VIOSRP_INLINE_FORMAT
struct
1 field >crq-valid
1 field >crq-format
1 field >crq-reserved
1 field >crq-status
2 field >crq-timeout
2 field >crq-iu-len
8 field >crq-iu-data-ptr
constant /crq
: srp-send-crq ( addr len -- )
80 crq >crq-valid c!
VIOSRP_SRP_FORMAT crq >crq-format c!
0 crq >crq-reserved c!
0 crq >crq-status c!
0 crq >crq-timeout w!
( len ) crq >crq-iu-len w!
( addr ) l2dma crq >crq-iu-data-ptr x!
crq crq-send
not IF
." VSCSI: Error sending CRQ !" cr
THEN
;
: srp-wait-crq ( -- [tag true] | false )
crq-wait not IF false EXIT THEN
crq >crq-format c@ VIOSRP_SRP_FORMAT <> IF
." VSCSI: Unsupported SRP response: "
crq >crq-format c@ . cr
false EXIT
THEN
crq >crq-iu-data-ptr x@ true
;
\ Add scsi functions to dictionary
scsi-open
\ -----------------------------------------------------------
\ SRP definitions
\ -----------------------------------------------------------
0 VALUE >srp_opcode
00 CONSTANT SRP_LOGIN_REQ
01 CONSTANT SRP_TSK_MGMT
02 CONSTANT SRP_CMD
03 CONSTANT SRP_I_LOGOUT
c0 CONSTANT SRP_LOGIN_RSP
c1 CONSTANT SRP_RSP
c2 CONSTANT SRP_LOGIN_REJ
80 CONSTANT SRP_T_LOGOUT
81 CONSTANT SRP_CRED_REQ
82 CONSTANT SRP_AER_REQ
41 CONSTANT SRP_CRED_RSP
42 CONSTANT SRP_AER_RSP
02 CONSTANT SRP_BUF_FORMAT_DIRECT
04 CONSTANT SRP_BUF_FORMAT_INDIRECT
struct
1 field >srp-login-opcode
3 +
8 field >srp-login-tag
4 field >srp-login-req-it-iu-len
4 +
2 field >srp-login-req-buf-fmt
1 field >srp-login-req-flags
5 +
10 field >srp-login-init-port-ids
10 field >srp-login-trgt-port-ids
constant /srp-login
struct
1 field >srp-lresp-opcode
3 +
4 field >srp-lresp-req-lim-delta
8 field >srp-lresp-tag
4 field >srp-lresp-max-it-iu-len
4 field >srp-lresp-max-ti-iu-len
2 field >srp-lresp-buf-fmt
1 field >srp-lresp-flags
constant /srp-login-resp
struct
1 field >srp-lrej-opcode
3 +
4 field >srp-lrej-reason
8 field >srp-lrej-tag
8 +
2 field >srp-lrej-buf-fmt
constant /srp-login-rej
00 CONSTANT SRP_NO_DATA_DESC
01 CONSTANT SRP_DATA_DESC_DIRECT
02 CONSTANT SRP_DATA_DESC_INDIRECT
struct
1 field >srp-cmd-opcode
1 field >srp-cmd-sol-not
3 +
1 field >srp-cmd-buf-fmt
1 field >srp-cmd-dout-desc-cnt
1 field >srp-cmd-din-desc-cnt
8 field >srp-cmd-tag
4 +
8 field >srp-cmd-lun
1 +
1 field >srp-cmd-task-attr
1 +
1 field >srp-cmd-add-cdb-len
10 field >srp-cmd-cdb
0 field >srp-cmd-cdb-add
constant /srp-cmd
struct
1 field >srp-rsp-opcode
1 field >srp-rsp-sol-not
2 +
4 field >srp-rsp-req-lim-delta
8 field >srp-rsp-tag
2 +
1 field >srp-rsp-flags
1 field >srp-rsp-status
4 field >srp-rsp-dout-res-cnt
4 field >srp-rsp-din-res-cnt
4 field >srp-rsp-sense-len
4 field >srp-rsp-resp-len
0 field >srp-rsp-data
constant /srp-rsp
\ Storage for up to 256 bytes SRP request */
CREATE srp 100 allot
0 VALUE srp-len
: srp-prep-cmd-nodata ( srplun -- )
srp /srp-cmd erase
SRP_CMD srp >srp-cmd-opcode c!
1 srp >srp-cmd-tag x!
srp >srp-cmd-lun x! \ 8 bytes lun
/srp-cmd to srp-len
;
: srp-prep-cmd-io ( addr len srplun -- )
srp-prep-cmd-nodata ( addr len )
swap l2dma ( len dmaaddr )
srp srp-len + ( len dmaaddr descaddr )
dup >r x! r> 8 + ( len descaddr+8 )
dup 0 swap l! 4 + ( len descaddr+c )
l!
srp-len 10 + to srp-len
;
: srp-prep-cmd-read ( addr len srplun -- )
srp-prep-cmd-io
01 srp >srp-cmd-buf-fmt c! \ in direct buffer
1 srp >srp-cmd-din-desc-cnt c!
;
: srp-prep-cmd-write ( addr len srplun -- )
srp-prep-cmd-io
10 srp >srp-cmd-buf-fmt c! \ out direct buffer
1 srp >srp-cmd-dout-desc-cnt c!
;
: srp-send-cmd ( -- )
vscsi-debug IF
." VSCSI: Sending SCSI cmd " srp >srp-cmd-cdb c@ . cr
THEN
srp srp-len srp-send-crq
;
: srp-rsp-find-sense ( -- addr )
\ XXX FIXME: Always in same position
srp >srp-rsp-data
;
: srp-wait-rsp ( -- true | [ ascq asc sense-key false ] )
srp-wait-crq not IF false EXIT THEN
dup 1 <> IF
." VSCSI: Invalid CRQ response tag, want 1 got " . cr
false EXIT
THEN drop
srp >srp-rsp-tag x@ dup 1 <> IF
." VSCSI: Invalid SRP response tag, want 1 got " . cr
false EXIT
THEN drop
srp >srp-rsp-status c@
vscsi-debug IF
." VSCSI: Got response status: "
dup .status-text cr
THEN
0 <> IF
srp-rsp-find-sense
scsi-get-sense-data
vscsi-debug IF
." VSCSI: Sense key: " dup .sense-text cr
THEN
false EXIT
THEN
true
;
\ -----------------------------------------------------------
\ Core VSCSI
\ -----------------------------------------------------------
CREATE sector d# 512 allot
8000000000000000 INSTANCE VALUE current-target
\ SCSI test-unit-read
: test-unit-ready ( -- true | [ ascq asc sense-key false ] )
current-target srp-prep-cmd-nodata
srp >srp-cmd-cdb scsi-build-test-unit-ready
srp-send-cmd
srp-wait-rsp
;
: inquiry ( -- true | false )
\ WARNING: ATAPI devices with libata seem to ignore the MSB of
\ the allocation length... let's only ask for ff bytes
sector ff current-target srp-prep-cmd-read
ff srp >srp-cmd-cdb scsi-build-inquiry
srp-send-cmd
srp-wait-rsp
dup not IF nip nip nip EXIT THEN \ swallow sense
;
: report-luns ( -- true | false )
sector 200 current-target srp-prep-cmd-read
200 srp >srp-cmd-cdb scsi-build-report-luns
srp-send-cmd
srp-wait-rsp
dup not IF nip nip nip EXIT THEN \ swallow sense
;
: read-capacity ( -- true | false )
sector scsi-length-read-cap-10 current-target srp-prep-cmd-read
srp >srp-cmd-cdb scsi-build-read-cap-10
srp-send-cmd
srp-wait-rsp
dup not IF nip nip nip EXIT THEN \ swallow sense
;
: start-stop-unit ( state# -- true | false )
current-target srp-prep-cmd-nodata
srp >srp-cmd-cdb scsi-build-start-stop-unit
srp-send-cmd
srp-wait-rsp
dup not IF nip nip nip EXIT THEN \ swallow sense
;
: get-media-event ( -- true | false )
sector scsi-length-media-event current-target srp-prep-cmd-read
srp >srp-cmd-cdb scsi-build-get-media-event
srp-send-cmd
srp-wait-rsp
dup not IF nip nip nip EXIT THEN \ swallow sense
;
: read-blocks ( -- addr block# #blocks blksz -- [ #read-blocks true ] | false )
over * ( addr block# #blocks len )
>r rot r> ( block# #blocks addr len )
5 0 DO
2dup current-target
srp-prep-cmd-read ( block# #blocks addr len )
2swap ( addr len block# #blocks )
2dup srp >srp-cmd-cdb scsi-build-read-10 ( addr len block# #blocks )
2swap ( block# #blocks addr len )
srp-send-cmd
srp-wait-rsp
IF 2drop nip true UNLOOP EXIT THEN
srp >srp-rsp-status c@ 8 <> IF
nip nip nip 2drop 2drop false EXIT
THEN
3drop
100 ms
LOOP
2drop 2drop false
;
\ Cleanup behind us
: vscsi-cleanup
\ ." VSCSI: Cleaning up" cr
crq-cleanup
\ Disable TCE bypass:
vscsi-unit 0 rtas-set-tce-bypass
;
\ Initialize our vscsi instance
: vscsi-init ( -- true | false )
." VSCSI: Initializing" cr
\ Can't use my-unit bcs we aren't instanciating (fix this ?)
" reg" get-node get-package-property IF
." VSCSI: Not reg property !!!" 0
THEN
decode-int to vscsi-unit 2drop
\ Enable TCE bypass special qemu feature
vscsi-unit 1 rtas-set-tce-bypass
\ Initialize CRQ
crq-init 0 <> IF false EXIT THEN
\ Send init command
" "(C0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00)" drop
crq-send not IF
." VSCSI: Error sending init command"
crq-cleanup false EXIT
THEN
\ Wait reply
crq-wait not IF
crq-cleanup false EXIT
THEN
\ Check init reply
crq c@ c0 <> crq 1 + c@ 02 <> or IF
." VSCSI: Initial handshake failed"
crq-cleanup false EXIT
THEN
\ We should now login etc.. but we really don't need to
\ with our qemu model
\ Ensure we cleanup after booting
['] vscsi-cleanup add-quiesce-xt
true
;
\ -----------------------------------------------------------
\ SCSI scan at boot and child device support
\ -----------------------------------------------------------
\ We use SRP luns of the form 8000 | (bus << 8) | (id << 5) | lun
\ in the top 16 bits of the 64-bit LUN
: set-target ( srplun -- )
to current-target
;
: dev-max-transfer ( -- n )
10000 \ Larger value seem to have problems with some CDROMs
;
: dev-get-capacity ( -- blocksize #blocks )
\ Make sure that there are zeros in the buffer in case something goes wrong:
sector 10 erase
\ Now issue the read-capacity command
read-capacity not IF
0 0 EXIT
THEN
sector scsi-get-capacity-10
;
: dev-read-blocks ( -- addr block# #blocks blksize -- #read-blocks )
read-blocks
;
: initial-test-unit-ready ( -- true | [ ascq asc sense-key false ] )
0 0 0 false
3 0 DO
2drop 2drop
test-unit-ready dup IF UNLOOP EXIT THEN
LOOP
;
: compare-sense ( ascq asc key ascq2 asc2 key2 -- true | false )
3 pick = ( ascq asc key ascq2 asc2 keycmp )
swap 4 pick = ( ascq asc key ascq2 keycmp asccmp )
rot 5 pick = ( ascq asc key keycmp asccmp ascqcmp )
and and nip nip nip
;
0 CONSTANT CDROM-READY
1 CONSTANT CDROM-NOT-READY
2 CONSTANT CDROM-NO-DISK
3 CONSTANT CDROM-TRAY-OPEN
4 CONSTANT CDROM-INIT-REQUIRED
5 CONSTANT CDROM-TRAY-MAYBE-OPEN
: cdrom-status ( -- status )
initial-test-unit-ready
IF CDROM-READY EXIT THEN
vscsi-debug IF
." TestUnitReady sense: " 3dup . . . cr
THEN
3dup 1 4 2 compare-sense IF
3drop CDROM-NOT-READY EXIT
THEN
get-media-event IF
sector w@ 4 >= IF
sector 2 + c@ 04 = IF
sector 5 + c@
dup 02 and 0<> IF drop 3drop CDROM-READY EXIT THEN
dup 01 and 0<> IF drop 3drop CDROM-TRAY-OPEN EXIT THEN
drop 3drop CDROM-NO-DISK EXIT
THEN
THEN
THEN
3dup 2 4 2 compare-sense IF
3drop CDROM-INIT-REQUIRED EXIT
THEN
over 4 = over 2 = and IF
\ Format in progress... what do we do ? Just ignore
3drop CDROM-READY EXIT
THEN
over 3a = IF
3drop CDROM-NO-DISK EXIT
THEN
\ Other error...
3drop CDROM-TRAY-MAYBE-OPEN
;
: cdrom-try-close-tray ( -- )
scsi-const-load start-stop-unit drop
;
: cdrom-must-close-tray ( -- )
scsi-const-load start-stop-unit not IF
." Tray open !" cr -65 throw
THEN
;
: dev-prep-cdrom ( -- )
5 0 DO
cdrom-status CASE
CDROM-READY OF UNLOOP EXIT ENDOF
CDROM-NO-DISK OF ." No medium !" cr -65 THROW ENDOF
CDROM-TRAY-OPEN OF cdrom-must-close-tray ENDOF
CDROM-INIT-REQUIRED OF cdrom-try-close-tray ENDOF
CDROM-TRAY-MAYBE-OPEN OF cdrom-try-close-tray ENDOF
ENDCASE
d# 1000 ms
LOOP
." Drive not ready !" cr -65 THROW
;
: dev-prep-disk ( -- )
initial-test-unit-ready 0= IF
." Disk not ready!" cr
3drop
THEN
;
: vscsi-create-disk ( srplun -- )
" disk" 0 " vio-vscsi-device.fs" included
;
: vscsi-create-cdrom ( srplun -- )
" cdrom" 1 " vio-vscsi-device.fs" included
;
: wrapped-inquiry ( -- true | false )
inquiry not IF false EXIT THEN
\ Skip devices with PQ != 0
sector inquiry-data>peripheral c@ e0 and 0 =
;
8 CONSTANT #dev
: vscsi-read-lun ( addr -- lun true | false )
dup c@ C0 AND CASE
40 OF w@-be 3FFF AND TRUE ENDOF
0 OF w@-be TRUE ENDOF
dup dup OF ." Unsupported LUN format = " . cr FALSE ENDOF
ENDCASE
;
: vscsi-report-luns ( -- array ndev )
\ array of pointers, up to 8 devices
#dev 3 << alloc-mem dup
0 ( devarray devcur ndev )
#dev 0 DO
i 8 << 8000 or 30 << set-target
report-luns IF
sector l@ ( devarray devcur ndev size )
sector 8 + swap ( devarray devcur ndev lunarray size )
dup 8 + dup alloc-mem ( devarray devcur ndev lunarray size size+ mem )
dup rot 0 fill ( devarray devcur ndev lunarray size mem )
dup >r swap move r> ( devarray devcur ndev mem )
dup sector l@ 3 >> 0 DO ( devarray devcur ndev mem memcur )
dup dup vscsi-read-lun IF
j 8 << 8000 or or 30 << swap x! 8 +
ELSE
2drop
THEN
LOOP drop
rot ( devarray ndev mem devcur )
dup >r x! r> 8 + ( devarray ndev devcur )
swap 1 +
THEN
LOOP
nip
;
: vscsi-find-disks ( -- )
." VSCSI: Looking for devices" cr
vscsi-report-luns
0 ?DO
dup x@
BEGIN
dup x@
dup 0= IF drop TRUE ELSE
set-target wrapped-inquiry IF
." " current-target (u.) type ." "
\ XXX FIXME: Check top bits to ignore unsupported units
\ and maybe provide better printout & more cases
\ XXX FIXME: Actually check for LUNs
sector inquiry-data>peripheral c@ CASE
0 OF ." DISK : " current-target vscsi-create-disk ENDOF
5 OF ." CD-ROM : " current-target vscsi-create-cdrom ENDOF
7 OF ." OPTICAL : " current-target vscsi-create-cdrom ENDOF
e OF ." RED-BLOCK: " current-target vscsi-create-disk ENDOF
dup dup OF ." ? (" . 8 emit 29 emit 5 spaces ENDOF
ENDCASE
sector .inquiry-text cr
THEN
8 + FALSE
THEN
UNTIL drop
8 +
LOOP drop
;
\ Remove scsi functions from word list
scsi-close
: setup-alias
" scsi" find-alias 0= IF
" scsi" get-node node>path set-alias
ELSE
drop
THEN
;
: vscsi-init-and-scan ( -- )
\ Create instance for scanning:
0 0 get-node open-node ?dup 0= IF EXIT THEN
my-self >r
dup to my-self
\ Scan the VSCSI bus:
vscsi-init IF
vscsi-find-disks
setup-alias
THEN
\ Close the temporary instance:
close-node
r> to my-self
;
\ Create a dummy "disk" node with no unit for the sake of
\ the SLES11 installer. It is -not- a fully functional node
\ you can open to access a disk at this stage
new-device
s" disk" device-name
s" block" device-type
finish-device
vscsi-init-and-scan