1 /* This file is part of the program psim. 2 3 Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, see <http://www.gnu.org/licenses/>. 17 18 */ 19 20 21 #ifndef _HW_DISK_C_ 22 #define _HW_DISK_C_ 23 24 #include "device_table.h" 25 26 #include "pk.h" 27 28 #include <stdio.h> 29 #include <unistd.h> 30 31 #ifndef SEEK_SET 32 #define SEEK_SET 0 33 #endif 34 35 /* DEVICE 36 37 38 cdrom - read-only removable mass storage device 39 40 disk - mass storage device 41 42 floppy - removable mass storage device 43 44 45 DESCRIPTION 46 47 48 Mass storage devices such as a hard-disk or cdrom-drive are not 49 normally directly connected to the processor. Instead, these 50 devices are attached to a logical bus, such as SCSI or IDE, and 51 then a controller of that bus is made accessible to the processor. 52 53 Reflecting this, within a device tree, mass storage devices such as 54 a <<cdrom>>, <<disk>> or <<floppy>> are created as children of of a 55 logical bus controller node (such as a SCSI or IDE interface). 56 That controller, in turn, would be made the child of a physical bus 57 node that is directly accessible to the processor. 58 59 The above mass storage devices provide two interfaces - a logical 60 and a physical. 61 62 At the physical level the <<device_io_...>> functions can be used 63 perform reads and writes of the raw media. The address being 64 interpreted as an offset from the start of the disk. 65 66 At the logical level, it is possible to create an instance of the 67 disk that provides access to any of the physical media, a disk 68 partition, or even a file within a partition. The <<disk-label>> 69 package, which implements this functionality, is described 70 elsewhere. Both the Open Firmware and Moto BUG rom emulations 71 support this interface. 72 73 Block devices such as the <<floppy>> and <<cdrom>> have removable 74 media. At the programmer level, the media can be changed using the 75 <<change_media>> ioctl. From within GDB, a <<change-media>> 76 operation can be initated by using the command. 77 78 | (gdb) sim 79 80 81 PROPERTIES 82 83 84 file = <file-name> (required) 85 86 The name of the file that contains an image of the disk. For 87 <<disk>> and <<floppy>> devices, the image will be opened for both 88 reading and writing. Multiple image files may be specified, the 89 second and later files being opened when <<change-media>> (with a 90 NULL file name) being specified. 91 92 93 block-size = <nr-bytes> (optional) 94 95 The value is returned by the block-size method. The default value 96 is 512 bytes. 97 98 99 max-transfer = <nr-bytes> (optional) 100 101 The value is returned by the max-transfer method. The default value 102 is 512 bytes. 103 104 105 #blocks = <nr-blocks> (optional) 106 107 The value is returned by the #blocks method. If no value is 108 present then -1 is returned. 109 110 111 read-only = <anything> (optional) 112 113 If this property is present, the disk file image is always opened 114 read-only. 115 116 EXAMPLES 117 118 119 Enable tracing 120 121 | $ psim -t 'disk-device' \ 122 123 124 Add a CDROM and disk to an IDE bus. Specify the host operating 125 system's cd drive as the CD-ROM image. 126 127 | -o '/pci/ide/disk@0/file "disk-image' \ 128 | -o '/pci/ide/cdrom@1/file "/dev/cd0a' \ 129 130 131 As part of the code implementing a logical bus device (for instance 132 the IDE controller), locate the CDROM device and then read block 133 47. 134 135 | device *cdrom = device_tree_find_device(me, "cdrom"); 136 | char block[512]; 137 | device_io_read_buffer(cdrom, buf, 0, 138 0, 47 * sizeof(block), // space, address 139 sizeof(block), NULL, 0); 140 141 142 Use the device instance interface to read block 47 of the file 143 called <<netbsd.elf>> on the disks default partition. Similar code 144 would be used in an operating systems pre-boot loader. 145 146 | device_instance *netbsd = 147 | device_create_instance(root, "/pci/ide/disk:,\netbsd.elf"); 148 | char block[512]; 149 | device_instance_seek(netbsd, 0, 47 * sizeof(block)); 150 | device_instance_read(netbsd, block, sizeof(block)); 151 152 153 BUGS 154 155 156 The block device specification includes mechanisms for determining 157 the physical device characteristics - such as the disks size. 158 Currently this mechanism is not implemented. 159 160 The functionality of this device (in particular the device instance 161 interface) depends on the implementation of <<disk-label>> package. 162 That package may not be fully implemented. 163 164 The disk does not know its size. Hence it relies on the failure of 165 fread(), fwrite() and fseek() calls to detect errors. 166 167 The disk size is limited by the addressable range covered by 168 unsigned_word (addr). An extension would be to instead use the 169 concatenated value space:addr. 170 171 The method #blocks should `stat' the disk to determine the number 172 of blocks if there is no #blocks property. 173 174 It would appear that OpenFirmware does not define a client call for 175 changing (ejecting) the media of a device. 176 177 */ 178 179 typedef struct _hw_disk_device { 180 int name_index; 181 int nr_names; 182 char *name; 183 int read_only; 184 /* unsigned_word size; */ 185 FILE *image; 186 } hw_disk_device; 187 188 typedef struct _hw_disk_instance { 189 unsigned_word pos; 190 hw_disk_device *disk; 191 } hw_disk_instance; 192 193 194 static void 195 open_disk_image(device *me, 196 hw_disk_device *disk, 197 const char *name) 198 { 199 if (disk->image != NULL) 200 fclose(disk->image); 201 if (disk->name != NULL) 202 free(disk->name); 203 disk->name = strdup(name); 204 disk->image = fopen(disk->name, disk->read_only ? "r" : "r+"); 205 if (disk->image == NULL) { 206 perror(device_name(me)); 207 device_error(me, "open %s failed\n", disk->name); 208 } 209 210 DTRACE(disk, ("image %s (%s)\n", 211 disk->name, 212 (disk->read_only ? "read-only" : "read-write"))); 213 } 214 215 static void 216 hw_disk_init_address(device *me) 217 { 218 hw_disk_device *disk = device_data(me); 219 unsigned_word address; 220 int space; 221 const char *name; 222 223 /* attach to the parent. Since the bus is logical, attach using just 224 the unit-address (size must be zero) */ 225 device_address_to_attach_address(device_parent(me), device_unit_address(me), 226 &space, &address, me); 227 device_attach_address(device_parent(me), attach_callback, 228 space, address, 0/*size*/, access_read_write_exec, 229 me); 230 231 /* Tell the world we are a disk. */ 232 device_add_string_property(me, "device_type", "block"); 233 234 /* get the name of the file specifying the disk image */ 235 disk->name_index = 0; 236 disk->nr_names = device_find_string_array_property(me, "file", 237 disk->name_index, &name); 238 if (!disk->nr_names) 239 device_error(me, "invalid file property"); 240 241 /* is it a RO device? */ 242 disk->read_only = 243 (strcmp(device_name(me), "disk") != 0 244 && strcmp(device_name(me), "floppy") != 0 245 && device_find_property(me, "read-only") == NULL); 246 247 /* now open it */ 248 open_disk_image(me, disk, name); 249 } 250 251 static int 252 hw_disk_ioctl(device *me, 253 cpu *processor, 254 unsigned_word cia, 255 device_ioctl_request request, 256 va_list ap) 257 { 258 switch (request) { 259 case device_ioctl_change_media: 260 { 261 hw_disk_device *disk = device_data(me); 262 const char *name = va_arg(ap, const char *); 263 if (name != NULL) { 264 disk->name_index = -1; 265 } 266 else { 267 disk->name_index = (disk->name_index + 1) % disk->nr_names; 268 if (!device_find_string_array_property(me, "file", 269 disk->name_index, &name)) 270 device_error(me, "invalid file property"); 271 } 272 open_disk_image(me, disk, name); 273 } 274 break; 275 default: 276 device_error(me, "insupported ioctl request"); 277 break; 278 } 279 return 0; 280 } 281 282 283 284 285 286 static unsigned 287 hw_disk_io_read_buffer(device *me, 288 void *dest, 289 int space, 290 unsigned_word addr, 291 unsigned nr_bytes, 292 cpu *processor, 293 unsigned_word cia) 294 { 295 hw_disk_device *disk = device_data(me); 296 unsigned nr_bytes_read; 297 if (space != 0) 298 device_error(me, "read - extended disk addressing unimplemented"); 299 if (nr_bytes == 0) 300 nr_bytes_read = 0; 301 else if (fseek(disk->image, addr, SEEK_SET) < 0) 302 nr_bytes_read = 0; 303 else if (fread(dest, nr_bytes, 1, disk->image) != 1) 304 nr_bytes_read = 0; 305 else 306 nr_bytes_read = nr_bytes; 307 DTRACE(disk, ("io-read - address 0x%lx, nr-bytes-read %d, requested %d\n", 308 (unsigned long) addr, (int)nr_bytes_read, (int)nr_bytes)); 309 return nr_bytes_read; 310 } 311 312 313 static unsigned 314 hw_disk_io_write_buffer(device *me, 315 const void *source, 316 int space, 317 unsigned_word addr, 318 unsigned nr_bytes, 319 cpu *processor, 320 unsigned_word cia) 321 { 322 hw_disk_device *disk = device_data(me); 323 unsigned nr_bytes_written; 324 if (space != 0) 325 device_error(me, "write - extended disk addressing unimplemented"); 326 if (disk->read_only) 327 nr_bytes_written = 0; 328 else if (nr_bytes == 0) 329 nr_bytes_written = 0; 330 else if (fseek(disk->image, addr, SEEK_SET) < 0) 331 nr_bytes_written = 0; 332 else if (fwrite(source, nr_bytes, 1, disk->image) != 1) 333 nr_bytes_written = 0; 334 else 335 nr_bytes_written = nr_bytes; 336 DTRACE(disk, ("io-write - address 0x%lx, nr-bytes-written %d, requested %d\n", 337 (unsigned long) addr, (int)nr_bytes_written, (int)nr_bytes)); 338 return nr_bytes_written; 339 } 340 341 342 /* instances of the hw_disk device */ 343 344 static void 345 hw_disk_instance_delete(device_instance *instance) 346 { 347 hw_disk_instance *data = device_instance_data(instance); 348 DITRACE(disk, ("delete - instance=%ld\n", 349 (unsigned long)device_instance_to_external(instance))); 350 free(data); 351 } 352 353 static int 354 hw_disk_instance_read(device_instance *instance, 355 void *buf, 356 unsigned_word len) 357 { 358 hw_disk_instance *data = device_instance_data(instance); 359 DITRACE(disk, ("read - instance=%ld len=%ld\n", 360 (unsigned long)device_instance_to_external(instance), 361 (long)len)); 362 if ((data->pos + len) < data->pos) 363 return -1; /* overflow */ 364 if (fseek(data->disk->image, data->pos, SEEK_SET) < 0) 365 return -1; 366 if (fread(buf, len, 1, data->disk->image) != 1) 367 return -1; 368 data->pos = ftell(data->disk->image); 369 return len; 370 } 371 372 static int 373 hw_disk_instance_write(device_instance *instance, 374 const void *buf, 375 unsigned_word len) 376 { 377 hw_disk_instance *data = device_instance_data(instance); 378 DITRACE(disk, ("write - instance=%ld len=%ld\n", 379 (unsigned long)device_instance_to_external(instance), 380 (long)len)); 381 if ((data->pos + len) < data->pos) 382 return -1; /* overflow */ 383 if (data->disk->read_only) 384 return -1; 385 if (fseek(data->disk->image, data->pos, SEEK_SET) < 0) 386 return -1; 387 if (fwrite(buf, len, 1, data->disk->image) != 1) 388 return -1; 389 data->pos = ftell(data->disk->image); 390 return len; 391 } 392 393 static int 394 hw_disk_instance_seek(device_instance *instance, 395 unsigned_word pos_hi, 396 unsigned_word pos_lo) 397 { 398 hw_disk_instance *data = device_instance_data(instance); 399 if (pos_hi != 0) 400 device_error(device_instance_device(instance), 401 "seek - extended addressing unimplemented"); 402 DITRACE(disk, ("seek - instance=%ld pos_hi=%ld pos_lo=%ld\n", 403 (unsigned long)device_instance_to_external(instance), 404 (long)pos_hi, (long)pos_lo)); 405 data->pos = pos_lo; 406 return 0; 407 } 408 409 static int 410 hw_disk_max_transfer(device_instance *instance, 411 int n_stack_args, 412 uint32_t stack_args[/*n_stack_args*/], 413 int n_stack_returns, 414 uint32_t stack_returns[/*n_stack_returns*/]) 415 { 416 device *me = device_instance_device(instance); 417 if ((n_stack_args != 0) 418 || (n_stack_returns != 1)) { 419 device_error(me, "Incorrect number of arguments for max-transfer method\n"); 420 return -1; 421 } 422 else { 423 unsigned_cell max_transfer; 424 if (device_find_property(me, "max-transfer")) 425 max_transfer = device_find_integer_property(me, "max-transfer"); 426 else 427 max_transfer = 512; 428 DITRACE(disk, ("max-transfer - instance=%ld max-transfer=%ld\n", 429 (unsigned long)device_instance_to_external(instance), 430 (long int)max_transfer)); 431 stack_returns[0] = max_transfer; 432 return 0; 433 } 434 } 435 436 static int 437 hw_disk_block_size(device_instance *instance, 438 int n_stack_args, 439 uint32_t stack_args[/*n_stack_args*/], 440 int n_stack_returns, 441 uint32_t stack_returns[/*n_stack_returns*/]) 442 { 443 device *me = device_instance_device(instance); 444 if ((n_stack_args != 0) 445 || (n_stack_returns != 1)) { 446 device_error(me, "Incorrect number of arguments for block-size method\n"); 447 return -1; 448 } 449 else { 450 unsigned_cell block_size; 451 if (device_find_property(me, "block-size")) 452 block_size = device_find_integer_property(me, "block-size"); 453 else 454 block_size = 512; 455 DITRACE(disk, ("block-size - instance=%ld block-size=%ld\n", 456 (unsigned long)device_instance_to_external(instance), 457 (long int)block_size)); 458 stack_returns[0] = block_size; 459 return 0; 460 } 461 } 462 463 static int 464 hw_disk_nr_blocks(device_instance *instance, 465 int n_stack_args, 466 uint32_t stack_args[/*n_stack_args*/], 467 int n_stack_returns, 468 uint32_t stack_returns[/*n_stack_returns*/]) 469 { 470 device *me = device_instance_device(instance); 471 if ((n_stack_args != 0) 472 || (n_stack_returns != 1)) { 473 device_error(me, "Incorrect number of arguments for block-size method\n"); 474 return -1; 475 } 476 else { 477 unsigned_word nr_blocks; 478 if (device_find_property(me, "#blocks")) 479 nr_blocks = device_find_integer_property(me, "#blocks"); 480 else 481 nr_blocks = -1; 482 DITRACE(disk, ("#blocks - instance=%ld #blocks=%ld\n", 483 (unsigned long)device_instance_to_external(instance), 484 (long int)nr_blocks)); 485 stack_returns[0] = nr_blocks; 486 return 0; 487 } 488 } 489 490 static device_instance_methods hw_disk_instance_methods[] = { 491 { "max-transfer", hw_disk_max_transfer }, 492 { "block-size", hw_disk_block_size }, 493 { "#blocks", hw_disk_nr_blocks }, 494 { NULL, }, 495 }; 496 497 static const device_instance_callbacks hw_disk_instance_callbacks = { 498 hw_disk_instance_delete, 499 hw_disk_instance_read, 500 hw_disk_instance_write, 501 hw_disk_instance_seek, 502 hw_disk_instance_methods, 503 }; 504 505 static device_instance * 506 hw_disk_create_instance(device *me, 507 const char *path, 508 const char *args) 509 { 510 device_instance *instance; 511 hw_disk_device *disk = device_data(me); 512 hw_disk_instance *data = ZALLOC(hw_disk_instance); 513 data->disk = disk; 514 data->pos = 0; 515 instance = device_create_instance_from(me, NULL, 516 data, 517 path, args, 518 &hw_disk_instance_callbacks); 519 DITRACE(disk, ("create - path=%s(%s) instance=%ld\n", 520 path, args, 521 (unsigned long)device_instance_to_external(instance))); 522 return pk_disklabel_create_instance(instance, args); 523 } 524 525 static device_callbacks const hw_disk_callbacks = { 526 { hw_disk_init_address, NULL }, 527 { NULL, }, /* address */ 528 { hw_disk_io_read_buffer, 529 hw_disk_io_write_buffer, }, 530 { NULL, }, /* DMA */ 531 { NULL, }, /* interrupt */ 532 { NULL, }, /* unit */ 533 hw_disk_create_instance, 534 hw_disk_ioctl, 535 }; 536 537 538 static void * 539 hw_disk_create(const char *name, 540 const device_unit *unit_address, 541 const char *args) 542 { 543 /* create the descriptor */ 544 hw_disk_device *hw_disk = ZALLOC(hw_disk_device); 545 return hw_disk; 546 } 547 548 549 const device_descriptor hw_disk_device_descriptor[] = { 550 { "disk", hw_disk_create, &hw_disk_callbacks }, 551 { "cdrom", hw_disk_create, &hw_disk_callbacks }, 552 { "floppy", hw_disk_create, &hw_disk_callbacks }, 553 { NULL }, 554 }; 555 556 #endif /* _HW_DISK_C_ */ 557