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