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
open_disk_image(device * me,hw_disk_device * disk,const char * name)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     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 
219 static void
hw_disk_init_address(device * me)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
hw_disk_ioctl(device * me,cpu * processor,unsigned_word cia,device_ioctl_request request,va_list ap)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
hw_disk_io_read_buffer(device * me,void * dest,int space,unsigned_word addr,unsigned nr_bytes,cpu * processor,unsigned_word cia)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
hw_disk_io_write_buffer(device * me,const void * source,int space,unsigned_word addr,unsigned nr_bytes,cpu * processor,unsigned_word cia)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
hw_disk_instance_delete(device_instance * instance)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   zfree(data);
355 }
356 
357 static int
hw_disk_instance_read(device_instance * instance,void * buf,unsigned_word len)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
hw_disk_instance_write(device_instance * instance,const void * buf,unsigned_word len)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
hw_disk_instance_seek(device_instance * instance,unsigned_word pos_hi,unsigned_word pos_lo)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
hw_disk_max_transfer(device_instance * instance,int n_stack_args,unsigned32 stack_args[],int n_stack_returns,unsigned32 stack_returns[])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
hw_disk_block_size(device_instance * instance,int n_stack_args,unsigned32 stack_args[],int n_stack_returns,unsigned32 stack_returns[])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
hw_disk_nr_blocks(device_instance * instance,int n_stack_args,unsigned32 stack_args[],int n_stack_returns,unsigned32 stack_returns[])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 *
hw_disk_create_instance(device * me,const char * path,const char * args)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 *
hw_disk_create(const char * name,const device_unit * unit_address,const char * args)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_ */
561