Home | History | Annotate | Line # | Download | only in ppc
      1 /*  This file is part of the program psim.
      2 
      3     Copyright (C) 1994-1997, Andrew Cagney <cagney (at) 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