source: trunk/src/os2ahci/ioctl.c@ 206

Last change on this file since 206 was 206, checked in by David Azarewicz, 5 years ago

Reworked internal implementation of /U

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-2021 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.