xref: /netbsd-src/external/gpl3/gdb/dist/sim/ppc/hw_disk.c (revision d9030711976e533fb52cfed73ebd1a865ef88911)
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 #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