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