blob: 939514887cb84f89021ea12bce5143a48ed743d8 [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
false VALUE vscsi-debug?
0 VALUE vscsi-unit
\ -----------------------------------------------------------
\ Direct DMA conversion hack
\ -----------------------------------------------------------
: l2dma ( laddr - dma_addr)
;
\ -----------------------------------------------------------
\ CRQ related functions
\ -----------------------------------------------------------
0 VALUE crq-real-base
0 VALUE crq-base
0 VALUE crq-dma
0 VALUE crq-offset
1000 CONSTANT CRQ-SIZE
CREATE crq 10 allot
: crq-alloc ( -- )
\ Allocate enough to align to a page
CRQ-SIZE fff + alloc-mem to crq-real-base
\ align the result
crq-real-base fff + fffff000 AND to crq-base 0 to crq-offset
crq-base l2dma to crq-dma
;
: crq-free ( -- )
vscsi-unit hv-free-crq
crq-real-base CRQ-SIZE fff + free-mem 0 to crq-base 0 to crq-real-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
\ Constants for srp-rsp-flags
01 CONSTANT SRP_RSP_FLAG_RSPVALID
02 CONSTANT SRP_RSP_FLAG_SNSVALID
04 CONSTANT SRP_RSP_FLAG_DOOVER
05 CONSTANT SRP_RSP_FLAG_DOUNDER
06 CONSTANT SRP_RSP_FLAG_DIOVER
07 CONSTANT SRP_RSP_FLAG_DIUNDER
\ 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 len true | false )
srp >srp-rsp-flags c@ SRP_RSP_FLAG_SNSVALID and 0= IF
false EXIT
THEN
\ XXX FIXME: We assume the sense data is right at response
\ data. A different server might actually have both
\ some response data we need to skip *and* some sense
\ data.
srp >srp-rsp-data srp >srp-rsp-sense-len l@ true
;
\ Wait for a response to the last sent SRP command
\ returns a SCSI status code or -1 (HW error).
\
: srp-wait-rsp ( -- stat )
srp-wait-crq not IF false EXIT THEN
dup 1 <> IF
." VSCSI: Invalid CRQ response tag, want 1 got " . cr
-1 EXIT
THEN drop
srp >srp-rsp-tag x@ dup 1 <> IF
." VSCSI: Invalid SRP response tag, want 1 got " . cr
-1 EXIT
THEN drop
srp >srp-rsp-status c@
vscsi-debug? IF
." VSCSI: Got response status: "
dup .status-text cr
THEN
;
\ -----------------------------------------------------------
\ Perform SCSI commands
\ -----------------------------------------------------------
8000000000000000 INSTANCE VALUE current-target
\ SCSI command. We do *NOT* implement the "standard" execute-command
\ because that doesn't have a way to return the sense buffer back, and
\ we do have auto-sense with some hosts. Instead we implement a made-up
\ do-scsi-command.
\
\ Note: stat is -1 for "hw error" (ie, error queuing the command or
\ getting the response).
\
\ A sense buffer is returned whenever the status is non-0 however
\ if sense-len is 0 then no sense data is actually present
\
: execute-scsi-command ( buf-addr buf-len dir cmd-addr cmd-len -- ... )
( ... [ sense-buf sense-len ] stat )
\ Stash command addr & len
>r >r ( buf-addr buf-len dir )
\ Command has no data ?
over 0= IF
3drop current-target srp-prep-cmd-nodata
ELSE
\ Command is a read ?
current-target swap IF srp-prep-cmd-read ELSE srp-prep-cmd-write THEN
THEN
\ Recover command and copy it to our srp buffer
r> r>
srp >srp-cmd-cdb swap move
srp-send-cmd
srp-wait-rsp
\ Check for HW error
dup -1 = IF
0 0 rot EXIT
THEN
\ Other error status
dup 0<> IF
srp-rsp-find-sense IF
vscsi-debug? IF
over scsi-get-sense-data
." VSCSI: Sense key [ " dup . ." ] " .sense-text
." ASC,ASCQ: " . . cr
THEN
ELSE 0 0
\ This relies on auto-sense from qemu... if that isn't always the
\ case we should request sense here
." VSCSI: No sense data" cr
THEN
rot
THEN
;
\ --------------------------------
\ Include the generic host helpers
\ --------------------------------
" scsi-host-helpers.fs" included
TRUE VALUE first-time-init?
0 VALUE open-count
\ Cleanup behind us
: vscsi-cleanup
vscsi-debug? IF ." VSCSI: Cleaning up" cr THEN
crq-cleanup
\ Disable TCE bypass:
vscsi-unit 0 rtas-set-tce-bypass
;
\ Initialize our vscsi instance
: vscsi-init ( -- true | false )
vscsi-debug? IF ." VSCSI: Initializing" cr THEN
my-unit to vscsi-unit
\ 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
first-time-init? IF
['] vscsi-cleanup add-quiesce-xt
false to first-time-init?
THEN
true
;
: open
vscsi-debug? IF ." VSCSI: Opening (count is " open-count . ." )" cr THEN
open-count 0= IF
vscsi-init IF
1 to open-count true
ELSE ." VSCSI initialization failed !" cr false THEN
ELSE
open-count 1 + to open-count
true
THEN
;
: close
vscsi-debug? IF ." VSCSI: Closing (count is " open-count . ." )" cr THEN
open-count 0> IF
open-count 1 - dup to open-count
0= IF
vscsi-cleanup
THEN
THEN
;
\ -----------------------------------------------------------
\ SCSI scan at boot and child device support
\ -----------------------------------------------------------
: (set-target)
to current-target
;
\ We use SRP luns of the form 8000 | (target << 8) | (bus << 5) | lun
\ in the top 16 bits of the 64-bit LUN (i.e. the "Logical unit addressing
\ method" in SAM5). Since the generic scsi-probe code of SLOF does not
\ really care about buses, we assume that the upper 3 bits of the "target"
\ value are the "bus" field.
: dev-generate-srplun ( bus+target lun -- srplun )
swap dup 1 >> e0 and ( lun bus+target bus )
swap 3f and 8 << ( lun bus target )
8000 or or or 30 <<
;
\ We obtain here a unit address on the stack, since our #address-cells
\ is 2, the 64-bit srplun is split in two cells that we need to join
\
\ Note: This diverges a bit from the original OF scsi spec as the two
\ cells are the 2 words of a 64-bit SRP LUN
: set-address ( srplun.lo srplun.hi -- )
lxjoin (set-target)
;
\ We set max-transfer to a fixed value for now to avoid problems
\ with some CD-ROM drives.
\ FIXME: Check max transfer coming from VSCSI
: max-transfer ( -- n )
10000 \ Larger value seem to have problems with some CDROMs
;
\ Report the amount of supported SCSI IDs - QEMU uses "max_target = 63"
\ and "max_channel = 7", we combine both to 64 * 8 = 512 devices
: dev-max-target ( -- #max-target )
200
;
" scsi-probe-helpers.fs" included
\ 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:
scsi-find-disks
setup-alias
\ Close the temporary instance:
close-node
r> to my-self
;
: vscsi-add-disk
" scsi-disk.fs" included
;
vscsi-add-disk
vscsi-init-and-scan