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

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

Variable name changes.

File size: 16.9 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-2018 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 && !pi->devs[d].ignored)
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
152 || !ad_infos[a].ports[p].devs[d].present
153 || ad_infos[a].ports[p].devs[d].ignored)
154 {
155 return(RPDONE | RPERR_UNIT);
156 }
157 if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
158 req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd))
159 {
160 return(RPDONE | RPERR_PARAMETER);
161 }
162
163 /* allocate IOCTL context data */
164 if ((ic = MemAlloc(sizeof(*ic))) == NULL)
165 {
166 return(RPDONE | RPERR_GENERAL);
167 }
168 memset(ic, 0x00, sizeof(*ic));
169
170 /* lock DMA transfer buffer into memory and construct S/G list */
171 if (req->buflen > 0)
172 {
173 if (KernVMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
174 req->buf, req->buflen, &ic->lh, (KernPageList_t*)&ic->sg_lst, &ic->sg_cnt) != 0)
175 {
176 /* couldn't lock buffer and/or produce a S/G list */
177 MemFree(ic);
178 return(RPDONE | RPERR_PARAMETER);
179 }
180 }
181
182 /* fill in adapter passthrough fields */
183 ic->iorb.iorbh.Length = sizeof(ic->iorb);
184 ic->iorb.iorbh.UnitHandle = iorb_unit(a, p, d);
185 ic->iorb.iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
186 ic->iorb.iorbh.CommandModifier = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB : IOCM_EXECUTE_ATA;
187 ic->iorb.iorbh.RequestControl = IORB_ASYNC_POST;
188 ic->iorb.iorbh.Timeout = req->timeout;
189 ic->iorb.iorbh.f16NotifyAddress = Far16AdrOfIoctlWakeup16;
190
191 ic->iorb.cSGList = ic->sg_cnt;
192 ic->iorb.f16SGList = MemFar16Adr(ic->sg_lst);
193 ic->iorb.ppSGLIST = MemPhysAdr(ic->sg_lst);
194
195 memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
196 ic->iorb.ControllerCmdLen = req->cmdlen;
197 ic->iorb.f16ControllerCmd = MemFar16Adr(ic->cmd);
198 ic->iorb.Flags = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
199
200 if (req->sense_len > 0)
201 {
202 /* initialize SCSI status block to allow getting sense data */
203 ic->iorb.iorbh.pStatusBlock = CastFar16ToULONG(MemFar16Adr(&ic->ssb));
204 ic->iorb.iorbh.StatusBlockLen = sizeof(ic->ssb);
205 ic->ssb.f16SenseData = MemFar16Adr(ic->sense);
206 ic->ssb.ReqSenseLen = sizeof(ic->sense);
207 ic->iorb.iorbh.RequestControl |= IORB_REQ_STATUSBLOCK;
208 }
209
210 /* send IORB on its way */
211 add_entry(MemFar16Adr(&ic->iorb.iorbh));
212
213 /* Wait for IORB completion. */
214
215 //DAZ 20171206 spin_lock(drv_lock);
216 while (!(ic->iorb.iorbh.Status & IORB_DONE))
217 {
218 KernBlock(CastFar16ToULONG(MemFar16Adr(&ic->iorb.iorbh)), 30000, 0, NULL, NULL);
219 }
220 //DAZ 20171206 spin_unlock(drv_lock);
221
222 ret = RPDONE;
223
224 /* map IORB error codes to device driver error codes */
225 if (ic->iorb.iorbh.Status & IORB_ERROR)
226 {
227 ret |= RPERR;
228
229 switch (ic->iorb.iorbh.ErrorCode)
230 {
231 case IOERR_UNIT_NOT_READY:
232 ret |= RPERR_NOTREADY;
233 break;
234
235 case IOERR_MEDIA_CHANGED:
236 ret |= RPERR_DISK;
237 break;
238
239 case IOERR_MEDIA:
240 case IOERR_MEDIA_NOT_FORMATTED:
241 ret |= RPERR_CRC;
242 break;
243
244 case IOERR_CMD_SYNTAX:
245 case IOERR_CMD_NOT_SUPPORTED:
246 ret |= RPERR_BADCOMMAND;
247 break;
248
249 case IOERR_MEDIA_WRITE_PROTECT:
250 ret |= RPERR_PROTECT;
251 break;
252
253 case IOERR_CMD_ABORTED:
254 ret |= RPERR_INTERRUPTED;
255 break;
256
257 case IOERR_RBA_ADDRESSING_ERROR:
258 ret |= RPERR_SEEK;
259 break;
260
261 case IOERR_RBA_LIMIT:
262 ret |= RPERR_SECTOR;
263 break;
264
265 case IOERR_CMD_SGLIST_BAD:
266 ret |= RPERR_PARAMETER;
267 break;
268
269 case IOERR_DEVICE_NONSPECIFIC:
270 case IOERR_ADAPTER_TIMEOUT:
271 case IOERR_ADAPTER_DEVICEBUSCHECK:
272 case IOERR_CMD_ADD_SOFTWARE_FAILURE:
273 case IOERR_CMD_SW_RESOURCE:
274 default:
275 ret |= RPERR_GENERAL;
276 break;
277 }
278
279 /* copy sense information, if there is any */
280 if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
281 (ic->ssb.Flags | STATUS_SENSEDATA_VALID))
282 {
283 memcpy(sense_buf, ic->sense, min(ic->ssb.ReqSenseLen, req->sense_len));
284 }
285 }
286 else if ((req->flags & PT_ATAPI) == 0)
287 {
288 /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
289 * registers which are sometimes used to indicate return conditions,
290 * e.g. when requesting the smart status)
291 */
292 memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
293 }
294
295 MemFree(ic);
296 if (req->buflen > 0) KernVMUnlock(&ic->lh);
297 return(ret);
298}
299
300/******************************************************************************
301 * Generic disk IOCTL handler; this IOCTL category has originally been defined
302 * in IBM1S506; the code has been more or less copied from DANIS506.
303 *
304 * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
305 * point, basically those calls required to get HDMON working.
306 */
307USHORT ioctl_gen_dsk(REQPACKET *ioctl)
308{
309 DSKSP_CommandParameters *cp = (DSKSP_CommandParameters*)Far16ToFlat(ioctl->ioctl.pvParm);
310 UnitInformationData *ui;
311 OS2AHCI_PASSTHROUGH pt;
312 REQPACKET tmp_ioctl;
313 USHORT size = 0;
314 USHORT ret;
315 USHORT a;
316 USHORT p;
317 USHORT d;
318 UCHAR unit;
319
320 unit = cp->byPhysicalUnit;
321
322 /* verify addressability of data buffer (depends on function code) */
323 switch (ioctl->ioctl.bFunction)
324 {
325 case DSKSP_GEN_GET_COUNTERS:
326 size = sizeof(DeviceCountersData);
327 break;
328
329 case DSKSP_GET_UNIT_INFORMATION:
330 size = sizeof(UnitInformationData);
331 break;
332
333 case DSKSP_GET_INQUIRY_DATA:
334 size = ATA_ID_WORDS * sizeof(u16);
335 break;
336 }
337
338 if (map_unit(unit, &a, &p, &d))
339 {
340 return(RPDONE | RPERR | RPERR_UNIT);
341 }
342
343 /* execute generic disk request */
344 switch (ioctl->ioctl.bFunction)
345 {
346 case DSKSP_GEN_GET_COUNTERS:
347 /* Not supported, yet; we would need dynamically allocated device
348 * structures to cope with the memory requirements of the corresponding
349 * statistics buffer. For the time being, we'll return an empty buffer.
350 */
351 memset(Far16ToFlat(ioctl->ioctl.pvData), 0x00, sizeof(DeviceCountersData));
352 ret = RPDONE;
353 break;
354
355 case DSKSP_GET_UNIT_INFORMATION:
356 /* get unit information; things like port addresses won't fit so we don't
357 * even bother returning those.
358 */
359 ui = (UnitInformationData*)Far16ToFlat(ioctl->ioctl.pvData);
360 memset(ui, 0x00, sizeof(*ui));
361
362 ui->wRevisionNumber = 1;
363 ui->wIRQ = ad_infos[a].irq;
364 ui->wFlags = UIF_VALID;
365 ui->wFlags |= UIF_RUNNING_BMDMA;
366 ui->wFlags |= (unit & 0x0001) ? UIF_SLAVE : 0;
367 ui->wFlags |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
368 ui->wFlags |= UIF_SATA;
369
370 ret = RPDONE;
371 break;
372
373 case DSKSP_GET_INQUIRY_DATA:
374 /* return ATA ID buffer */
375 memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
376 tmp_ioctl.ioctl.bCategory = OS2AHCI_IOCTL_CATEGORY;
377 tmp_ioctl.ioctl.bFunction = OS2AHCI_IOCTL_PASSTHROUGH;
378 tmp_ioctl.ioctl.pvParm = FlatToFar16(&pt);
379
380 memset(&pt, 0x00, sizeof(pt));
381 pt.adapter = a;
382 pt.port = p;
383 pt.device = d;
384 pt.cmdlen = sizeof(pt.cmd.ata);
385 pt.cmd.ata.cmd = (ad_infos[a].ports[p].devs[d].atapi) ?
386 ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
387 pt.buflen = size;
388 pt.buf = Far16ToFlat(ioctl->ioctl.pvData);
389
390 ret = gen_ioctl(&tmp_ioctl);
391 break;
392
393 default:
394 ret = RPDONE | RPERR_BADCOMMAND;
395 break;
396 }
397
398 return(ret);
399}
400
401/******************************************************************************
402 * SMART IOCTL handler; this IOCTL category has originally been defined in
403 * IBM1S506; the code has been more or less copied from DANIS506.
404 */
405USHORT ioctl_smart(REQPACKET *ioctl)
406{
407 DSKSP_CommandParameters *cp = (DSKSP_CommandParameters *)Far16ToFlat(ioctl->ioctl.pvParm);
408 USHORT ret;
409 UCHAR unit;
410 UCHAR parm = 0;
411 void *pData = NULL;
412
413 unit = cp->byPhysicalUnit;
414
415 if (ioctl->ioctl.pvData)
416 {
417 pData = (void*)Far16ToFlat(ioctl->ioctl.pvData);
418 parm = *(UCHAR*)pData;
419 }
420
421 /* execute SMART request */
422 switch (ioctl->ioctl.bFunction)
423 {
424 case DSKSP_SMART_ONOFF:
425 ret = do_smart(unit, (BYTE) ((parm) ? ATA_SMART_ENABLE : ATA_SMART_DISABLE), 0, 0, NULL);
426 break;
427
428 case DSKSP_SMART_AUTOSAVE_ONOFF:
429 ret = do_smart(unit, ATA_SMART_AUTOSAVE, (BYTE) ((parm) ? (BYTE) 0xf1 : 0), 0, NULL);
430 break;
431
432 case DSKSP_SMART_AUTO_OFFLINE:
433 ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, 0, NULL);
434 break;
435
436 case DSKSP_SMART_EXEC_OFFLINE:
437 ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, 0, parm, NULL);
438 break;
439
440 case DSKSP_SMART_SAVE:
441 ret = do_smart(unit, ATA_SMART_SAVE, 0, 0, NULL);
442 break;
443
444 case DSKSP_SMART_GETSTATUS:
445 ret = do_smart(unit, ATA_SMART_STATUS, 0, 0, pData);
446 break;
447
448 case DSKSP_SMART_GET_ATTRIBUTES:
449 ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, 0, pData);
450 break;
451
452 case DSKSP_SMART_GET_THRESHOLDS:
453 ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, 0, pData);
454 break;
455
456 case DSKSP_SMART_GET_LOG:
457 ret = do_smart(unit, ATA_SMART_READ_LOG, 1, parm, pData);
458 break;
459
460 default:
461 ret = RPDONE | RPERR_BADCOMMAND;
462 }
463
464 return(ret);
465}
466
467/******************************************************************************
468 * Perform SMART request. The code has been more or less copied from DANIS506.
469 */
470static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE cnt, BYTE lba_l, void *buf)
471{
472 OS2AHCI_PASSTHROUGH pt;
473 REQPACKET ioctl;
474 USHORT ret;
475 USHORT a;
476 USHORT p;
477 USHORT d;
478
479 if (map_unit(unit, &a, &p, &d))
480 {
481 return(RPDONE | RPERR_UNIT);
482 }
483
484 /* Perform SMART request using the existing OS2AHCI_IOTCL_PASSTHROUGH IOCTL
485 * interface which already takes care of allocating an IORB, s/g lists, etc.
486 */
487 memset(&ioctl, 0x00, sizeof(ioctl));
488 ioctl.ioctl.bCategory = OS2AHCI_IOCTL_CATEGORY;
489 ioctl.ioctl.bFunction = OS2AHCI_IOCTL_PASSTHROUGH;
490 ioctl.ioctl.pvParm = FlatToFar16(&pt);
491
492 memset(&pt, 0x00, sizeof(pt));
493 pt.adapter = a;
494 pt.port = p;
495 pt.device = d;
496 pt.cmdlen = sizeof(pt.cmd.ata);
497 pt.cmd.ata.features = sub_func;
498 pt.cmd.ata.count = cnt;
499 pt.cmd.ata.lba_l = (0xc24fL << 8) | lba_l;
500 pt.cmd.ata.cmd = ATA_CMD_SMART;
501
502 if (buf != NULL && sub_func != ATA_SMART_STATUS)
503 {
504 pt.buflen = 512;
505 pt.buf = buf;
506 }
507
508 if (((ret = gen_ioctl(&ioctl)) & RPERR) == 0 && sub_func == ATA_SMART_STATUS)
509 {
510 /* ATA_SMART_STATUS doesn't transfer anything but instead relies on the
511 * returned D2H FIS, mapped to the ATA CMD, to have a certain value
512 * (0xf42c); the IOCTL result is expected to be returned as a ULONG in
513 * the data buffer.
514 */
515 if (((pt.cmd.ata.lba_l >> 8) & 0xffff) == 0xf42c)
516 {
517 *((ULONG *) buf) = 1;
518 } else {
519 *((ULONG *) buf) = 0;
520 }
521 }
522
523 return(ret);
524}
525
526/******************************************************************************
527 * Map DSKSP unit number to corresponding adapter/port/device number. Units
528 * are identified by an 8-bit adapter/device number with the lowest bit
529 * selecting between master (0) and slave (1). This number is mapped to our
530 * ATA/ATAPI units sequentially.
531 */
532static int map_unit(BYTE unit, USHORT *a, USHORT *p, USHORT *d)
533{
534 USHORT _a;
535 USHORT _p;
536 USHORT _d;
537
538 /* map unit to adapter/port/device */
539 for (_a = 0; _a < ad_info_cnt; _a++)
540 {
541 AD_INFO *ai = ad_infos + _a;
542
543 for (_p = 0; _p <= ai->port_max; _p++)
544 {
545 P_INFO *pi = ai->ports + _p;
546
547 for (_d = 0; _d <= pi->dev_max; _d++)
548 {
549 if (pi->devs[_d].present && !pi->devs[_d].ignored)
550 {
551 if (unit-- == 0)
552 {
553 /* found the device */
554 *a = _a;
555 *p = _p;
556 *d = _d;
557 return(0);
558 }
559 }
560 }
561 }
562 }
563
564 /* unit not found */
565 return(-1);
566}
567
568/******************************************************************************
569 * IORB notification routine; used to wake up the sleeping application thread
570 * when the IOCTL IORB is complete.
571 */
572IORBH *IoctlWakeup(ULONG ulArg)
573{
574 KernWakeup(ulArg, 0, NULL, 0);
575 return(NULL);
576}
577
Note: See TracBrowser for help on using the repository browser.