Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / devices / mem_bus / libmemsparse / memsparse.c
CommitLineData
920dae64
AT
1/*
2* ========== Copyright Header Begin ==========================================
3*
4* OpenSPARC T2 Processor File: memsparse.c
5* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
6* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
7*
8* The above named program is free software; you can redistribute it and/or
9* modify it under the terms of the GNU General Public
10* License version 2 as published by the Free Software Foundation.
11*
12* The above named program is distributed in the hope that it will be
13* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15* General Public License for more details.
16*
17* You should have received a copy of the GNU General Public
18* License along with this work; if not, write to the Free Software
19* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20*
21* ========== Copyright Header End ============================================
22*/
23/*
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27#pragma ident "@(#)memsparse.c 1.2 07/03/26 SMI"
28
29 /*
30 * Special purpose memory device to allow for holey memory based on
31 * address patterns. Only accesses to the memory device are checked,
32 * the device implements data for the entire range of the device and
33 * a backing file, if used, also contains data for the entire range.
34 *
35 * For example:
36 * device "memsparse" 0x100000 +1M {
37 * mask 0xc0 0x40;
38 * load "test.bin";
39 * }
40 * This specifies that bits [7:6] of the address (the '0xc0') must
41 * be 01(binary) (the '0x40').
42 */
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <sys/mman.h>
51#include <errno.h>
52#include <fcntl.h>
53#include <strings.h>
54
55#include "basics.h"
56#include "allocate.h"
57#include "lexer.h"
58#include "simcore.h"
59#include "config.h"
60#include "dumpinfo.h"
61#include "strutil.h"
62#include "fatal.h"
63#include "options.h"
64
65
66static void memsprse_parse(config_dev_t *);
67static void memsprse_init(config_dev_t *);
68static void memsprse_dump(config_dev_t *);
69static tpaddr_t memsprse_cacheable(config_addr_t *, dev_access_t type,
70 tpaddr_t off, uint8_t **cbp);
71static bool_t memsprse_cpu_access(simcpu_t *, config_addr_t *, tpaddr_t off,
72 maccess_t op, uint64_t *regp);
73
74dev_type_t dev_type_memsparse = {
75 "memsparse",
76 memsprse_parse,
77 memsprse_init,
78 memsprse_dump,
79 memsprse_cacheable,
80 memsprse_cpu_access, /* shouldnt be called if we're a RAM */
81 DEV_MAGIC
82};
83
84
85typedef struct {
86 uint8_t *datap;
87 uint64_t size; /* size of memory segment */
88 char *fnamep;
89 uint64_t mask;
90 uint64_t match;
91 uint64_t stride; /* 2^mask_SLB */
92 uint64_t stridemask; /* stride - 1 */
93 uint64_t fileextent; /* how much of file to load in */
94 uint64_t fileextenta; /* page alligned fileextent */
95 uint64_t fileoffset; /* offset in file to start loading */
96 uint64_t memoffset; /* offset in memory of where to load */
97 bool_t is_rom; /* mmap file with MAP_PRIVATE flag */
98 bool_t is_shared; /* mmap file with MAP_SHARED flag */
99} memsprse_dev_t;
100
101/* This should be in the processor structure - but is common to all now. */
102#define SS_CACHE_LINE 64
103
104
105 /*
106 *
107 */
108
109void
110memsprse_parse(config_dev_t *config_devp)
111{
112 lexer_tok_t tok;
113 memsprse_dev_t *mdp;
114 long pgsize;
115 uint64_t x;
116
117 pgsize = getpagesize();
118 /*
119 * Allocate the memory device and all that stuff
120 */
121 mdp = Xcalloc(1, memsprse_dev_t);
122
123 mdp->is_rom = false;
124 mdp->is_shared = false;
125 mdp->mask = 0;
126 mdp->match = 0;
127 mdp->fnamep = NULL;
128 mdp->datap = (void*)0;
129 mdp->size = config_devp->addrp->range;
130
131/* CSTYLED */
132DBG( PRINTF(("memsprse_parse: parsing device %d\n", config_devp->device_id)); );
133
134 tok = lex_get_token();
135 switch (tok) {
136 case T_S_Colon:
137 goto finished; /* nothing more to parse */
138 case T_L_Brace:
139 break;
140 default:
141 lex_fatal("expecting either ; or { when parsing memory device");
142 }
143
144
145 /*
146 * Start the parsing loop
147 */
148 do {
149 char *fnamep;
150 uint64_t startoffset = 0LL;
151 uint64_t foffset = 0LL;
152 uint64_t flen = 0LL;
153 struct stat sb;
154 int idx;
155 bool_t is_shared, is_rom;
156
157 tok = lex_get_token();
158
159 if (tok == T_R_Brace) break;
160
161 if (tok != T_Token) {
162fail:;
163 lex_fatal("expected load, rom, shared or mask "
164 "directive parsing memory device");
165 }
166
167 if (streq(lex.strp, "mask")) {
168 if (mdp->mask) lex_fatal("mask already specified");
169 tok = lex_get_token();
170 if (tok != T_Number)
171 lex_fatal("mask expects a number");
172 if (lex.val == 0)
173 lex_fatal("mask must be non-zero");
174 mdp->mask = lex.val;
175 tok = lex_get_token();
176 if (tok != T_Number)
177 lex_fatal("mask expects a second number");
178 if ((lex.val & ~mdp->mask) != 0)
179 lex_fatal("(match & ~mask) must be zero");
180 mdp->match = lex.val;
181 lex_get(T_S_Colon);
182 continue;
183 }
184
185 if (streq(lex.strp, "rom")) {
186 if (mdp->is_rom) lex_fatal("rom already specified");
187 mdp->is_rom = true;
188 lex_get(T_S_Colon);
189 continue;
190 }
191
192 if (streq(lex.strp, "shared")) {
193 if (mdp->is_shared)
194 lex_fatal("shared already specified");
195 mdp->is_shared = true;
196 lex_get(T_S_Colon);
197 continue;
198 }
199
200 if (!streq(lex.strp, "load")) goto fail;
201
202 if (mdp->fnamep != NULL)
203 lex_fatal("load already specified");
204
205 tok = lex_get_token();
206
207 /*
208 * Get the load offset, ie. the offset from the base
209 * address to load this file. For disk devices, we
210 * don't need this as we use the size of the previous
211 * disk to work out where to load the next disk.
212 */
213 switch (tok) {
214 case T_Plus:
215 lex_get(T_Number);
216 startoffset = lex.val;
217 break;
218 case T_Number:
219 if (lex.val < config_devp->addrp->baseaddr ||
220 lex.val >= config_devp->addrp->topaddr)
221 lex_fatal("specified load address is outside "
222 "the range of the memory device");
223
224 startoffset = lex.val - config_devp->addrp->baseaddr;
225 break;
226 case T_String:
227 startoffset = 0x0LL;
228 goto got_string;
229 default:
230 lex_fatal("Expected either a start address / offset "
231 "or filename for memory device load directive");
232 }
233
234 lex_get(T_String);
235
236got_string:;
237 fnamep = Xstrdup(lex.strp);
238
239 tok = lex_get_token();
240 if (tok == T_S_Colon)
241 goto load_file;
242 if (tok != T_Number)
243 lex_fatal("Expected ; or file offset for memory device "
244 "load directive");
245 if (lex.val < 0LL)
246 lex_fatal("load file offset must be >0 for load directive");
247
248 foffset = lex.val;
249
250 tok = lex_get_token();
251 if (tok == T_S_Colon)
252 goto load_file;
253 if (tok != T_Number)
254 lex_fatal("Expected ; or load length for memory device "
255 "load directive");
256 if (lex.val <= 0LL)
257 lex_fatal("load length must be >=0 for load directive");
258
259 flen = lex.val;
260
261 lex_get(T_S_Colon);
262
263load_file:
264 if (stat(fnamep, &sb) < 0)
265 lex_fatal("error opening load file %s", fnamep);
266
267 if (flen == 0LL)
268 flen = sb.st_size - foffset;
269
270 if (sb.st_size < (foffset + flen))
271 lex_fatal("load file %s is smaller than the specified "
272 "load range", fnamep);
273
274 if ((startoffset + flen) > config_devp->addrp->range)
275 lex_fatal("load file %s is larger than the memory device",
276 fnamep);
277
278 /* OK have parsed file info - add it to the load block */
279
280 mdp->fnamep = fnamep;
281 mdp->fileextent = flen; /* how much of file to load in */
282 mdp->fileextenta = sim_roundup(flen, pgsize);
283 mdp->fileoffset = foffset; /* offset in file to start load */
284 mdp->memoffset = startoffset; /* offset in mem to start load */
285 mdp->is_shared = is_shared; /* MMAP with flag MAP_SHARED */
286 mdp->is_rom = is_rom; /* MMAP with flag MAP_PRIVATE */
287
288 } while (1);
289
290finished:;
291 config_devp->devp = mdp;
292 if (mdp->mask == 0)
293 lex_fatal("memsparse must have a mask directive");
294 mdp->stride = 1;
295 x = mdp->mask;
296 while ((x & 1) == 0) {
297 x >>= 1;
298 mdp->stride <<= 1;
299 }
300 if (mdp->stride < SS_CACHE_LINE)
301 warning("memsparse mask stride 0x%llx < size of cache "
302 "line 0x%llx", mdp->stride, SS_CACHE_LINE);
303 mdp->stridemask = mdp->stride - 1;
304 if ((config_devp->addrp->baseaddr & mdp->stridemask) != 0)
305 lex_fatal("memsparse: base must be aligned to stride of "
306 "0x%llx", mdp->stride);
307 if ((config_devp->addrp->range & mdp->stridemask) != 0)
308 lex_fatal("memsparse: size must be a multiple of stride of "
309 "0x%llx", mdp->stride);
310}
311
312
313
314 /*
315 * Initialise the mem after parsing is complete
316 * If no files have been requested, then we force a
317 * /dev/zero mapping ...
318 */
319
320void
321memsprse_init(config_dev_t *config_devp)
322{
323 long pgsize;
324 memsprse_dev_t *mdp;
325 uint8_t *datap;
326 int idx;
327
328 mdp = (memsprse_dev_t *)config_devp->devp;
329 pgsize = getpagesize();
330
331 /*
332 * Perform a check to see that the requested loaded files are
333 * a) correctly aligned, and
334 * b) whether we need a /dev/zero mapping to back sections
335 * not covered by other mmapped files (beginning, middle,
336 * end), or because we need to "load" files in that are
337 * not correctly aligned.
338 */
339
340
341 /*
342 * For the moment we create a MAP_ANON block of the entire
343 * ram/rom then map over than any additional data files we
344 * need.
345 * Force a segv if we ever try and write to a "ROM".
346 */
347
348/* CSTYLED */
349DBG( PRINTF(("memory mapping : 0x%llx\n", sim_roundup(mdp->size, pgsize))); );
350
351 datap = mdp->datap = (void*)mmap(NULL, sim_roundup(mdp->size, pgsize),
352 PROT_READ | (mdp->is_rom ? 0 : PROT_WRITE),
353 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0);
354 if (MAP_FAILED == datap) fatal("Initial mmap of anon memory failed");
355
356 /*
357 * Now we either map or load in all the remaining files that the
358 * config file may have specified.
359 */
360
361 if (mdp->fnamep != NULL) {
362 int fd;
363 uint8_t *mapp;
364 bool_t is_shared = mdp->is_shared;
365
366
367 do {
368 fd = open(mdp->fnamep, is_shared ? O_RDWR : O_RDONLY);
369 } while (fd < 0 && EAGAIN == errno);
370
371 if (fd < 0) fatal("Failed opening file %s", mdp->fnamep);
372
373 /*
374 * Check that the fileoffset (offset in file to start
375 * loading) is page alligned
376 */
377 if ((mdp->fileoffset % pgsize) != 0)
378 fatal("Offset 0x%llx in file %s is not aligned "
379 "with 0x%llx pagesize",
380 mdp->fileoffset, mdp->fnamep, pgsize);
381
382#if 0 /* { */
383 /* Allow non-rounded out contents */
384 if ((mdp->fileextent % pgsize) != 0)
385 fatal("Loaded extent 0x%llx for file %s is not "
386 "aligned with 0x%llx pagesize",
387 mdp->fileextent, mdp->fnamep, pgsize);
388#endif /* } */
389
390 /*
391 * Check that the memoffset (offset in memory to start
392 * loading) is page alligned
393 */
394 if ((mdp->memoffset % pgsize) != 0)
395 fatal("Offset 0x%llx into RAM for file %s "
396 "is not aligned with 0x%llx pagesize",
397 mdp->memoffset, mdp->fnamep, pgsize);
398
399 ASSERT(NULL != datap || (NULL == datap &&
400 0LL == mdp->memoffset));
401
402/* CSTYLED */
403DBG( PRINTF(("mapping %s (%s): 0x%llx -> 0x%llx (0x%llx)\n",
404 mdp->fnamep,
405 (is_shared ? "SHARED" : "PRIVATE"),
406 config_devp->addrp->baseaddr + mdp->memoffset,
407 config_devp->addrp->baseaddr + mdp->memoffset +
408/* CSTYLED */
409 mdp->fileextent, mdp->fileextent)););
410
411 /*
412 * mmap the file into memory with the appropriate flags.
413 */
414 mapp = (void*)mmap((void*)(datap + mdp->memoffset),
415 mdp->fileextent,
416 PROT_READ | PROT_WRITE,
417 (is_shared ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED |
418 MAP_NORESERVE, fd, mdp->fileoffset);
419
420 if (((uint8_t *)MAP_FAILED) == mapp)
421 fatal("Failed mapping file %s",
422 mdp->fnamep);
423
424 ASSERT((datap + mdp->memoffset) == mapp);
425 }
426}
427
428
429
430
431
432
433 /*
434 * Memory configuration dump
435 */
436
437void
438memsprse_dump(config_dev_t *config_devp)
439{
440}
441
442
443/*
444 * Returns the extent of the linear cacheable block
445 * starting at offset, and a pointer to the state block
446 *
447 * Note: if the device is a ROM, attempts to write
448 * i.e. DA_Store as type must be failed -
449 * the function is supposed to return 0.
450 */
451
452tpaddr_t
453memsprse_cacheable(config_addr_t *config_addrp, dev_access_t type,
454 tpaddr_t offset, uint8_t **blockp)
455{
456 memsprse_dev_t *mdp;
457
458 mdp = config_addrp->config_devp->devp;
459
460 /* fail store attempts to a ROM */
461 if (mdp->is_rom && (type & DA_Store))
462 return ((tpaddr_t)0);
463
464
465 if ((offset < 0) || (offset >= config_addrp->range)) {
466/* CSTYLED */
467 SANITY(*blockp = NULL;);
468 return (NULL);
469 }
470
471 if (((config_addrp->baseaddr + offset) & mdp->mask) !=
472 mdp->match) {
473 return ((tpaddr_t)0);
474 }
475
476 *blockp = mdp->datap + offset;
477
478 /*
479 * This expression for the extent relies on parse enforcing
480 * alignment of base address and size to mdp->stride.
481 */
482 return (mdp->stride - (offset & (mdp->stride - 1)));
483}
484
485
486
487/*
488 * Should only get invoked if this is a ROM.
489 * Indicate the store failed - return false
490 * ROMs are read only.
491 */
492
493bool_t
494memsprse_cpu_access(simcpu_t *sp, config_addr_t *cap, tpaddr_t offset,
495 maccess_t op, uint64_t *regp)
496{
497 memsprse_dev_t *mdp;
498
499 mdp = cap->config_devp->devp;
500
501 if (mdp->is_rom) {
502 EXEC_WARNING(("memsprse_cpu_access: attempted store to ROM "
503 "@ pc 0x%llx of %d bytes at offset 0x%llx",
504 sp->pc, 1<<(op & MA_Size_Mask), offset));
505 }
506 return (false);
507}