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