| \ ***************************************************************************** |
| \ * 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 |