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