1 /*
2 * Copyright 2021 David Sebek, dasebek@gmail.com
3 * Copyright 2008-2013 Axel Dörfler, axeld@pinc-software.de
4 * Copyright 2002/03 Thomas Kurschel
5 * All rights reserved. Distributed under the terms of the MIT License.
6 */
7
8
9 /*! Peripheral driver to handle any kind of SCSI disks,
10 i.e. hard disk and floopy disks (ZIP etc.)
11
12 Much work is done by scsi_periph and block_io.
13
14 You'll find das_... all over the place. This stands for
15 "Direct Access Storage" which is the official SCSI name for
16 normal (floppy/hard/ZIP)-disk drives.
17 */
18
19
20 #include "scsi_disk.h"
21
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <AutoDeleter.h>
26
27 #include <fs/devfs.h>
28 #include <util/fs_trim_support.h>
29
30 #include "dma_resources.h"
31 #include "IORequest.h"
32 #include "IOSchedulerSimple.h"
33
34
35 //#define TRACE_SCSI_DISK
36 #ifdef TRACE_SCSI_DISK
37 # define TRACE(x...) dprintf("scsi_disk: " x)
38 #else
39 # define TRACE(x...) ;
40 #endif
41
42
43 static const uint8 kDriveIcon[] = {
44 0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
45 0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
46 0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
47 0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
48 0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
49 0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
50 0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
51 0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
52 0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
53 0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
54 0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
55 0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
56 0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
57 0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
58 0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
59 0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
60 0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
61 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
62 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
63 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
64 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
65 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
66 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
67 };
68
69
70 static scsi_periph_interface* sSCSIPeripheral;
71 static device_manager_info* sDeviceManager;
72
73
74 static status_t
update_capacity(das_driver_info * device)75 update_capacity(das_driver_info* device)
76 {
77 TRACE("update_capacity()\n");
78
79 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
80 if (ccb == NULL)
81 return B_NO_MEMORY;
82
83 status_t status = sSCSIPeripheral->check_capacity(
84 device->scsi_periph_device, ccb);
85
86 device->scsi->free_ccb(ccb);
87
88 return status;
89 }
90
91
92 static status_t
get_geometry(das_handle * handle,device_geometry * geometry)93 get_geometry(das_handle* handle, device_geometry* geometry)
94 {
95 das_driver_info* info = handle->info;
96
97 status_t status = update_capacity(info);
98 if (status != B_OK)
99 return status;
100
101 devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
102 geometry->bytes_per_physical_sector = info->physical_block_size;
103
104 geometry->device_type = B_DISK;
105 geometry->removable = info->removable;
106
107 // TBD: for all but CD-ROMs, read mode sense - medium type
108 // (bit 7 of block device specific parameter for Optical Memory Block Device)
109 // (same for Direct-Access Block Devices)
110 // (same for write-once block devices)
111 // (same for optical memory block devices)
112 geometry->read_only = false;
113 geometry->write_once = false;
114
115 TRACE("scsi_disk: get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32
116 ", %" B_PRId32 ", %d, %d, %d, %d, %" B_PRId32 "\n", geometry->bytes_per_sector,
117 geometry->sectors_per_track, geometry->cylinder_count,
118 geometry->head_count, geometry->device_type,
119 geometry->removable, geometry->read_only, geometry->write_once,
120 geometry->bytes_per_physical_sector);
121
122 return B_OK;
123 }
124
125
126 static status_t
load_eject(das_driver_info * device,bool load)127 load_eject(das_driver_info *device, bool load)
128 {
129 TRACE("load_eject()\n");
130
131 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
132 if (ccb == NULL)
133 return B_NO_MEMORY;
134
135 err_res result = sSCSIPeripheral->send_start_stop(
136 device->scsi_periph_device, ccb, load, true);
137
138 device->scsi->free_ccb(ccb);
139
140 return result.error_code;
141 }
142
143
144 static status_t
synchronize_cache(das_driver_info * device)145 synchronize_cache(das_driver_info *device)
146 {
147 TRACE("synchronize_cache()\n");
148
149 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
150 if (ccb == NULL)
151 return B_NO_MEMORY;
152
153 err_res result = sSCSIPeripheral->synchronize_cache(
154 device->scsi_periph_device, ccb);
155
156 device->scsi->free_ccb(ccb);
157
158 return result.error_code;
159 }
160
161
162 static status_t
trim_device(das_driver_info * device,fs_trim_data * trimData)163 trim_device(das_driver_info* device, fs_trim_data* trimData)
164 {
165 TRACE("trim_device()\n");
166
167 trimData->trimmed_size = 0;
168
169 scsi_ccb* request = device->scsi->alloc_ccb(device->scsi_device);
170 if (request == NULL)
171 return B_NO_MEMORY;
172
173 scsi_block_range* blockRanges = (scsi_block_range*)
174 malloc(trimData->range_count * sizeof(*blockRanges));
175 if (blockRanges == NULL)
176 return B_NO_MEMORY;
177
178 MemoryDeleter deleter(blockRanges);
179
180 for (uint32 i = 0; i < trimData->range_count; i++) {
181 uint64 startBytes = trimData->ranges[i].offset;
182 uint64 sizeBytes = trimData->ranges[i].size;
183 uint32 blockSize = device->block_size;
184
185 // Align to a block boundary so we don't discard blocks
186 // that could also contain some other data
187 uint64 blockOffset = startBytes % blockSize;
188 if (blockOffset == 0) {
189 blockRanges[i].lba = startBytes / blockSize;
190 blockRanges[i].size = sizeBytes / blockSize;
191 } else {
192 blockRanges[i].lba = startBytes / blockSize + 1;
193 blockRanges[i].size = (sizeBytes - (blockSize - blockOffset))
194 / blockSize;
195 }
196 }
197
198 // Check ranges against device capacity and make them fit
199 for (uint32 i = 0; i < trimData->range_count; i++) {
200 if (blockRanges[i].lba >= device->capacity) {
201 dprintf("trim_device(): range offset (LBA) %" B_PRIu64
202 " exceeds device capacity %" B_PRIu64 "\n",
203 blockRanges[i].lba, device->capacity);
204 return B_BAD_VALUE;
205 }
206 uint64 maxSize = device->capacity - blockRanges[i].lba;
207 blockRanges[i].size = min_c(blockRanges[i].size, maxSize);
208 }
209
210 uint64 trimmedBlocks;
211 status_t status = sSCSIPeripheral->trim_device(device->scsi_periph_device,
212 request, blockRanges, trimData->range_count, &trimmedBlocks);
213
214 device->scsi->free_ccb(request);
215 // Some blocks may have been trimmed even if trim_device returns a failure
216 trimData->trimmed_size = trimmedBlocks * device->block_size;
217
218 return status;
219 }
220
221
222 static status_t
do_io(void * cookie,IOOperation * operation)223 do_io(void* cookie, IOOperation* operation)
224 {
225 das_driver_info* info = (das_driver_info*)cookie;
226
227 // TODO: this can go away as soon as we pushed the IOOperation to the upper
228 // layers - we can then set scsi_periph::io() as callback for the scheduler
229 size_t bytesTransferred;
230 status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
231 &bytesTransferred);
232
233 info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
234 return status;
235 }
236
237
238 // #pragma mark - device module API
239
240
241 static status_t
das_init_device(void * _info,void ** _cookie)242 das_init_device(void* _info, void** _cookie)
243 {
244 das_driver_info* info = (das_driver_info*)_info;
245
246 // and get (initial) capacity
247 scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device);
248 if (request == NULL)
249 return B_NO_MEMORY;
250
251 sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
252 info->scsi->free_ccb(request);
253
254 *_cookie = info;
255 return B_OK;
256 }
257
258
259 static void
das_uninit_device(void * _cookie)260 das_uninit_device(void* _cookie)
261 {
262 das_driver_info* info = (das_driver_info*)_cookie;
263
264 delete info->io_scheduler;
265 }
266
267
268 static status_t
das_open(void * _info,const char * path,int openMode,void ** _cookie)269 das_open(void* _info, const char* path, int openMode, void** _cookie)
270 {
271 das_driver_info* info = (das_driver_info*)_info;
272
273 das_handle* handle = (das_handle*)malloc(sizeof(das_handle));
274 if (handle == NULL)
275 return B_NO_MEMORY;
276
277 handle->info = info;
278
279 status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
280 (periph_handle_cookie)handle, &handle->scsi_periph_handle);
281 if (status < B_OK) {
282 free(handle);
283 return status;
284 }
285
286 *_cookie = handle;
287 return B_OK;
288 }
289
290
291 static status_t
das_close(void * cookie)292 das_close(void* cookie)
293 {
294 das_handle* handle = (das_handle*)cookie;
295 TRACE("close()\n");
296
297 sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
298 return B_OK;
299 }
300
301
302 static status_t
das_free(void * cookie)303 das_free(void* cookie)
304 {
305 das_handle* handle = (das_handle*)cookie;
306 TRACE("free()\n");
307
308 sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
309 free(handle);
310 return B_OK;
311 }
312
313
314 static status_t
das_io(void * cookie,io_request * request)315 das_io(void *cookie, io_request *request)
316 {
317 das_handle* handle = (das_handle*)cookie;
318
319 return handle->info->io_scheduler->ScheduleRequest(request);
320 }
321
322
323 static status_t
das_ioctl(void * cookie,uint32 op,void * buffer,size_t length)324 das_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
325 {
326 das_handle* handle = (das_handle*)cookie;
327 das_driver_info* info = handle->info;
328
329 TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
330
331 switch (op) {
332 case B_GET_DEVICE_SIZE:
333 {
334 status_t status = update_capacity(info);
335 if (status != B_OK)
336 return status;
337
338 size_t size = info->capacity * info->block_size;
339 return user_memcpy(buffer, &size, sizeof(size_t));
340 }
341
342 case B_GET_GEOMETRY:
343 {
344 if (buffer == NULL || length > sizeof(device_geometry))
345 return B_BAD_VALUE;
346
347 device_geometry geometry;
348 status_t status = get_geometry(handle, &geometry);
349 if (status != B_OK)
350 return status;
351
352 return user_memcpy(buffer, &geometry, length);
353 }
354
355 case B_GET_ICON_NAME:
356 // TODO: take device type into account!
357 return user_strlcpy((char*)buffer, info->removable
358 ? "devices/drive-removable-media" : "devices/drive-harddisk",
359 B_FILE_NAME_LENGTH);
360
361 case B_GET_VECTOR_ICON:
362 {
363 // TODO: take device type into account!
364 device_icon iconData;
365 if (length != sizeof(device_icon))
366 return B_BAD_VALUE;
367 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
368 return B_BAD_ADDRESS;
369
370 if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
371 if (user_memcpy(iconData.icon_data, kDriveIcon,
372 sizeof(kDriveIcon)) != B_OK)
373 return B_BAD_ADDRESS;
374 }
375
376 iconData.icon_size = sizeof(kDriveIcon);
377 return user_memcpy(buffer, &iconData, sizeof(device_icon));
378 }
379
380 case B_EJECT_DEVICE:
381 case B_SCSI_EJECT:
382 return load_eject(info, false);
383
384 case B_LOAD_MEDIA:
385 return load_eject(info, true);
386
387 case B_FLUSH_DRIVE_CACHE:
388 return synchronize_cache(info);
389
390 case B_TRIM_DEVICE:
391 {
392 // We know the buffer is kernel-side because it has been
393 // preprocessed in devfs
394 ASSERT(IS_KERNEL_ADDRESS(buffer));
395 return trim_device(info, (fs_trim_data*)buffer);
396 }
397
398 default:
399 return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
400 buffer, length);
401 }
402 }
403
404
405 // #pragma mark - scsi_periph callbacks
406
407
408 static void
das_set_capacity(das_driver_info * info,uint64 capacity,uint32 blockSize,uint32 physicalBlockSize)409 das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize, uint32 physicalBlockSize)
410 {
411 TRACE("das_set_capacity(device = %p, capacity = %" B_PRIu64
412 ", blockSize = %" B_PRIu32 ")\n", info, capacity, blockSize);
413
414 info->capacity = capacity;
415
416 if (info->block_size != blockSize) {
417 if (info->block_size != 0) {
418 dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
419 blockSize);
420 panic("updating DMAResource not yet implemented...");
421 }
422
423 // TODO: we need to replace the DMAResource in our IOScheduler
424 status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
425 32);
426 if (status != B_OK)
427 panic("initializing DMAResource failed: %s", strerror(status));
428
429 info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
430 info->dma_resource);
431 if (info->io_scheduler == NULL)
432 panic("allocating IOScheduler failed.");
433
434 // TODO: use whole device name here
435 status = info->io_scheduler->Init("scsi");
436 if (status != B_OK)
437 panic("initializing IOScheduler failed: %s", strerror(status));
438
439 info->io_scheduler->SetCallback(do_io, info);
440 }
441
442 info->block_size = blockSize;
443 info->physical_block_size = physicalBlockSize;
444 }
445
446
447 static void
das_media_changed(das_driver_info * device,scsi_ccb * request)448 das_media_changed(das_driver_info *device, scsi_ccb *request)
449 {
450 // do a capacity check
451 // TODO: is this a good idea (e.g. if this is an empty CD)?
452 sSCSIPeripheral->check_capacity(device->scsi_periph_device, request);
453 }
454
455
456 scsi_periph_callbacks callbacks = {
457 (void (*)(periph_device_cookie, uint64, uint32, uint32))das_set_capacity,
458 (void (*)(periph_device_cookie, scsi_ccb *))das_media_changed
459 };
460
461
462 // #pragma mark - driver module API
463
464
465 static float
das_supports_device(device_node * parent)466 das_supports_device(device_node *parent)
467 {
468 const char *bus;
469 uint8 deviceType;
470
471 // make sure parent is really the SCSI bus manager
472 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
473 return -1;
474
475 if (strcmp(bus, "scsi"))
476 return 0.0;
477
478 // check whether it's really a Direct Access Device
479 if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
480 &deviceType, true) != B_OK || deviceType != scsi_dev_direct_access)
481 return 0.0;
482
483 return 0.6;
484 }
485
486
487 /*! Called whenever a new device was added to system;
488 if we really support it, we create a new node that gets
489 server by the block_io module
490 */
491 static status_t
das_register_device(device_node * node)492 das_register_device(device_node *node)
493 {
494 const scsi_res_inquiry *deviceInquiry = NULL;
495 size_t inquiryLength;
496 uint32 maxBlocks;
497
498 // get inquiry data
499 if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
500 (const void **)&deviceInquiry, &inquiryLength, true) != B_OK
501 || inquiryLength < sizeof(scsi_res_inquiry))
502 return B_ERROR;
503
504 // get block limit of underlying hardware to lower it (if necessary)
505 if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
506 &maxBlocks, true) != B_OK)
507 maxBlocks = INT_MAX;
508
509 // using 10 byte commands, at most 0xffff blocks can be transmitted at once
510 // (sadly, we cannot update this value later on if only 6 byte commands
511 // are supported, but the block_io module can live with that)
512 maxBlocks = min_c(maxBlocks, 0xffff);
513
514 // ready to register
515 device_attr attrs[] = {
516 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "SCSI Disk" }},
517 // tell block_io whether the device is removable
518 {"removable", B_UINT8_TYPE, {.ui8 = deviceInquiry->removable_medium}},
519 // impose own max block restriction
520 {B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {.ui32 = maxBlocks}},
521 { NULL }
522 };
523
524 return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME,
525 attrs, NULL, NULL);
526 }
527
528
529 static status_t
das_init_driver(device_node * node,void ** cookie)530 das_init_driver(device_node *node, void **cookie)
531 {
532 TRACE("das_init_driver");
533
534 uint8 removable;
535 status_t status = sDeviceManager->get_attr_uint8(node, "removable",
536 &removable, false);
537 if (status != B_OK)
538 return status;
539
540 das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info));
541 if (info == NULL)
542 return B_NO_MEMORY;
543
544 memset(info, 0, sizeof(*info));
545
546 info->dma_resource = new(std::nothrow) DMAResource;
547 if (info->dma_resource == NULL) {
548 free(info);
549 return B_NO_MEMORY;
550 }
551
552 info->node = node;
553 info->removable = removable;
554
555 device_node* parent = sDeviceManager->get_parent_node(node);
556 sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi,
557 (void **)&info->scsi_device);
558 sDeviceManager->put_node(parent);
559
560 status = sSCSIPeripheral->register_device((periph_device_cookie)info,
561 &callbacks, info->scsi_device, info->scsi, info->node,
562 info->removable, 10, &info->scsi_periph_device);
563 if (status != B_OK) {
564 delete info->dma_resource;
565 free(info);
566 return status;
567 }
568
569 *cookie = info;
570 return B_OK;
571 }
572
573
574 static void
das_uninit_driver(void * _cookie)575 das_uninit_driver(void *_cookie)
576 {
577 das_driver_info* info = (das_driver_info*)_cookie;
578
579 sSCSIPeripheral->unregister_device(info->scsi_periph_device);
580 delete info->dma_resource;
581 free(info);
582 }
583
584
585 static status_t
das_register_child_devices(void * _cookie)586 das_register_child_devices(void* _cookie)
587 {
588 das_driver_info* info = (das_driver_info*)_cookie;
589 status_t status;
590
591 char* name = sSCSIPeripheral->compose_device_name(info->node,
592 "disk/scsi");
593 if (name == NULL)
594 return B_ERROR;
595
596 status = sDeviceManager->publish_device(info->node, name,
597 SCSI_DISK_DEVICE_MODULE_NAME);
598
599 free(name);
600 return status;
601 }
602
603
604 static status_t
das_rescan_child_devices(void * _cookie)605 das_rescan_child_devices(void* _cookie)
606 {
607 das_driver_info* info = (das_driver_info*)_cookie;
608 uint64 capacity = info->capacity;
609 update_capacity(info);
610 if (info->capacity != capacity)
611 sSCSIPeripheral->media_changed(info->scsi_periph_device);
612 return B_OK;
613 }
614
615
616
617 module_dependency module_dependencies[] = {
618 {SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
619 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
620 {}
621 };
622
623 struct device_module_info sSCSIDiskDevice = {
624 {
625 SCSI_DISK_DEVICE_MODULE_NAME,
626 0,
627 NULL
628 },
629
630 das_init_device,
631 das_uninit_device,
632 NULL, //das_remove,
633
634 das_open,
635 das_close,
636 das_free,
637 NULL, // read
638 NULL, // write
639 das_io,
640 das_ioctl,
641
642 NULL, // select
643 NULL, // deselect
644 };
645
646 struct driver_module_info sSCSIDiskDriver = {
647 {
648 SCSI_DISK_DRIVER_MODULE_NAME,
649 0,
650 NULL
651 },
652
653 das_supports_device,
654 das_register_device,
655 das_init_driver,
656 das_uninit_driver,
657 das_register_child_devices,
658 das_rescan_child_devices,
659 NULL, // removed
660 };
661
662 module_info* modules[] = {
663 (module_info*)&sSCSIDiskDriver,
664 (module_info*)&sSCSIDiskDevice,
665 NULL
666 };
667