Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / ppc / mol / osi-scsi.c
diff --git a/qemu/roms/openbios/arch/ppc/mol/osi-scsi.c b/qemu/roms/openbios/arch/ppc/mol/osi-scsi.c
new file mode 100644 (file)
index 0000000..18f3dc5
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *   Creation Date: <2003/12/11 21:23:54 samuel>
+ *   Time-stamp: <2004/01/07 19:38:45 samuel>
+ *
+ *     <osi-scsi.c>
+ *
+ *     SCSI device node
+ *
+ *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "mol/mol.h"
+#include "scsi_sh.h"
+#include "osi_calls.h"
+
+#define MAX_TARGETS    32
+
+typedef struct {
+       int             probed;
+       int             valid;          /* a useable device found */
+
+       int             is_cd;
+       int             blocksize;
+} target_info_t;
+
+static target_info_t   scsi_devs[ MAX_TARGETS ];
+
+typedef struct {
+       int             target;
+       target_info_t   *info;
+} instance_data_t;
+
+
+DECLARE_NODE( scsi, INSTALL_OPEN, sizeof(instance_data_t),
+             "/pci/pci-bridge/mol-scsi/sd", "/mol/mol-scsi/sd" );
+
+
+static int
+scsi_cmd_( instance_data_t *sd, const char *cmd, int cmdlen, char *dest,
+          int len, int prelen, int postlen )
+{
+       char prebuf[4096], postbuf[4096];
+       scsi_req_t r[2];        /* the [2] is a hack to get space for the sg-list */
+       char sb[32];
+
+       /* memset( dest, 0, len ); */
+
+       if( (unsigned int)prelen > sizeof(prebuf) || (unsigned int)postlen > sizeof(postbuf) ) {
+               printk("bad pre/post len %d %d\n", prelen, postlen );
+               return 1;
+       }
+
+       memset( r, 0, sizeof(r[0]) );
+       r->lun = 0;
+       r->target = sd->target;
+       r->is_write = 0;
+       memcpy( r->cdb, cmd, cmdlen );
+       r->client_addr = (int)&r;
+       r->cdb_len = cmdlen;
+       r->sense[0].base = (int)&sb;
+       r->sense[0].size = sizeof(sb);
+       r->size = prelen + len + postlen;
+       r->n_sg = 3;
+       r->sglist.n_el = 3;
+       r->sglist.vec[0].base = (int)prebuf;
+       r->sglist.vec[0].size = prelen;
+       r->sglist.vec[1].base = (int)dest;
+       r->sglist.vec[1].size = len;
+       r->sglist.vec[2].base = (int)postbuf;
+       r->sglist.vec[2].size = postlen;
+
+       if( OSI_SCSISubmit((int)&r) ) {
+               printk("OSI_SCSISubmit: error!\n");
+               return 1;
+       }
+       while( !OSI_SCSIAck() )
+               OSI_USleep( 10 );
+
+       if( r->adapter_status )
+               return -1;
+       if( r->scsi_status )
+               return ((sb[2] & 0xf) << 16) | (sb[12] << 8) | sb[13];
+       return 0;
+}
+
+static int
+scsi_cmd( instance_data_t *sd, const char *cmd, int cmdlen )
+{
+       return scsi_cmd_( sd, cmd, cmdlen, NULL, 0, 0, 0 );
+}
+
+/* ( buf blk nblks -- actual ) */
+static void
+scsi_read_blocks( instance_data_t *sd )
+{
+       int nblks = POP();
+       int blk = POP();
+       char *dest = (char*)POP();
+       unsigned char cmd[10];
+       int len = nblks * sd->info->blocksize;
+
+       memset( dest, 0, len );
+
+       /* printk("READ: blk: %d length %d\n", blk, len ); */
+       memset( cmd, 0, sizeof(cmd) );
+       cmd[0] = 0x28; /* READ_10 */
+       cmd[2] = blk >> 24;
+       cmd[3] = blk >> 16;
+       cmd[4] = blk >> 8;
+       cmd[5] = blk;
+       cmd[7] = nblks >> 8;
+       cmd[8] = nblks;
+
+       if( scsi_cmd_(sd, cmd, 10, dest, len, 0, 0) ) {
+               printk("read: scsi_cmd failed\n");
+               RET( -1 );
+       }
+       PUSH( nblks );
+}
+
+static int
+inquiry( instance_data_t *sd )
+{
+       char inquiry_cmd[6] = { 0x12, 0, 0, 0, 32, 0 };
+       char start_stop_unit_cmd[6] = { 0x1b, 0, 0, 0, 1, 0 };
+       char test_unit_ready_cmd[6] = { 0x00, 0, 0, 0, 0, 0 };
+       char prev_allow_medium_removal[6] = { 0x1e, 0, 0, 0, 1, 0 };
+       char set_cd_speed_cmd[12] = { 0xbb, 0, 0xff, 0xff, 0xff, 0xff,
+                                     0, 0, 0, 0, 0, 0 };
+       target_info_t *info = &scsi_devs[sd->target];
+       char ret[32];
+       int i, sense;
+
+       if( sd->target >= MAX_TARGETS )
+               return -1;
+       sd->info = info;
+
+       if( info->probed )
+               return info->valid ? 0:-1;
+       info->probed = 1;
+
+       if( (sense=scsi_cmd_(sd, inquiry_cmd, 6, ret, 2, 0, 0)) ) {
+               if( sense < 0 )
+                       return -1;
+               printk("INQUIRY failed\n");
+               return -1;
+       }
+
+       /* medium present? */
+       if( (scsi_cmd(sd, test_unit_ready_cmd, 6) >> 8) == 0x23a ) {
+               printk("no media\n");
+               return -1;
+       }
+
+       info->is_cd = 0;
+       info->blocksize = 512;
+
+       if( ret[0] == 5 /* CD/DVD */ ) {
+               info->blocksize = 2048;
+               info->is_cd = 1;
+
+               scsi_cmd( sd, prev_allow_medium_removal, 6 );
+               scsi_cmd( sd, set_cd_speed_cmd, 12 );
+               scsi_cmd( sd, start_stop_unit_cmd, 6 );
+
+       } else if( ret[0] == 0 /* DISK */ ) {
+               scsi_cmd( sd, test_unit_ready_cmd, 6 );
+               scsi_cmd( sd, start_stop_unit_cmd, 6 );
+       } else {
+               /* don't boot from this device (could be a scanner :-)) */
+               return -1;
+       }
+
+       /* wait for spin-up (or whatever) to complete */
+       for( i=0; ; i++ ) {
+               if( i > 300 ) {
+                       printk("SCSI timeout (sense %x)\n", sense );
+                       return -1;
+               }
+               sense = scsi_cmd( sd, test_unit_ready_cmd, 6 );
+               if( (sense & 0xf0000) == 0x20000 ) {
+                       OSI_USleep( 10000 );
+                       continue;
+               }
+               break;
+       }
+
+       info->valid = 1;
+       return 0;
+}
+
+/* ( -- success? ) */
+static void
+scsi_open( instance_data_t *sd )
+{
+       static int once = 0;
+       phandle_t ph;
+
+       fword("my-unit");
+       sd->target = POP();
+
+       if( !once ) {
+               once++;
+               OSI_SCSIControl( SCSI_CTRL_INIT, 0 );
+       }
+
+       /* obtiain device information */
+       if( inquiry(sd) )
+               RET(0);
+
+       selfword("open-deblocker");
+
+       /* interpose disk-label */
+       ph = find_dev("/packages/disk-label");
+       fword("my-args");
+       PUSH_ph( ph );
+       fword("interpose");
+
+       PUSH( -1 );
+}
+
+/* ( -- ) */
+static void
+scsi_close( instance_data_t *pb )
+{
+       selfword("close-deblocker");
+}
+
+
+/* ( -- bs ) */
+static void
+scsi_block_size( instance_data_t *sd )
+{
+       PUSH( sd->info->blocksize );
+}
+
+/* ( -- maxbytes ) */
+static void
+scsi_max_transfer( instance_data_t *sd )
+{
+       PUSH( 1024*1024 );
+}
+
+static void
+scsi_initialize( instance_data_t *sd )
+{
+       fword("is-deblocker");
+}
+
+
+NODE_METHODS( scsi ) = {
+       { NULL,                 scsi_initialize },
+       { "open",               scsi_open               },
+       { "close",              scsi_close              },
+       { "read-blocks",        scsi_read_blocks        },
+       { "block-size",         scsi_block_size },
+       { "max-transfer",       scsi_max_transfer       },
+};
+
+void
+osiscsi_init( void )
+{
+       REGISTER_NODE( scsi );
+}