1/*  dv-nvram.c -- Generic driver for a non volatile ram (battery saved)
2    Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
3    Written by Stephane Carrez (stcarrez@worldnet.fr)
4    (From a driver model Contributed by Cygnus Solutions.)
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19    */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24#include "sim-assert.h"
25
26#include <unistd.h>
27#include <fcntl.h>
28#include <errno.h>
29
30
31/* DEVICE
32
33        nvram - Non Volatile Ram
34
35
36   DESCRIPTION
37
38        Implements a generic battery saved CMOS ram. This ram device does
39        not contain any realtime clock and does not generate any interrupt.
40        The ram content is loaded from a file and saved when it is changed.
41        It is intended to be generic.
42
43
44   PROPERTIES
45
46   reg <base> <length>
47
48        Base and size of the non-volatile ram bank.
49
50   file <path>
51
52        Path where the memory must be saved or loaded when we start.
53
54   mode {map | save-modified | save-all}
55
56        Controls how to load and save the memory content.
57
58           map            The file is mapped in memory
59           save-modified  The simulator keeps an open file descriptor to
60                          the file and saves portion of memory which are
61                          modified.
62           save-all       The simulator saves the complete memory each time
63                          it's modified (it does not keep an open file
64                          descriptor).
65
66
67   PORTS
68
69        None.
70
71
72   NOTES
73
74        This device is independent of the Motorola 68hc11.
75
76   */
77
78
79
80/* static functions */
81
82/* Control of how to access the ram and save its content.  */
83
84enum nvram_mode
85{
86  /* Save the complete ram block each time it's changed.
87     We don't keep an open file descriptor.  This should be
88     ok for small memory banks.  */
89  NVRAM_SAVE_ALL,
90
91  /* Save only the memory bytes which are modified.
92     This mode means that we have to keep an open file
93     descriptor (O_RDWR).  It's good for middle sized memory banks.  */
94  NVRAM_SAVE_MODIFIED,
95
96  /* Map file in memory (not yet implemented).
97     This mode is suitable for large memory banks.  We don't allocate
98     a buffer to represent the ram, instead it's mapped in memory
99     with mmap.  */
100  NVRAM_MAP_FILE
101};
102
103struct nvram
104{
105  address_word    base_address; /* Base address of ram.  */
106  unsigned        size;         /* Size of ram.  */
107  unsigned8       *data;        /* Pointer to ram memory.  */
108  const char      *file_name;   /* Path of ram file.  */
109  int             fd;           /* File description of opened ram file.  */
110  enum nvram_mode mode;         /* How load/save ram file.  */
111};
112
113
114
115/* Finish off the partially created hw device.  Attach our local
116   callbacks.  Wire up our port names etc.  */
117
118static hw_io_read_buffer_method  nvram_io_read_buffer;
119static hw_io_write_buffer_method nvram_io_write_buffer;
120
121
122
123static void
124attach_nvram_regs (struct hw *me, struct nvram *controller)
125{
126  unsigned_word attach_address;
127  int attach_space;
128  unsigned attach_size;
129  reg_property_spec reg;
130  int result, oerrno;
131
132  /* Get ram bank description (base and size).  */
133  if (hw_find_property (me, "reg") == NULL)
134    hw_abort (me, "Missing \"reg\" property");
135
136  if (!hw_find_reg_array_property (me, "reg", 0, &reg))
137    hw_abort (me, "\"reg\" property must contain one addr/size entry");
138
139  hw_unit_address_to_attach_address (hw_parent (me),
140				     &reg.address,
141				     &attach_space,
142				     &attach_address,
143				     me);
144  hw_unit_size_to_attach_size (hw_parent (me),
145			       &reg.size,
146			       &attach_size, me);
147
148  hw_attach_address (hw_parent (me), 0,
149		     attach_space, attach_address, attach_size,
150		     me);
151
152  controller->mode         = NVRAM_SAVE_ALL;
153  controller->base_address = attach_address;
154  controller->size         = attach_size;
155  controller->fd           = -1;
156
157  /* Get the file where the ram content must be loaded/saved.  */
158  if(hw_find_property (me, "file") == NULL)
159    hw_abort (me, "Missing \"file\" property");
160
161  controller->file_name = hw_find_string_property (me, "file");
162
163  /* Get the mode which defines how to save the memory.  */
164  if(hw_find_property (me, "mode") != NULL)
165    {
166      const char *value = hw_find_string_property (me, "mode");
167
168      if (strcmp (value, "map") == 0)
169        controller->mode = NVRAM_MAP_FILE;
170      else if (strcmp (value, "save-modified") == 0)
171        controller->mode = NVRAM_SAVE_MODIFIED;
172      else if (strcmp (value, "save-all") == 0)
173        controller->mode = NVRAM_SAVE_ALL;
174      else
175	hw_abort (me, "illegal value for mode parameter `%s': "
176                  "use map, save-modified or save-all", value);
177    }
178
179  /* Initialize the ram by loading/mapping the file in memory.
180     If the file does not exist, create and give it some content.  */
181  switch (controller->mode)
182    {
183    case NVRAM_MAP_FILE:
184      hw_abort (me, "'map' mode is not yet implemented, use 'save-modified'");
185      break;
186
187    case NVRAM_SAVE_MODIFIED:
188    case NVRAM_SAVE_ALL:
189      controller->data = (char*) hw_malloc (me, attach_size);
190      if (controller->data == 0)
191        hw_abort (me, "Not enough memory, try to use the mode 'map'");
192
193      memset (controller->data, 0, attach_size);
194      controller->fd = open (controller->file_name, O_RDWR);
195      if (controller->fd < 0)
196        {
197          controller->fd = open (controller->file_name,
198                                 O_RDWR | O_CREAT, 0644);
199          if (controller->fd < 0)
200            hw_abort (me, "Cannot open or create file '%s'",
201                      controller->file_name);
202          result = write (controller->fd, controller->data, attach_size);
203          if (result != attach_size)
204            {
205              oerrno = errno;
206              hw_free (me, controller->data);
207              close (controller->fd);
208              errno = oerrno;
209              hw_abort (me, "Failed to save the ram content");
210            }
211        }
212      else
213        {
214          result = read (controller->fd, controller->data, attach_size);
215          if (result != attach_size)
216            {
217              oerrno = errno;
218              hw_free (me, controller->data);
219              close (controller->fd);
220              errno = oerrno;
221              hw_abort (me, "Failed to load the ram content");
222            }
223        }
224      if (controller->mode == NVRAM_SAVE_ALL)
225        {
226          close (controller->fd);
227          controller->fd = -1;
228        }
229      break;
230
231    default:
232      break;
233    }
234}
235
236
237static void
238nvram_finish (struct hw *me)
239{
240  struct nvram *controller;
241
242  controller = HW_ZALLOC (me, struct nvram);
243
244  set_hw_data (me, controller);
245  set_hw_io_read_buffer (me, nvram_io_read_buffer);
246  set_hw_io_write_buffer (me, nvram_io_write_buffer);
247
248  /* Attach ourself to our parent bus.  */
249  attach_nvram_regs (me, controller);
250}
251
252
253
254/* generic read/write */
255
256static unsigned
257nvram_io_read_buffer (struct hw *me,
258                      void *dest,
259                      int space,
260                      unsigned_word base,
261                      unsigned nr_bytes)
262{
263  struct nvram *controller = hw_data (me);
264
265  HW_TRACE ((me, "read 0x%08lx %d [%ld]",
266             (long) base, (int) nr_bytes,
267             (long) (base - controller->base_address)));
268
269  base -= controller->base_address;
270  if (base + nr_bytes > controller->size)
271    nr_bytes = controller->size - base;
272
273  memcpy (dest, &controller->data[base], nr_bytes);
274  return nr_bytes;
275}
276
277
278
279static unsigned
280nvram_io_write_buffer (struct hw *me,
281                       const void *source,
282                       int space,
283                       unsigned_word base,
284                       unsigned nr_bytes)
285{
286  struct nvram *controller = hw_data (me);
287
288  HW_TRACE ((me, "write 0x%08lx %d [%ld]",
289             (long) base, (int) nr_bytes,
290             (long) (base - controller->base_address)));
291
292  base -= controller->base_address;
293  if (base + nr_bytes > controller->size)
294    nr_bytes = controller->size - base;
295
296  switch (controller->mode)
297    {
298    case NVRAM_SAVE_ALL:
299      {
300        int fd, result, oerrno;
301
302        fd = open (controller->file_name, O_WRONLY, 0644);
303        if (fd < 0)
304          {
305            return 0;
306          }
307
308        memcpy (&controller->data[base], source, nr_bytes);
309        result = write (fd, controller->data, controller->size);
310        oerrno = errno;
311        close (fd);
312        errno = oerrno;
313
314        if (result != controller->size)
315          {
316            return 0;
317          }
318        return nr_bytes;
319      }
320
321    case NVRAM_SAVE_MODIFIED:
322      {
323        off_t pos;
324        int result;
325
326        pos = lseek (controller->fd, (off_t) base, SEEK_SET);
327        if (pos != (off_t) base)
328          return 0;
329
330        result = write (controller->fd, source, nr_bytes);
331        if (result < 0)
332          return 0;
333
334        nr_bytes = result;
335        break;
336      }
337
338    default:
339      break;
340    }
341  memcpy (&controller->data[base], source, nr_bytes);
342  return nr_bytes;
343}
344
345
346const struct hw_descriptor dv_nvram_descriptor[] = {
347  { "nvram", nvram_finish, },
348  { NULL },
349};
350
351