source: trunk/src/os2ahci/ioctl.c

Last change on this file was 211, checked in by David Azarewicz, 2 years ago

Added workaround to help with VirtualBox issues.
Improved diagnostic messages.
Changed how timeouts are reset and how ctx hooks are triggered.
Added quirk for devices with issues executing some standard commands.
Changed to make /N the default.

File size: 16.4 KB
Line 
1/**
2 * ioctl.c - Generic IOCTL command processing
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 * Copyright (c) 2013-2023 David Azarewicz <david@88watts.net>
7 *
8 * Authors: Christian Mueller, Markus Thielen
9 *
10 * Parts copied from/inspired by the Linux AHCI driver;
11 * those parts are (c) Linux AHCI/ATA maintainers
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28#include "os2ahci.h"
29#include "atapi.h"
30#include "ata.h"
31#include "ioctl.h"
32
33#include <Dev32scsi.h>
34
35#pragma pack(1)
36
37/* Memory area for IOCTLs which send IORBs downstream; currently only
38 * OS2AHCI_IOCTL_PASSTHROUGH falls into this category, thus we're simply
39 * reusing the IORB_ADAPTER_PASSTHRU structure for now. If this ever changes,
40 * we'll need to define a union to cover all IORB types in question.
41 */
42#define CMDLEN sizeof(((OS2AHCI_PASSTHROUGH *) 0)->cmd)
43typedef struct {
44 IORB_ADAPTER_PASSTHRU iorb; /* IORB */
45 SCSI_STATUS_BLOCK ssb; /* SCSI status block */
46 UCHAR cmd[CMDLEN]; /* copy of passthrough cmd
47 * (need to fill ATA return
48 * registers at interrupt time)
49 */
50 UCHAR sense[ATAPI_SENSE_LEN]; /* sense buffer */
51 SCATGATENTRY sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
52 ULONG sg_cnt; /* number of S/G elements */
53 struct _KernVMLock_t lh; /* lock handle for KernVMLock() */
54} IOCTL_CONTEXT;
55
56static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE cnt, BYTE lba_l, void *buf);
57static int map_unit(BYTE unit, USHORT *a, USHORT *p, USHORT *d);
58
59extern IORBH FAR16DATA * (__far16 *Far16AdrOfIoctlWakeup16)(IORBH FAR16DATA*);
60
61/******************************************************************************
62 * Return device list to allow the ring 3 application to figure out which
63 * adapter/port/device combinations are available.
64 */
65USHORT ioctl_get_devlist(REQPACKET *ioctl)
66{
67 OS2AHCI_DEVLIST *devlst = (OS2AHCI_DEVLIST*)Far16ToFlat(ioctl->ioctl.pvData);
68 USHORT maxcnt = 0;
69 USHORT cnt = 0;
70 USHORT a;
71 USHORT p;
72 USHORT d;
73
74 if (KernCopyIn(&maxcnt, ioctl->ioctl.pvParm, sizeof(maxcnt)))
75 {
76 return(RPDONE | RPERR_PARAMETER);
77 }
78
79 /* fill-in device list */
80 for (a = 0; a < ad_info_cnt; a++)
81 {
82 AD_INFO *ai = ad_infos + a;
83
84 for (p = 0; p <= ai->port_max; p++)
85 {
86 P_INFO *pi = ai->ports + p;
87
88 for (d = 0; d <= pi->dev_max; d++)
89 {
90 if (pi->devs[d].present)
91 {
92 /* add this device to the device list */
93 if (cnt >= maxcnt)
94 {
95 /* not enough room in devlst */
96 goto ioctl_get_device_done;
97 }
98
99 devlst->devs[cnt].adapter = a;
100 devlst->devs[cnt].port = p;
101 devlst->devs[cnt].device = d;
102 devlst->devs[cnt].type = pi->devs[d].dev_type;
103 devlst->devs[cnt].ncq_max = pi->devs[d].ncq_max;
104
105 if (pi->devs[d].lba48) devlst->devs[cnt].flags |= DF_LBA48;
106 if (pi->devs[d].atapi) devlst->devs[cnt].flags |= DF_ATAPI;
107 if (pi->devs[d].atapi_16) devlst->devs[cnt].flags |= DF_ATAPI_16;
108 if (pi->devs[d].removable) devlst->devs[cnt].flags |= DF_REMOVABLE;
109 cnt++;
110 }
111 }
112 }
113 }
114
115ioctl_get_device_done:
116 devlst->cnt = cnt;
117 return(RPDONE);
118}
119
120/******************************************************************************
121 * Adapter passthrough IOCTL. This IOCTL covers both ATA and ATAPI passthrough
122 * requests.
123 */
124USHORT ioctl_passthrough(REQPACKET *ioctl)
125{
126 OS2AHCI_PASSTHROUGH *req = (OS2AHCI_PASSTHROUGH *)Far16ToFlat(ioctl->ioctl.pvParm);
127 char *sense_buf = (char *)Far16ToFlat(ioctl->ioctl.pvData);
128 IOCTL_CONTEXT *ic;
129 USHORT ret;
130 USHORT a;
131 USHORT p;
132 USHORT d;
133
134 /* Verify basic request parameters such as adapter/port/device, size of
135 * DMA buffer (the S/G list can't have more than AHCI_MAX_SG / 2 entries), ...
136 */
137 a = req->adapter;
138 p = req->port;
139 d = req->device;
140 if (a >= ad_info_cnt || p > ad_infos[a].port_max
141 || d > ad_infos[a].ports[p].dev_max
142 || !ad_infos[a].ports[p].devs[d].present)
143 {
144 return(RPDONE | RPERR_UNIT);
145 }
146 if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
147 req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd))
148 {
149 return(RPDONE | RPERR_PARAMETER);
150 }
151
152 /* allocate IOCTL context data */
153 if ((ic = MemAlloc(sizeof(*ic))) == NULL)
154 {
155 return(RPDONE | RPERR_GENERAL);
156 }
157 memset(ic, 0x00, sizeof(*ic));
158
159 /* lock DMA transfer buffer into memory and construct S/G list */
160 if (req->buflen > 0)
161 {
162 if (KernVMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
163 req->buf, req->buflen, &ic->lh, (KernPageList_t*)&ic->sg_lst, &ic->sg_cnt) != 0)
164 {
165 /* couldn't lock buffer and/or produce a S/G list */
166 MemFree(ic);
167 return(RPDONE | RPERR_PARAMETER);
168 }
169 }
170
171 /* fill in adapter passthrough fields */
172 ic->iorb.iorbh.Length = sizeof(ic->iorb);
173 ic->iorb.iorbh.UnitHandle = iorb_unit(a, p, d);
174 ic->iorb.iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
175 ic->iorb.iorbh.CommandModifier = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB : IOCM_EXECUTE_ATA;
176 ic->iorb.iorbh.RequestControl = IORB_ASYNC_POST;
177 ic->iorb.iorbh.Timeout = req->timeout;
178 ic->iorb.iorbh.f16NotifyAddress = Far16AdrOfIoctlWakeup16;
179
180 ic->iorb.cSGList = ic->sg_cnt;
181 ic->iorb.f16SGList = MemFar16Adr(ic->sg_lst);
182 ic->iorb.ppSGLIST = MemPhysAdr(ic->sg_lst);
183
184 memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
185 ic->iorb.ControllerCmdLen = req->cmdlen;
186 ic->iorb.f16ControllerCmd = MemFar16Adr(ic->cmd);
187 ic->iorb.Flags = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
188
189 if (req->sense_len > 0)
190 {
191 /* initialize SCSI status block to allow getting sense data */
192 ic->iorb.iorbh.pStatusBlock = CastFar16ToULONG(MemFar16Adr(&ic->ssb));
193 ic->iorb.iorbh.StatusBlockLen = sizeof(ic->ssb);
194 ic->ssb.f16SenseData = MemFar16Adr(ic->sense);
195 ic->ssb.ReqSenseLen = sizeof(ic->sense);
196 ic->iorb.iorbh.RequestControl |= IORB_REQ_STATUSBLOCK;
197 }
198
199 /* send IORB on its way */
200 add_entry(MemFar16Adr(&ic->iorb.iorbh));
201
202 /* Wait for IORB completion. */
203
204 //DAZ 20171206 spin_lock(drv_lock);
205 while (!(ic->iorb.iorbh.Status & IORB_DONE))
206 {
207 KernBlock(CastFar16ToULONG(MemFar16Adr(&ic->iorb.iorbh)), 30000, 0, NULL, NULL);
208 }
209 //DAZ 20171206 spin_unlock(drv_lock);
210
211 ret = RPDONE;
212
213 /* map IORB error codes to device driver error codes */
214 if (ic->iorb.iorbh.Status & IORB_ERROR)
215 {
216 ret |= RPERR;
217
218 switch (ic->iorb.iorbh.ErrorCode)
219 {
220 case IOERR_UNIT_NOT_READY:
221 ret |= RPERR_NOTREADY;
222 break;
223
224 case IOERR_MEDIA_CHANGED:
225 ret |= RPERR_DISK;
226 break;
227
228 case IOERR_MEDIA:
229 case IOERR_MEDIA_NOT_FORMATTED:
230 ret |= RPERR_CRC;
231 break;
232
233 case IOERR_CMD_SYNTAX:
234 case IOERR_CMD_NOT_SUPPORTED:
235 ret |= RPERR_BADCOMMAND;
236 break;
237
238 case IOERR_MEDIA_WRITE_PROTECT:
239 ret |= RPERR_PROTECT;
240 break;
241
242 case IOERR_CMD_ABORTED:
243 ret |= RPERR_INTERRUPTED;
244 break;
245
246 case IOERR_RBA_ADDRESSING_ERROR:
247 ret |= RPERR_SEEK;
248 break;
249
250 case IOERR_RBA_LIMIT:
251 ret |= RPERR_SECTOR;
252 break;
253
254 case IOERR_CMD_SGLIST_BAD:
255 ret |= RPERR_PARAMETER;
256 break;
257
258 case IOERR_DEVICE_NONSPECIFIC:
259 case IOERR_ADAPTER_TIMEOUT:
260 case IOERR_ADAPTER_DEVICEBUSCHECK:
261 case IOERR_CMD_ADD_SOFTWARE_FAILURE:
262 case IOERR_CMD_SW_RESOURCE:
263 default:
264 ret |= RPERR_GENERAL;
265 break;
266 }
267
268 /* copy sense information, if there is any */
269 if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
270 (ic->ssb.Flags | STATUS_SENSEDATA_VALID))
271 {
272 memcpy(sense_buf, ic->sense, min(ic->ssb.ReqSenseLen, req->sense_len));
273 }
274 }
275 else if ((req->flags & PT_ATAPI) == 0)
276 {
277 /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
278 * registers which are sometimes used to indicate return conditions,
279 * e.g. when requesting the smart status)
280 */
281 memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
282 }
283
284 MemFree(ic);
285 if (req->buflen > 0) KernVMUnlock(&ic->lh);
286 return(ret);
287}
288
289/******************************************************************************
290 * Generic disk IOCTL handler; this IOCTL category has originally been defined
291 * in IBM1S506; the code has been more or less copied from DANIS506.
292 *
293 * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
294 * point, basically those calls required to get HDMON working.
295 */
296USHORT ioctl_gen_dsk(REQPACKET *ioctl)
297{
298 DSKSP_CommandParameters *cp = (DSKSP_CommandParameters*)Far16ToFlat(ioctl->ioctl.pvParm);
299 UnitInformationData *ui;
300 OS2AHCI_PASSTHROUGH pt;
301 REQPACKET tmp_ioctl;
302 USHORT size = 0;
303 USHORT ret;
304 USHORT a;
305 USHORT p;
306 USHORT d;
307 UCHAR unit;
308
309 unit = cp->byPhysicalUnit;
310
311 /* verify addressability of data buffer (depends on function code) */
312 switch (ioctl->ioctl.bFunction)
313 {
314 case DSKSP_GEN_GET_COUNTERS:
315 size = sizeof(DeviceCountersData);
316 break;
317
318 case DSKSP_GET_UNIT_INFORMATION:
319 size = sizeof(UnitInformationData);
320 break;
321
322 case DSKSP_GET_INQUIRY_DATA:
323 size = ATA_ID_WORDS * sizeof(u16);
324 break;
325 }
326
327 if (map_unit(unit, &a, &p, &d))
328 {
329 return(RPDONE | RPERR | RPERR_UNIT);
330 }
331
332 /* execute generic disk request */
333 switch (ioctl->ioctl.bFunction)
334 {
335 case DSKSP_GEN_GET_COUNTERS:
336 /* Not supported, yet; we would need dynamically allocated device
337 * structures to cope with the memory requirements of the corresponding
338 * statistics buffer. For the time being, we'll return an empty buffer.
339 */
340 memset(Far16ToFlat(ioctl->ioctl.pvData), 0x00, sizeof(DeviceCountersData));
341 ret = RPDONE;
342 break;
343
344 case DSKSP_GET_UNIT_INFORMATION:
345 /* get unit information; things like port addresses won't fit so we don't
346 * even bother returning those.
347 */
348 ui = (UnitInformationData*)Far16ToFlat(ioctl->ioctl.pvData);
349 memset(ui, 0x00, sizeof(*ui));
350
351 ui->wRevisionNumber = 1;
352 ui->wIRQ = ad_infos[a].irq;
353 ui->wFlags = UIF_VALID;
354 ui->wFlags |= UIF_RUNNING_BMDMA;
355 ui->wFlags |= (unit & 0x0001) ? UIF_SLAVE : 0;
356 ui->wFlags |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
357 ui->wFlags |= UIF_SATA;
358
359 ret = RPDONE;
360 break;
361
362 case DSKSP_GET_INQUIRY_DATA:
363 /* return ATA ID buffer */
364 memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
365 tmp_ioctl.ioctl.bCategory = OS2AHCI_IOCTL_CATEGORY;
366 tmp_ioctl.ioctl.bFunction = OS2AHCI_IOCTL_PASSTHROUGH;
367 tmp_ioctl.ioctl.pvParm = FlatToFar16(&pt);
368
369 memset(&pt, 0x00, sizeof(pt));
370 pt.adapter = a;
371 pt.port = p;
372 pt.device = d;
373 pt.cmdlen = sizeof(pt.cmd.ata);
374 pt.cmd.ata.cmd = (ad_infos[a].ports[p].devs[d].atapi) ?
375 ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
376 pt.buflen = size;
377 pt.buf = Far16ToFlat(ioctl->ioctl.pvData);
378
379 ret = gen_ioctl(&tmp_ioctl);
380 break;
381
382 default:
383 ret = RPDONE | RPERR_BADCOMMAND;
384 break;
385 }
386
387 return(ret);
388}
389
390/******************************************************************************
391 * SMART IOCTL handler; this IOCTL category has originally been defined in
392 * IBM1S506; the code has been more or less copied from DANIS506.
393 */
394USHORT ioctl_smart(REQPACKET *ioctl)
395{
396 DSKSP_CommandParameters *cp = (DSKSP_CommandParameters *)Far16ToFlat(ioctl->ioctl.pvParm);
397 USHORT ret;
398 UCHAR unit;
399 UCHAR parm = 0;
400 void *pData = NULL;
401
402 unit = cp->byPhysicalUnit;
403
404 if (ioctl->ioctl.pvData)
405 {
406 pData = (void*)Far16ToFlat(ioctl->ioctl.pvData);
407 parm = *(UCHAR*)pData;
408 }
409
410 /* execute SMART request */
411 switch (ioctl->ioctl.bFunction)
412 {
413 case DSKSP_SMART_ONOFF:
414 ret = do_smart(unit, (BYTE) ((parm) ? ATA_SMART_ENABLE : ATA_SMART_DISABLE), 0, 0, NULL);
415 break;
416
417 case DSKSP_SMART_AUTOSAVE_ONOFF:
418 ret = do_smart(unit, ATA_SMART_AUTOSAVE, (BYTE) ((parm) ? (BYTE) 0xf1 : 0), 0, NULL);
419 break;
420
421 case DSKSP_SMART_AUTO_OFFLINE:
422 ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, 0, NULL);
423 break;
424
425 case DSKSP_SMART_EXEC_OFFLINE:
426 ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, 0, parm, NULL);
427 break;
428
429 case DSKSP_SMART_SAVE:
430 ret = do_smart(unit, ATA_SMART_SAVE, 0, 0, NULL);
431 break;
432
433 case DSKSP_SMART_GETSTATUS:
434 ret = do_smart(unit, ATA_SMART_STATUS, 0, 0, pData);
435 break;
436
437 case DSKSP_SMART_GET_ATTRIBUTES:
438 ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, 0, pData);
439 break;
440
441 case DSKSP_SMART_GET_THRESHOLDS:
442 ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, 0, pData);
443 break;
444
445 case DSKSP_SMART_GET_LOG:
446 ret = do_smart(unit, ATA_SMART_READ_LOG, 1, parm, pData);
447 break;
448
449 default:
450 ret = RPDONE | RPERR_BADCOMMAND;
451 }
452
453 return(ret);
454}
455
456/******************************************************************************
457 * Perform SMART request. The code has been more or less copied from DANIS506.
458 */
459static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE cnt, BYTE lba_l, void *buf)
460{
461 OS2AHCI_PASSTHROUGH pt;
462 REQPACKET ioctl;
463 USHORT ret;
464 USHORT a;
465 USHORT p;
466 USHORT d;
467
468 if (map_unit(unit, &a, &p, &d))
469 {
470 return(RPDONE | RPERR_UNIT);
471 }
472
473 /* Perform SMART request using the existing OS2AHCI_IOTCL_PASSTHROUGH IOCTL
474 * interface which already takes care of allocating an IORB, s/g lists, etc.
475 */
476 memset(&ioctl, 0x00, sizeof(ioctl));
477 ioctl.ioctl.bCategory = OS2AHCI_IOCTL_CATEGORY;
478 ioctl.ioctl.bFunction = OS2AHCI_IOCTL_PASSTHROUGH;
479 ioctl.ioctl.pvParm = FlatToFar16(&pt);
480
481 memset(&pt, 0x00, sizeof(pt));
482 pt.adapter = a;
483 pt.port = p;
484 pt.device = d;
485 pt.cmdlen = sizeof(pt.cmd.ata);
486 pt.cmd.ata.features = sub_func;
487 pt.cmd.ata.count = cnt;
488 pt.cmd.ata.lba_l = (0xc24fL << 8) | lba_l;
489 pt.cmd.ata.cmd = ATA_CMD_SMART;
490
491 if (buf != NULL && sub_func != ATA_SMART_STATUS)
492 {
493 pt.buflen = 512;
494 pt.buf = buf;
495 }
496
497 if (((ret = gen_ioctl(&ioctl)) & RPERR) == 0 && sub_func == ATA_SMART_STATUS)
498 {
499 /* ATA_SMART_STATUS doesn't transfer anything but instead relies on the
500 * returned D2H FIS, mapped to the ATA CMD, to have a certain value
501 * (0xf42c); the IOCTL result is expected to be returned as a ULONG in
502 * the data buffer.
503 */
504 if (((pt.cmd.ata.lba_l >> 8) & 0xffff) == 0xf42c)
505 {
506 *((ULONG *) buf) = 1;
507 } else {
508 *((ULONG *) buf) = 0;
509 }
510 }
511
512 return(ret);
513}
514
515/******************************************************************************
516 * Map DSKSP unit number to corresponding adapter/port/device number. Units
517 * are identified by an 8-bit adapter/device number with the lowest bit
518 * selecting between master (0) and slave (1). This number is mapped to our
519 * ATA/ATAPI units sequentially.
520 */
521static int map_unit(BYTE unit, USHORT *a, USHORT *p, USHORT *d)
522{
523 USHORT _a;
524 USHORT _p;
525 USHORT _d;
526
527 /* map unit to adapter/port/device */
528 for (_a = 0; _a < ad_info_cnt; _a++)
529 {
530 AD_INFO *ai = ad_infos + _a;
531
532 for (_p = 0; _p <= ai->port_max; _p++)
533 {
534 P_INFO *pi = ai->ports + _p;
535
536 for (_d = 0; _d <= pi->dev_max; _d++)
537 {
538 if (pi->devs[_d].present)
539 {
540 if (unit-- == 0)
541 {
542 /* found the device */
543 *a = _a;
544 *p = _p;
545 *d = _d;
546 return(0);
547 }
548 }
549 }
550 }
551 }
552
553 /* unit not found */
554 return(-1);
555}
556
557/******************************************************************************
558 * IORB notification routine; used to wake up the sleeping application thread
559 * when the IOCTL IORB is complete.
560 */
561IORBH *IoctlWakeup(ULONG ulArg)
562{
563 KernWakeup(ulArg, 0, NULL, 0);
564 return(NULL);
565}
566
Note: See TracBrowser for help on using the repository browser.