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

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

Fixed ADD RM id.

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