041ed3830452ab39a597693c2b955c2722aac566
[external/binutils.git] / sim / ppc / hw_disk.c
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
183 typedef 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
192 typedef struct _hw_disk_instance {
193   unsigned_word pos;
194   hw_disk_device *disk;
195 } hw_disk_instance;
196
197
198 static void
199 open_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     free(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
219 static void
220 hw_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
255 static int
256 hw_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
290 static unsigned
291 hw_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
317 static unsigned
318 hw_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
348 static void
349 hw_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   free(data);
355 }
356
357 static int
358 hw_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
376 static int
377 hw_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
397 static int
398 hw_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
413 static int
414 hw_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
440 static int
441 hw_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
467 static int
468 hw_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
494 static 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
501 static 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
509 static device_instance *
510 hw_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
529 static 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
542 static void *
543 hw_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
553 const 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_ */