1 // Code for handling usb attached scsi devices.
2 //
3 // only usb 2.0 for now.
4 //
5 // once we have xhci driver with usb 3.0 support this must
6 // be updated to use usb3 streams so booting from usb3
7 // devices actually works.
8 //
9 // Authors:
10 //  Gerd Hoffmann <kraxel@redhat.com>
11 //
12 // based on usb-msc.c which is written by:
13 //  Kevin O'Connor <kevin@koconnor.net>
14 //
15 // This file may be distributed under the terms of the GNU LGPLv3 license.
16 
17 #include "biosvar.h" // GET_GLOBALFLAT
18 #include "block.h" // DTYPE_USB
19 #include "blockcmd.h" // cdb_read
20 #include "config.h" // CONFIG_USB_UAS
21 #include "malloc.h" // free
22 #include "output.h" // dprintf
23 #include "std/disk.h" // DISK_RET_SUCCESS
24 #include "string.h" // memset
25 #include "usb.h" // struct usb_s
26 #include "usb-uas.h" // usb_uas_init
27 #include "util.h" // bootprio_find_usb
28 
29 #define UAS_UI_COMMAND              0x01
30 #define UAS_UI_SENSE                0x03
31 #define UAS_UI_RESPONSE             0x04
32 #define UAS_UI_TASK_MGMT            0x05
33 #define UAS_UI_READ_READY           0x06
34 #define UAS_UI_WRITE_READY          0x07
35 
36 #define UAS_PIPE_ID_COMMAND         0x01
37 #define UAS_PIPE_ID_STATUS          0x02
38 #define UAS_PIPE_ID_DATA_IN         0x03
39 #define UAS_PIPE_ID_DATA_OUT        0x04
40 
41 typedef struct {
42     u8    id;
43     u8    reserved;
44     u16   tag;
45 } PACKED  uas_ui_header;
46 
47 typedef struct {
48     u8    prio_taskattr;   /* 6:3 priority, 2:0 task attribute   */
49     u8    reserved_1;
50     u8    add_cdb_length;  /* 7:2 additional adb length (dwords) */
51     u8    reserved_2;
52     u8    lun[8];
53     u8    cdb[16];
54     u8    add_cdb[];
55 } PACKED  uas_ui_command;
56 
57 typedef struct {
58     u16   status_qualifier;
59     u8    status;
60     u8    reserved[7];
61     u16   sense_length;
62     u8    sense_data[18];
63 } PACKED  uas_ui_sense;
64 
65 typedef struct {
66     u16   add_response_info;
67     u8    response_code;
68 } PACKED  uas_ui_response;
69 
70 typedef struct {
71     u8    function;
72     u8    reserved;
73     u16   task_tag;
74     u8    lun[8];
75 } PACKED  uas_ui_task_mgmt;
76 
77 typedef struct {
78     uas_ui_header  hdr;
79     union {
80         uas_ui_command   command;
81         uas_ui_sense     sense;
82         uas_ui_task_mgmt task;
83         uas_ui_response  response;
84     };
85 } PACKED  uas_ui;
86 
87 struct uasdrive_s {
88     struct drive_s drive;
89     struct usbdevice_s *usbdev;
90     struct usb_pipe *command, *status, *data_in, *data_out;
91     u32 lun;
92 };
93 
94 int
uas_process_op(struct disk_op_s * op)95 uas_process_op(struct disk_op_s *op)
96 {
97     if (!CONFIG_USB_UAS)
98         return DISK_RET_EBADTRACK;
99 
100     struct uasdrive_s *drive_gf = container_of(
101         op->drive_fl, struct uasdrive_s, drive);
102 
103     uas_ui ui;
104     memset(&ui, 0, sizeof(ui));
105     ui.hdr.id = UAS_UI_COMMAND;
106     ui.hdr.tag = 0xdead;
107     ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun);
108     int blocksize = scsi_fill_cmd(op, ui.command.cdb, sizeof(ui.command.cdb));
109     if (blocksize < 0)
110         return default_process_op(op);
111     int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command),
112                             USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui),
113                             sizeof(ui.hdr) + sizeof(ui.command));
114     if (ret) {
115         dprintf(1, "uas: command send fail");
116         goto fail;
117     }
118 
119     memset(&ui, 0xff, sizeof(ui));
120     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
121                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
122     if (ret) {
123         dprintf(1, "uas: status recv fail");
124         goto fail;
125     }
126 
127     switch (ui.hdr.id) {
128     case UAS_UI_SENSE:
129         goto have_sense;
130     case UAS_UI_READ_READY:
131         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in),
132                             USB_DIR_IN, op->buf_fl, op->count * blocksize);
133         if (ret) {
134             dprintf(1, "uas: data read fail");
135             goto fail;
136         }
137         break;
138     case UAS_UI_WRITE_READY:
139         ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out),
140                             USB_DIR_OUT, op->buf_fl, op->count * blocksize);
141         if (ret) {
142             dprintf(1, "uas: data write fail");
143             goto fail;
144         }
145         break;
146     default:
147         dprintf(1, "uas: unknown status ui id %d", ui.hdr.id);
148         goto fail;
149     }
150 
151     memset(&ui, 0xff, sizeof(ui));
152     ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status),
153                         USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui));
154     if (ret) {
155         dprintf(1, "uas: status recv fail");
156         goto fail;
157     }
158     if (ui.hdr.id != UAS_UI_SENSE) {
159         dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id);
160         goto fail;
161     }
162 
163 have_sense:
164     if (ui.sense.status == 0) {
165         return DISK_RET_SUCCESS;
166     }
167 
168 fail:
169     return DISK_RET_EBADTRACK;
170 }
171 
172 static void
uas_init_lun(struct uasdrive_s * drive,struct usbdevice_s * usbdev,struct usb_pipe * command,struct usb_pipe * status,struct usb_pipe * data_in,struct usb_pipe * data_out,u32 lun)173 uas_init_lun(struct uasdrive_s *drive, struct usbdevice_s *usbdev,
174              struct usb_pipe *command, struct usb_pipe *status,
175              struct usb_pipe *data_in, struct usb_pipe *data_out,
176              u32 lun)
177 {
178     memset(drive, 0, sizeof(*drive));
179     if (usb_32bit_pipe(data_in))
180         drive->drive.type = DTYPE_UAS_32;
181     else
182         drive->drive.type = DTYPE_UAS;
183     drive->usbdev = usbdev;
184     drive->command = command;
185     drive->status = status;
186     drive->data_in = data_in;
187     drive->data_out = data_out;
188     drive->lun = lun;
189 }
190 
191 static int
uas_add_lun(u32 lun,struct drive_s * tmpl_drv)192 uas_add_lun(u32 lun, struct drive_s *tmpl_drv)
193 {
194     struct uasdrive_s *tmpl_lun =
195         container_of(tmpl_drv, struct uasdrive_s, drive);
196     struct uasdrive_s *drive = malloc_fseg(sizeof(*drive));
197     if (!drive) {
198         warn_noalloc();
199         return -1;
200     }
201     uas_init_lun(drive, tmpl_lun->usbdev,
202                  tmpl_lun->command, tmpl_lun->status,
203                  tmpl_lun->data_in, tmpl_lun->data_out,
204                  lun);
205 
206     int prio = bootprio_find_usb(drive->usbdev, drive->lun);
207     int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio);
208     if (ret) {
209         free(drive);
210         return -1;
211     }
212     return 0;
213 }
214 
215 int
usb_uas_setup(struct usbdevice_s * usbdev)216 usb_uas_setup(struct usbdevice_s *usbdev)
217 {
218     if (!CONFIG_USB_UAS)
219         return -1;
220 
221     // Verify right kind of device
222     struct usb_interface_descriptor *iface = usbdev->iface;
223     if (iface->bInterfaceSubClass != US_SC_SCSI ||
224         iface->bInterfaceProtocol != US_PR_UAS) {
225         dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n"
226                 , iface->bInterfaceSubClass, iface->bInterfaceProtocol);
227         return -1;
228     }
229 
230     /* find & allocate pipes */
231     struct usb_endpoint_descriptor *ep = NULL;
232     struct usb_pipe *command = NULL;
233     struct usb_pipe *status = NULL;
234     struct usb_pipe *data_in = NULL;
235     struct usb_pipe *data_out = NULL;
236     u8 *desc = (u8*)iface;
237     while (desc) {
238         desc += desc[0];
239         switch (desc[1]) {
240         case USB_DT_ENDPOINT:
241             ep = (void*)desc;
242             break;
243         case USB_DT_ENDPOINT_COMPANION:
244             /* No support (yet) for usb3 streams */
245             dprintf(1, "Superspeed UAS devices not supported (yet)\n");
246             goto fail;
247         case 0x24:
248             switch (desc[2]) {
249             case UAS_PIPE_ID_COMMAND:
250                 command = usb_alloc_pipe(usbdev, ep);
251                 break;
252             case UAS_PIPE_ID_STATUS:
253                 status = usb_alloc_pipe(usbdev, ep);
254                 break;
255             case UAS_PIPE_ID_DATA_IN:
256                 data_in = usb_alloc_pipe(usbdev, ep);
257                 break;
258             case UAS_PIPE_ID_DATA_OUT:
259                 data_out = usb_alloc_pipe(usbdev, ep);
260                 break;
261             default:
262                 goto fail;
263             }
264             break;
265         default:
266             desc = NULL;
267             break;
268         }
269     }
270     if (!command || !status || !data_in || !data_out)
271         goto fail;
272 
273     struct uasdrive_s lun0;
274     uas_init_lun(&lun0, usbdev, command, status, data_in, data_out, 0);
275     int ret = scsi_rep_luns_scan(&lun0.drive, uas_add_lun);
276     if (ret <= 0) {
277         dprintf(1, "Unable to configure UAS drive.\n");
278         goto fail;
279     }
280 
281     return 0;
282 
283 fail:
284     usb_free_pipe(usbdev, command);
285     usb_free_pipe(usbdev, status);
286     usb_free_pipe(usbdev, data_in);
287     usb_free_pipe(usbdev, data_out);
288     return -1;
289 }
290