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

Last change on this file since 193 was 193, checked in by David Azarewicz, 8 years ago

Fixed IOCtl pass-thru interface.
Cosmetic changes to user interface.
Removed old IBM smartctl exe from distribution.

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