/* * Copyright (c) 2023 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "detritus.h" #include "request.h" struct scsi_inquiry { unsigned char deviceType; unsigned char deviceQualifier; unsigned char version; unsigned char responseFormat; unsigned char additionalLength; unsigned char vendor; short reserved; unsigned char vendorID[8]; unsigned char productID[16]; unsigned char revision[4]; unsigned char vendor2[20]; unsigned char reserved2[42]; unsigned char vendor3[158]; }; enum { SCSI_READ, SCSI_READ_UNKNOWN_SIZE, SCSI_WRITE }; #define BLUESCSI_TLS_CMD 0x1d #define BLUESCSI_TLS_CMD_INIT 0x01 #define BLUESCSI_TLS_CMD_STATUS 0x02 #define BLUESCSI_TLS_CMD_READ_PLAIN 0x03 #define BLUESCSI_TLS_CMD_WRITE_PLAIN 0x04 #define BLUESCSI_TLS_CMD_READ_CIPHER 0x05 #define BLUESCSI_TLS_CMD_WRITE_CIPHER 0x06 #define BLUESCSI_TLS_CMD_CLOSE 0x07 uint8_t tls_req_last_id = 1; static short tls_scsi_id = -1; static struct SCSIInstr tib[2]; static unsigned char scsi_buf[2048]; short scsi_io(unsigned char *cdb, unsigned long cdb_len, short io_type, unsigned char *buf, unsigned long len); bool scsi_can_do_tls(void) { return (tls_scsi_id != -1); } short scsi_find_tls(void) { struct scsi_inquiry inq; unsigned char cdb[16] = { 0 }; short stat, msg, err; short i, scsi_id; cdb[0] = 0x12; /* inquiry */ cdb[4] = 5; /* length */ tib[0].scOpcode = scNoInc; tib[0].scParam1 = (unsigned long)&inq; tib[0].scParam2 = 5; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; SCSIReset(); for (scsi_id = 0; scsi_id <= 7; scsi_id++) { for (i = 0; i <= 1; i++) { if (SCSIGet() != noErr) goto scan_failed; if (SCSISelect(scsi_id) != noErr) break; if (SCSICmd((Ptr)&cdb, 6) != noErr) { SCSIComplete(&stat, &msg, 300); goto scan_failed; } memset(&inq, 0, sizeof(inq)); if (SCSIRead((Ptr)&tib) != noErr) { SCSIComplete(&stat, &msg, 300); goto scan_failed; } if (i == 0) { cdb[4] += inq.additionalLength; tib[0].scParam2 += inq.additionalLength; } if (SCSIComplete(&stat, &msg, 300) != noErr) goto scan_failed; if (memcmp(inq.vendorID, "BlueSCSI", 8) == 0 && memcmp(inq.productID, "TLS", 3) == 0) { tls_scsi_id = scsi_id; return noErr; } } } scan_failed: return -1; } uint8_t scsi_tls_init(struct tls_init_request *req) { unsigned char cdb[6]; unsigned short size; uint8_t tls_id; tls_id = ++tls_req_last_id; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_TLS_CMD; cdb[1] = BLUESCSI_TLS_CMD_INIT; cdb[2] = tls_id; size = sizeof(struct tls_init_request); cdb[3] = (size >> 8) & 0xff; cdb[4] = size & 0xff; memcpy(scsi_buf, req, size); if (scsi_io(cdb, sizeof(cdb), SCSI_WRITE, scsi_buf, size) == -1) return 0; return tls_id; } bool scsi_tls_close(uint8_t tls_id) { unsigned char cdb[6]; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_TLS_CMD; cdb[1] = BLUESCSI_TLS_CMD_CLOSE; cdb[2] = tls_id; return (scsi_io(cdb, sizeof(cdb), SCSI_WRITE, NULL, 0) != -1); } short scsi_tls_status(uint8_t tls_id, short *cipherspace, short *plainspace, short *error) { unsigned char cdb[6]; size_t size; short ret; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_TLS_CMD; cdb[1] = BLUESCSI_TLS_CMD_STATUS; cdb[2] = tls_id; ret = scsi_io(cdb, sizeof(cdb), SCSI_READ, (unsigned char *)&scsi_buf, 8); if (ret != 8) return 0; *cipherspace = ((short)scsi_buf[2] << 8) | scsi_buf[3]; *plainspace = ((short)scsi_buf[4] << 8) | scsi_buf[5]; *error = ((short)scsi_buf[6] << 8) | scsi_buf[7]; return (((short)scsi_buf[0] << 8) | scsi_buf[1]); } size_t scsi_tls_read(uint8_t tls_id, unsigned char **buf, size_t max_size, bool cipher) { unsigned char cdb[6]; short ret; if (*buf == NULL) { *buf = (unsigned char *)&scsi_buf; if (max_size == 0 || max_size > sizeof(scsi_buf)) max_size = sizeof(scsi_buf); } memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_TLS_CMD; if (cipher) cdb[1] = BLUESCSI_TLS_CMD_READ_CIPHER; else cdb[1] = BLUESCSI_TLS_CMD_READ_PLAIN; cdb[2] = tls_id; cdb[3] = (max_size >> 8) & 0xff; cdb[4] = max_size & 0xff; ret = scsi_io(cdb, sizeof(cdb), SCSI_READ_UNKNOWN_SIZE, *buf, max_size); if (ret < 0) { *buf = NULL; return 0; } return ret; } size_t scsi_tls_write(uint8_t tls_id, unsigned char *buf, size_t buf_size, bool cipher) { unsigned char cdb[6]; short ret; memset(cdb, 0, sizeof(cdb)); cdb[0] = BLUESCSI_TLS_CMD; if (cipher) cdb[1] = BLUESCSI_TLS_CMD_WRITE_CIPHER; else cdb[1] = BLUESCSI_TLS_CMD_WRITE_PLAIN; cdb[2] = tls_id; cdb[3] = (buf_size >> 8) & 0xff; cdb[4] = buf_size & 0xff; ret = scsi_io(cdb, sizeof(cdb), SCSI_WRITE, buf, buf_size); if (ret < 0) return 0; return ret; } short scsi_io(unsigned char *cdb, unsigned long cdb_len, short io_type, unsigned char *buf, unsigned long len) { short ret, ioret, stat, msg, err, tlen; if (SCSIGet() != noErr) { warn("SCSIGet failed"); return -1; } if (SCSISelect(tls_scsi_id) != noErr) { warn("SCSISelect failed"); return -1; } if (SCSICmd((Ptr)cdb, cdb_len) != noErr) { SCSIComplete(&stat, &msg, 300); warn("SCSICmd failed"); return -1; } if (len) { switch (io_type) { case SCSI_READ: memset(buf, 0, len); tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)buf; tib[0].scParam2 = len; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIRead((Ptr)&tib); break; case SCSI_READ_UNKNOWN_SIZE: memset(buf, 0, len); tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)buf; tib[0].scParam2 = 2; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIRead((Ptr)&tib); tlen = ((short)buf[0] << 8) + buf[1]; if (tlen > len) { warn("SCSI trying to read %ld bytes but buf is only %ld", tlen, len); tlen = len; } len = tlen; if (tlen) { memset(buf, 0, tlen); tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)buf; tib[0].scParam2 = tlen; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIRead((Ptr)&tib); } break; case SCSI_WRITE: tib[0].scOpcode = scNoInc; tib[0].scParam1 = (long)buf; tib[0].scParam2 = len; tib[1].scOpcode = scStop; tib[1].scParam1 = 0; tib[1].scParam2 = 0; ioret = SCSIWrite((Ptr)&tib); break; } } else { tib[0].scOpcode = scStop; tib[0].scParam1 = 0; tib[0].scParam2 = 0; ioret = SCSIWrite((Ptr)&tib); } /* complete and free the bus before responding to the read/write */ if ((ret = SCSIComplete(&stat, &msg, 300 /* 1/60 ticks */)) != noErr) { warn("SCSIComplete failed"); return -1; } if (ioret != noErr && ioret != scPhaseErr) warn("SCSIRead/Write failed"); if (stat != noErr) return -1; return len; } void scsi_cleanup(void) { short stat, msg; SCSIComplete(&stat, &msg, 60); }