1 /*  dv-nvram.c -- Generic driver for a non volatile ram (battery saved)
2     Copyright (C) 1999-2013 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 
84 enum 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 
103 struct 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 
118 static hw_io_read_buffer_method  nvram_io_read_buffer;
119 static hw_io_write_buffer_method nvram_io_write_buffer;
120 
121 
122 
123 static void
attach_nvram_regs(struct hw * me,struct nvram * controller)124 attach_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 
237 static void
nvram_finish(struct hw * me)238 nvram_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 
256 static unsigned
nvram_io_read_buffer(struct hw * me,void * dest,int space,unsigned_word base,unsigned nr_bytes)257 nvram_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 
279 static unsigned
nvram_io_write_buffer(struct hw * me,const void * source,int space,unsigned_word base,unsigned nr_bytes)280 nvram_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 
346 const struct hw_descriptor dv_nvram_descriptor[] = {
347   { "nvram", nvram_finish, },
348   { NULL },
349 };
350 
351