Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / mem / Memory.cc
CommitLineData
920dae64
AT
1// ========== Copyright Header Begin ==========================================
2//
3// OpenSPARC T2 Processor File: Memory.cc
4// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
6//
7// The above named program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public
9// License version 2 as published by the Free Software Foundation.
10//
11// The above named program is distributed in the hope that it will be
12// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15//
16// You should have received a copy of the GNU General Public
17// License along with this work; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19//
20// ========== Copyright Header End ============================================
21/*
22 * Copyright (C) 2005 Sun Microsystems, Inc.
23 * All rights reserved.
24 */
25
26#include <errno.h>
27#include <signal.h>
28#include <ctype.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <assert.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <limits.h>
37
38#include "types.h"
39#include "dr.h"
40#include "ui.h"
41#include "system.h"
42#include "Memory.h"
43
44char mem_help_string[] = "Usage : mem -a <start addr> -s <num_of_bytes> [-cpu <id>][-dis -va]\n";
45char memdump_help_string[] = "memdump <filename> <start phys addr> <size>\n";
46static int mem_cmd_action (void * , int argc, char **argv);
47static int memdump_cmd_action (void * , int argc, char **argv);
48
49bool_t mem_dump (DR_OPAQUE pdr);
50bool_t mem_restore (DR_OPAQUE pdr);
51
52
53
54
55
56#if defined(MEMORY_FLAT)
57
58
59// constructor , e.g. SMemory(uint64_t(0x1) << 36,48);
60SMemory::SMemory
61(
62 uint64_t _ram_size, // mem size (bytes)
63 uint_t pa_bits // number of valid bits in physical address
64)
65 :
66 mem(0), size(_ram_size)
67{
68
69 assert(pa_bits < 64);
70 pa_mask = (uint64_t(1)<<pa_bits)-1;
71
72 for (int i = 0; i < SAM_NMEM_LOCKS; i++)
73 mutex_init(&locks[i], USYNC_THREAD, NULL);
74
75 DR_register ((char*)"mm1", mem_dump, mem_restore, (void*)this);
76 UI_register_cmd_2 ((char*)"mem", mem_help_string, mem_cmd_action, NULL);
77 UI_register_cmd_2 ((char*)"memdump", memdump_help_string, memdump_cmd_action, NULL);
78}
79
80
81// mem init method
82int SMemory::init
83(
84 char *file_name, // file name for private mem file, default is NULL
85 int is_private // 1 if private file, 0 if a temporary file (default)
86)
87{
88 // check if init method was called second time
89 if (mem && (mem != MAP_FAILED))
90 {
91 munmap((char*)mem,size);
92 }
93
94
95 int prot = PROT_READ|PROT_WRITE;
96 int flags = MAP_ALIGN;
97
98 mfile = -1;
99
100 if (file_name == NULL)
101 {
102 // map anonymous fragment (in swap space);
103 // for large mem size it requires to have enough
104 // swap space on the host machine
105 flags |= MAP_ANON;
106
107 if ( !SYSTEM_get_memreserve() )
108 {
109 // do not reserve swap space before the run
110 ui->warning("You might run out of swap space with -nomemreserve option \n");
111 flags |= MAP_NORESERVE|MAP_PRIVATE;
112 }
113 else
114 {
115 // swap space should be reserved before the run
116 flags |= MAP_SHARED;
117 }
118 mem = (uint8_t*)mmap((char*)0,size, prot, flags, -1, 0);
119 }
120 else // map memory image to a file
121 {
122 if (file_name[0] == '/')
123 {
124 // this is an absolute path
125 mem_file = strdup(file_name);
126 }
127 else // file in the working directory
128 {
129 char path[PATH_MAX];
130
131 if (getwd(path) == NULL)
132 {
133 ui->error("memory: cannot get current working directory\n");
134 return 0;
135 }
136 mem_file = (char *)calloc(strlen(path)+strlen(file_name)+16, sizeof(char));
137 sprintf(mem_file,"%s/%s", path, file_name);
138 }
139
140 ui->output("map ram image to %s, size = %lld bytes\n", mem_file, size);
141
142
143 if (is_private)
144 {
145 // open a checkpoint mem image
146 int img = open ( mem_file, O_RDONLY|O_LARGEFILE );
147
148 if (img < 0)
149 {
150 ui->error("memory: cannot open %s, %s\n",mem_file, strerror(errno));
151 return 0;
152 }
153
154 // To avoid the mem image file to be modified during the run
155 // map it as a private - all changes will go to pages allocated in
156 // swap space
157
158 if ( !SYSTEM_get_memreserve() )
159 {
160 // do not reserve swap space before the run
161 ui->warning("You might run out of swap space with -nomemreserve option \n");
162 flags |= MAP_NORESERVE;
163 }
164
165 mem = (uint8_t*)mmap((char*)0,size, prot, flags|MAP_PRIVATE, img, 0);
166 }
167 else // temporary mem file
168 {
169 if (access(mem_file, W_OK)==0)
170 {
171 ui->error("memory: another sim is already using %s file\n",mem_file);
172 return 0;
173 }
174
175 // open a new mem file
176 mfile = open (mem_file, O_RDWR|O_CREAT|O_LARGEFILE|O_TRUNC,
177 S_IRWXO|S_IRWXG|S_IRWXU);
178
179 if (mfile < 0)
180 {
181 ui->error("memory: cannot open %s, %s\n", mem_file, strerror(errno));
182 return 0;
183 }
184
185 // write the very last byte in the mem file
186 // to set the effective size of the file
187 if (lseek(mfile, size-1, SEEK_SET) < 0)
188 {
189 ui->error("memory: cannot set mem file size, %s \n", strerror(errno));
190 return 0;
191 }
192
193 if (write(mfile, "", 1) != 1)
194 {
195 ui->error("memory: cannot write mem file, %s \n", strerror(errno));
196 return 0;
197 }
198
199 mem = (uint8_t*)mmap((char*)0,size, prot, flags|MAP_SHARED, mfile, 0);
200 }
201
202
203 } // map to a file
204
205 if (mem == MAP_FAILED)
206 {
207 ui->error("memory: cannot mmap file, %s\n",strerror(errno));
208 return 0;
209 }
210
211
212 return 1;
213}
214/*}}}*/
215
216
217// load bin format
218int SMemory::load_bin( const char *file, uint64_t addr)/*{{{*/
219{
220 FILE *fp = fopen (file, "r");
221
222 struct stat s;
223
224 if (fp == NULL)
225 {
226 ui->perror(file);
227 return 1;
228 }
229
230 if (stat(file, &s))
231 {
232 ui->perror(file);
233 return 1;
234 }
235
236 uint64_t fsize = s.st_size;
237 ui->verbose("loading %s, base addr 0x%016llx, size 0x%llx\n", file, addr, fsize);
238
239
240 // check if bin file could fit into the allocated memory
241 if (addr+fsize > get_size())
242 {
243 ui->error("MEM: ram_size = 0x%llx, cannot load 0x%llx bytes \n", get_size(), fsize);
244 return 1;
245 }
246
247 uint8_t *start_addr = (uint8_t *)(mem + ofs(addr));
248 if(fread(start_addr, sizeof(uint8_t), fsize, fp) == 0)
249 {
250 ui->error("MEM: cannot read file %s \n", file);
251 return 1;
252 }
253
254 fclose(fp);
255
256 return 0;
257}
258
259
260SMemory::~SMemory()/*{{{*/
261{
262 if (mem && (mem != MAP_FAILED))
263 {
264 munmap((char*)mem,size);
265 }
266
267 if (mfile > 0)
268 {
269 close ( mfile );
270 remove( mem_file );
271 }
272
273 if (mem_file)
274 {
275 free ( mem_file );
276 }
277}
278/*}}}*/
279
280
281///////////////////////////////////////////////////////////////
282void SMemory::handle_out_of_range ( uint64_t addr )
283{
284 ui->error("memory: address %llx exceeds mem range %llx, redirect access to address 0; bus error was not signaled \n", addr, size);
285 exit(1);
286}
287
288#elif defined(MEMORY_SPARSE)
289
290
291
292SMemory::SMemory
293(
294 uint64_t ram_size,
295 uint_t pa_bits,
296 uint_t _l1bits,
297 uint_t _l2bits,
298 uint_t _l3bits
299)/*{{{*/
300 :
301 l1(0),
302 l1bits(_l1bits),
303 l2bits(_l2bits),
304 l3bits(_l3bits),
305 l1shft((l2bits + l3bits) - 3),// Compensate for pointer indexing, as I don't trust compiler
306 l2shft(l3bits - 3), // for doing a good job on the index computation code
307 l1size((1 << l1bits) << 3),
308 l2size((1 << l2bits) << 3),
309 l3size( 1 << l3bits),
310 l1mask(((1 << l1bits) - 1) << 3),
311 l2mask(((1 << l2bits) - 1) << 3),
312 l3mask(((1 << l3bits) - 1))
313{
314 assert(l3bits >= 13);
315
316 size = ram_size;
317 pa_mask = (uint64_t(1)<<pa_bits)-1;
318
319 memset(uninit_page,0,512);
320
321 l1 = (uint8_t***)calloc(l1size, sizeof(uint8_t));
322
323 for (int i = 0; i < SAM_NMEM_LOCKS; i++)
324 mutex_init(&locks[i], USYNC_THREAD, NULL);
325
326
327 mutex_init(&l2_lock, USYNC_THREAD, NULL);
328 mutex_init(&l3_lock, USYNC_THREAD, NULL);
329
330 mlist = 0;
331
332 DR_register ((char*)"mm1", mem_dump, mem_restore, (void*)this);
333 UI_register_cmd_2 ((char*)"mem", mem_help_string, mem_cmd_action, NULL);
334 UI_register_cmd_2 ((char*)"memdump", memdump_help_string, memdump_cmd_action, NULL);
335}
336
337
338SMemory::~SMemory()/*{{{*/
339{
340 // memory object is constructed only once and
341 // cannot be reconfigured during the run;
342 // rely on the system to free up all allocated memory on exit();
343 return;
344
345
346 /* normally we would need to
347 * remove sparse mem tables
348 */
349
350#if 0
351 for (int i1=0; i1 < (1 << l1bits); i1++)
352 {
353 uint8_t** l2 = l1[i1];
354 if (l2)
355 {
356 for (int i2=0; i2 < (1 <<l2bits); i2++)
357 {
358 uint8_t* l3= l2[i2];
359 if (l3)
360 {
361 // it maybe mmaped to a file;
362 // if some random value is passed to free(),
363 // the results are undefined.
364 free(l3);
365 }
366 }
367 free(l2);
368 }
369 }
370 free(l1);
371
372 // remove mmaped files
373 while(mlist)
374 {
375 MappedFileEntry *next = mlist->next;
376 delete(mlist);
377 mlist = next;
378 }
379#endif
380}
381
382
383
384
385
386
387////////////////////////////////////////////////////////////////
388//
389// mem block copy
390//
391int SMemory::block_read(uint64_t addr, uint8_t *tgt, int _size)
392{
393 //for (int i=0; i<_size; i++) tgt[i] = ld8u(addr+i);
394
395 while(_size)
396 {
397 uint8_t* paddr = get_st_ptr (addr); // page allocate
398 uint64_t offset = addr & l3mask;
399 uint64_t pagebytes = l3size - offset;
400
401 uint64_t nb = (_size > pagebytes) ? pagebytes : _size;
402 memcpy(tgt, paddr, (size_t)nb);
403 _size -= (int) nb;
404 addr += nb;
405 tgt += nb;
406 } // while more bytes to copy
407
408 return 0;
409}
410
411
412int SMemory::block_write(uint64_t addr, const uint8_t *src, int _size)
413{
414 //for (int i=0; i<_size; i++) st8(addr+i, src[i]);
415
416
417 while(_size)
418 {
419 uint8_t* paddr = get_st_ptr (addr);
420 uint64_t offset = addr & l3mask;
421 uint64_t pagebytes = l3size - offset;
422
423 uint64_t nb = (_size > pagebytes) ? pagebytes : _size;
424 memcpy(paddr, src, (size_t)nb);
425 _size -= (int) nb;
426 addr += nb;
427 src += nb;
428
429 } // while more bytes to copy
430
431
432 return 0;
433}
434
435
436
437
438
439// construct memory mapped file entry
440MappedFileEntry::MappedFileEntry
441(
442 const char *file_name, // name of the file
443 uint64_t saddr // starting address
444)
445:
446name (0),
447mfile(-1),
448mem (0),
449size (0),
450next (0)
451{
452 addr = saddr; // keep starting address
453
454 // check file name
455 if (file_name[0] == '/')
456 {
457 // this is an absolute path
458 name = strdup(file_name);
459 }
460 else // file is in working directory
461 {
462 char path[PATH_MAX];
463
464 if (getwd(path) == NULL)
465 {
466 ui->error("memory: cannot get current working directory\n");
467 return;
468 }
469
470 // keep absolute file name
471 name = (char *)calloc(strlen(path)+strlen(file_name)+16, sizeof(char));
472 sprintf(name,"%s/%s", path, file_name);
473 }
474
475 // check file size
476 struct stat s;
477 if (stat(name, &s))
478 {
479 ui->perror(name);
480 return;
481 }
482 size = s.st_size;
483
484 ui->verbose("loading %s, base addr 0x%016llx, size 0x%llx\n", name, addr, size);
485
486 // open the file
487 mfile = open ( name, O_RDONLY|O_LARGEFILE );
488
489 if (mfile < 0)
490 {
491 ui->error("memory: cannot open %s, %s\n",name, strerror(errno));
492 return;
493 }
494
495 // map file as a private - all changes will go to pages allocated in swap space
496 int prot = PROT_READ|PROT_WRITE;
497 int flags = MAP_ALIGN|MAP_PRIVATE;
498
499 if ( !SYSTEM_get_memreserve() )
500 {
501 // do not reserve swap space before the run
502 ui->warning("You might run out of swap space with -nomemreserve option \n");
503 flags |= MAP_NORESERVE;
504 }
505
506 mem = (uint8_t*)mmap((char*)0,size, prot, flags, mfile, 0);
507
508}
509
510MappedFileEntry::~MappedFileEntry()
511{
512 if (mem && (mem != MAP_FAILED))
513 {
514 munmap((char*)mem,size);
515 }
516
517 if (mfile > 0)
518 {
519 close ( mfile );
520 }
521
522 if (name)
523 {
524 free (name);
525 }
526}
527
528// load bin format
529int SMemory::load_bin( const char *file, uint64_t addr)/*{{{*/
530{
531 MappedFileEntry *mm_entry = new MappedFileEntry(file, addr);
532
533 if (!mm_entry->is_valid() || !this->map(mm_entry))
534 {
535 ui->error("cannot load %s, base addr 0x%016llx\n", file, addr);
536 delete mm_entry;
537 return 1;
538 }
539
540 link(mm_entry);
541
542 return 0;
543}
544
545// map page from the mmaped file
546int SMemory::map_page
547(
548 uint64_t paddr, // page address
549 uint8_t *maddr // page address in the mmapped file
550)
551{
552 // look up the entry in the tables where paddr suppose to reside
553 uint8_t*** o1 = (uint8_t***)((char*)l1 + ((paddr >> l1shft) & l1mask) );
554 uint8_t** l2 = *o1;
555
556 if (l2 == 0)
557 {
558 l2 = *o1 = (uint8_t**)calloc(l2size,sizeof(uint8_t));
559 }
560 if (l2==0)
561 {
562 ui->error("\nMEM: Run out of memory, exit...\n");
563 exit(1);
564 }
565
566 uint8_t** o2 = (uint8_t**)((char*)l2 + ((paddr >> l2shft) & l2mask));
567 uint8_t* l3 = *o2;
568
569 if (l3 == 0) // map the page
570 {
571 *o2 = maddr;
572 }
573 else // page is already allocated
574 {
575 return 0;
576 }
577
578 return 1;
579}
580
581
582// map sparse mem pages to mem mapped file
583int SMemory::map
584(
585 MappedFileEntry *entry
586)
587{
588 // init counters and pointers
589 int npages = 0; // page counter
590 uint64_t start_addr = entry->addr;
591 uint64_t addr = start_addr;
592 uint64_t fsize = entry->size;
593 uint8_t *faddr = entry->mem;
594 off_t foff = 0;
595
596 while ( fsize > 0 )
597 {
598 uint64_t offset = addr & l3mask;
599 uint64_t pagebytes = l3size - offset;
600
601 // number of bytes that should be on the same sparse mem page
602 uint64_t nb = (fsize > pagebytes) ? pagebytes : fsize;
603
604 // if this address range doesn't fit exactly to the sparse mem page
605 // and can't be memory mapped (because a previous file as been
606 // mapped to this range), just allocate space with get_st_ptr and
607 // read from the file.
608 if (nb != l3size || !map_page(addr, faddr))
609 {
610 // make a copy of this page
611 uint8_t* paddr = get_st_ptr (addr);
612 // seek to the beginning of the "page" to copy
613 if (lseek(entry->mfile, foff, SEEK_SET) == -1)
614 {
615 ui->error("\nMEM: can't seek to 0x%llx for file %s\n",
616 foff, entry->name);
617 exit(1);
618 }
619
620 if (read(entry->mfile, paddr, nb) != nb)
621 {
622 ui->error("\nMEM: can't read 0x%llx bytes from file %s\n",
623 nb, entry->name);
624 exit(1);
625 }
626 }
627
628 fsize -= nb;
629 addr += nb;
630 faddr += nb;
631 foff += nb;
632 } // while something left in the file
633
634 return 1;
635
636}
637
638#endif // end MEMORY_SPARSE
639
640
641
642enum { BUFFER_SIZE = 512 };
643static char buffer[BUFFER_SIZE];
644
645// load image format;
646// use st64_nl, common for all mem models
647int SMemory::load( const char* mem_image_filename )/*{{{*/
648{
649 const char* separ = " \t\n";
650 FILE* image = fopen(mem_image_filename,"r");
651 uint64_t addr = 0;
652
653
654 if (!image)
655 {
656 ui->error("MEM: No such mem image file found.");
657 return 1;
658 }
659
660 while (fgets(buffer,BUFFER_SIZE,image))
661 {
662 if (buffer[0] == '@')
663 {
664 addr = strtoull(buffer+1,0,16);
665 }
666 else if (isxdigit(buffer[0]))
667 {
668 char* token = strtok(buffer,separ);
669 uint64_t length = strlen(token);
670
671 if (length == 16)
672 {
673 do
674 {
675 uint64_t data = strtoull(token,0,16);
676 st64_nl(addr,data);
677 addr += 8;
678 }
679 while ((token = strtok(0,separ)));
680 }
681 else if (length == 8)
682 {
683 do
684 {
685 uint32_t data = uint32_t(strtoul(token,0,16));
686 st32(addr,data);
687 addr += 4;
688 }
689 while ((token = strtok(0,separ)));
690 }
691 else if ((length == 1) && buffer[0] == '0')
692 {
693 // skip empty line
694 continue;
695 }
696 else
697 {
698 ui->error("A memory entry must be either 4 or 8 bytes.");
699 return 1;
700 }
701 }
702 }
703
704 fclose(image);
705 return 0;
706}
707/*}}}*/
708
709
710
711
712void SMemory::save( const char* filename, uint64_t addr, uint64_t _size )/*{{{*/
713{
714 const int PAGE_SIZE = 1 << 13;
715 const int PAGE_MASK = PAGE_SIZE - 1;
716
717 FILE* image = fopen(filename,"w");
718
719 if (!image)
720 {
721 ui->perror(filename);
722 return;
723 }
724
725 uint8_t *buf8 = (uint8_t *)malloc(PAGE_SIZE);
726
727 if ((addr & PAGE_MASK) != 0)
728 {
729 uint64_t n = PAGE_SIZE - (addr & PAGE_MASK);
730 if (_size < n)
731 n = _size;
732
733 block_read(addr, buf8, int(n));
734 fwrite(buf8,1,n,image);
735
736 addr += n;
737 _size -= n;
738 }
739 while (_size >= PAGE_SIZE)
740 {
741 block_read(addr, buf8, PAGE_SIZE);
742 fwrite(buf8,1,PAGE_SIZE,image);
743 addr += PAGE_SIZE;
744 _size -= PAGE_SIZE;
745 }
746 if (_size)
747 {
748 block_read(addr, buf8, _size);
749 fwrite(buf8,1,_size,image);
750 }
751
752 free(buf8);
753 fclose(image);
754}
755
756
757//////////////////////////////////////////////////
758///
759/// Memory object DUMP/RESTORE
760///
761//////////////////////////////////////////////////
762
763#define DUMPSIZE 1024
764#define DISPLAY_SIZE (0x20000000LLU) // 512M
765
766#define THRESHOLD_512M (0x20000000LLU)
767#define THRESHOLD_4G (0x100000000LLU)
768#define THRESHOLD_16G (0x400000000LLU)
769
770#define MASK_32M (0x1ffffffLLU)
771#define MASK_128M (0x7ffffffLLU)
772#define MASK_1G (0x3fffffffLLU)
773#define MASK_4G (0xffffffffLLU)
774
775#define MAX_ADDR (0xFFFFFFFFFFFFFFFFLLU)
776
777extern void dump_uint32 (FILE * fp, uint32_t val);
778extern void dump_uint64 (FILE * fp, uint64_t val);
779extern uint64_t restore_uint64 (FILE * fp);
780
781
782////////////////////////////////////////////////////////
783//
784// restore mem from dump file
785//
786
787
788
789int SMemory::restore ( char *dir )
790{
791 if (DR_restore_object (dir, (char*)"mm1"))
792 return 1;
793
794 return 0;
795}
796
797
798bool_t mem_dump (DR_OPAQUE pdr)
799{
800 char *name = DR_get_name (pdr);
801 char *dir = DR_get_dir ();
802 SMemory *msp = (SMemory*) DR_get_client_data (pdr);
803
804 return msp->dump(dir, name);
805}
806
807
808
809#if defined(MEMORY_FLAT)
810
811///////////////////////////////////////////////////
812
813
814int SMemory::dump ( char *dir, char *name)
815{
816 FILE *fp;
817
818 uint64_t i,j;
819 uint64_t npages = this->get_size()/DUMPSIZE;
820 uint64_t mask;
821 uint64_t cur_addr = 0;
822 uint64_t dpages = 0;
823
824 char fname[PATH_MAX];
825 sprintf (fname, "%s/%s.dmp.img", dir, name);
826
827 // set to false if older mm1.dmp (v4) format is needed
828 bool img_format = true;
829
830 if ((fp = fopen (fname, "w")) == NULL)
831 {
832 ui->perror(fname);
833 return 0;
834 }
835
836 // depending on memsize mask defines progress update frequency
837 if (this->get_size() <= THRESHOLD_512M) {
838 mask = MASK_32M;
839 }
840 else if (this->get_size() <= THRESHOLD_4G) {
841 mask = MASK_128M;
842 }
843 else if (this->get_size() <= THRESHOLD_16G) {
844 mask = MASK_1G;
845 }
846 else {
847 mask = MASK_4G;
848 }
849
850 ui->output("MEM: ram_size = 0x%llx (%lld MB), pages = 0x%lld\n",
851 get_size(), get_size()>>20, npages);
852
853 for (i = 0; i < npages; i++)
854 {
855 bool_t clean = 1;
856
857 cur_addr += DUMPSIZE;
858
859 // show the progress
860 if ((cur_addr & mask) == 0)
861 {
862 ui->output(".");
863 ui->flush();
864 }
865
866 uint64_t paddr = i*DUMPSIZE;
867 uint8_t *mem_page = get_base() + paddr;
868
869 // check if page is modified - has non-zero values;
870 // sparse mem files always read 0 from address that
871 // was never written, by default;
872 for (j = 0; j < DUMPSIZE; j = j + 8)
873 {
874 if (*(uint64_t *) (mem_page + j) != 0) {
875 clean = 0;
876 break;
877 }
878 }
879
880 if (!clean) // dump modified page
881 {
882 if (img_format)
883 {
884 // write the page using sparse file
885 if ( fseek(fp, paddr, SEEK_SET) < 0 )
886 {
887 ui->error("MEM: cannot set page addr 0x%llx, %s \n",
888 paddr, strerror(errno));
889 return 0;
890 }
891 }
892 else // old dmp format
893 {
894 dump_uint64(fp, i); // page number
895 }
896
897 if ( fwrite (mem_page, DUMPSIZE, 1, fp) != 1 )
898 {
899 ui->perror("DUMP (mm1)");
900 return 0;
901 }
902 dpages++;
903 }
904
905 }
906
907 if (img_format)
908 {
909 // make sure that the very last byte is written
910 // to set correct file size
911 uint64_t last_byte = get_size() - 1;
912 fseek(fp, last_byte, SEEK_SET);
913
914 uint8_t *plast_byte = get_base() + last_byte;
915 if ( fwrite (plast_byte, 1, 1, fp) != 1 )
916 {
917 ui->perror("DUMP (mm1)");
918 return 0;
919 }
920
921
922 }
923 else
924 {
925 i = MAX_ADDR;
926 dump_uint64(fp, i);
927 }
928
929 fclose (fp);
930 ui->output("MEM: dumped 0x%llx (modified 0x%llx) pages \n", npages, dpages);
931 return 1;
932} // mem_dump()
933
934/////////////////////////////////////////////
935//
936// restore command
937//
938bool_t mem_restore (DR_OPAQUE pdr)
939{
940 char *name = DR_get_name (pdr);
941 char *dir = DR_get_rdir (pdr);
942 SMemory *msp = (SMemory*) DR_get_client_data (pdr);
943
944 FILE *fp;
945
946
947 uint64_t npages = msp->get_size()/DUMPSIZE;
948 uint64_t zpages = 0;
949 uint64_t page_no;
950 char *mem;
951
952
953 char fname[PATH_MAX];
954 sprintf (fname, "%s/%s.dmp.img", dir, name);
955
956 if ( access(fname, R_OK) ==0 )
957 {
958 // restore mem image checkpoint;
959 // map it as a private file
960 if (msp->init(fname, 1))
961 {
962 ui->output("MEM: restored 0x%llx bytes \n", msp->get_size());
963 return 1;
964 }
965 return 0;
966 }
967
968
969 // restore from mm1.dmp file
970 if( !msp->init() )
971 {
972 ui->error("MEM: cannot allocate enough space \n");
973 return 0;
974 }
975
976 // mem dmp checkpoint
977 sprintf (fname, "%s/%s.dmp", dir, name);
978 if ((fp = fopen (fname, "r")) == NULL) {
979 ui->perror(fname);
980 return 0;
981 }
982
983 fseek(fp, 0, SEEK_END);
984 size_t fsize = ftell(fp);
985 fseek(fp, 0, SEEK_SET);
986 int ztotal = (fsize/DUMPSIZE);
987
988 ui->output("(dump size %lld MB):\n", (uint64_t)(fsize>>20));;
989 int pcttarget = 0;
990
991 // copy non-zero pages to mem file
992 while (1) {
993
994 uint64_t offset;
995 page_no = restore_uint64(fp);
996
997 if (page_no == MAX_ADDR) {
998 break;
999 }
1000
1001 offset = page_no*DUMPSIZE;
1002
1003 if (offset > (msp->get_size() - DUMPSIZE)) {
1004 ui->output("MEM: restore to 0x%llx is beyond memory ram_size (0x%llx), discarding...\n",
1005 offset, msp->get_size());
1006 return 0;
1007 }
1008 else {
1009 mem = (char*) (msp->get_base()+offset);
1010 }
1011 if (fread(mem, DUMPSIZE, 1, fp) != 1) {
1012 ui->output("MEM: restore : restore state failed\n");
1013 return 0;
1014 }
1015 zpages++;
1016
1017 int pct = (100.0*zpages/ztotal);
1018 if (pct >= pcttarget) {
1019 ui->output("%d%% ", pct);
1020 ui->flush();
1021 pcttarget = pct+2;
1022 }
1023 }
1024
1025 ui->output("MEM: restored 0x%llx pages \n", zpages);
1026 return 1;
1027}
1028
1029#elif defined(MEMORY_SPARSE)
1030
1031// dump sparse mem pages
1032int SMemory::dump ( char *dir, char* name)
1033{
1034 FILE *fp, *fidx;
1035 char fname[PATH_MAX], iname[PATH_MAX];
1036 sprintf (fname, "%s/%s.dmp.img", dir, name);
1037 sprintf (iname, "%s/%s.dmp.idx", dir, name);
1038
1039
1040 ui->output("\n");
1041 if ((fp = fopen (fname, "w")) == NULL)
1042 {
1043 ui->perror(fname);
1044 return 0;
1045 }
1046
1047 if ((fidx = fopen (iname, "w")) == NULL)
1048 {
1049 ui->perror(iname);
1050 return 0;
1051 }
1052
1053 // version number
1054 dump_uint64(fidx, SAM_MEM_DUMP_VERSION);
1055
1056 // dump sparse mem table sizes
1057 dump_uint64(fidx, l1size);
1058 dump_uint64(fidx, l2size);
1059 dump_uint64(fidx, l3size);
1060
1061 uint64_t npages = 0;
1062 uint64_t dpages = 0;
1063 uint64_t foffset = 0;
1064
1065 uint64_t next_page_addr = MAX_ADDR;
1066 uint64_t block_addr = 0;
1067 uint64_t block_offs = 0;
1068 uint64_t block_size = l3size;
1069 uint64_t nblocks = 0;
1070 uint64_t nidx = 0;
1071
1072 for (uint64_t i1=0; i1 < (1 << l1bits); i1++)
1073 {
1074 uint8_t** l2 = l1[i1];
1075 if (l2)
1076 {
1077 for (uint64_t i2=0; i2 < (1 <<l2bits); i2++)
1078 {
1079 uint64_t page_addr = (i1 << (l2bits + l3bits)) | (i2 << l3bits);
1080
1081 uint8_t* l3= l2[i2];
1082 if (l3) // dump this page
1083 {
1084 if ( page_addr == next_page_addr )
1085 {
1086 // it is a next page in the block
1087 block_size += l3size;
1088 }
1089 else // start a new block
1090 {
1091 if (next_page_addr != MAX_ADDR)
1092 {
1093 // current block address,offset, size
1094 dump_uint64(fidx, block_addr);
1095 dump_uint64(fidx, block_offs);
1096 dump_uint64(fidx, block_size);
1097 nidx++;
1098 }
1099
1100 // start a new block
1101 block_addr = page_addr;
1102 block_offs = foffset;
1103 block_size = l3size;
1104 next_page_addr = page_addr;
1105 nblocks++;
1106 }
1107
1108 if (is_dirty(l3)) dpages++;
1109
1110 // clear dirty bit
1111 l3 = mask_dirty(l3);
1112 l2[i2] = l3;
1113
1114 // dump mem page
1115 if (fwrite ( (char*)l3, l3size, 1, fp ) != 1)
1116 {
1117 ui->perror("DUMP (mm1)");
1118 return 0;
1119 }
1120
1121 foffset += l3size;
1122 next_page_addr += l3size;
1123
1124 npages++; // number of pages
1125
1126 // display progress
1127 if (npages % 32 == 0) {
1128 // erase previous value using back-space chars
1129 ui->output("%-5d MB\r",(npages << (l3bits-20)) );
1130 }
1131
1132 } // if (l3)
1133 }
1134 }
1135 }
1136
1137 // last page
1138 if (nblocks != nidx)
1139 {
1140 dump_uint64(fidx, block_addr);
1141 dump_uint64(fidx, block_offs);
1142 dump_uint64(fidx, block_size);
1143 }
1144
1145 // delimiter
1146 dump_uint64(fidx,MAX_ADDR);
1147 dump_uint64(fidx,nblocks);
1148 dump_uint64(fidx,npages);
1149
1150 fclose (fp);
1151 fclose (fidx);
1152
1153 ui->output("\nMEM: dumped %lld pages (%lld dirty), %lld blocks \n", npages, dpages, nblocks);
1154 return 1;
1155
1156}
1157
1158// restore from a checkpoint which has
1159// addresses and data are in one file
1160uint64_t restore_mem_checkpoint
1161(
1162 SMemory *msp,
1163 MappedFileEntry *entry
1164)
1165{
1166
1167 // we are here because we didn't find an index file
1168 // if this is a blaze dump, it has to be a flat-memory dump
1169 // otherwise, it has to be a vonk sparse memory dump with addresses & data in one file
1170
1171 // find the first vcpu and get its vcpu type
1172 int cpuid;
1173 Vcpu * first_vcpu = NULL;
1174 for (cpuid=0; cpuid<=g_vcpu_id_max; cpuid++) {
1175 first_vcpu = get_vcpu(cpuid);
1176 if (first_vcpu != NULL) break;
1177 }
1178
1179 if ((first_vcpu->config.cpu_type & VCPU_IMPL_SIM_MASK) == VCPU_IMPL_SIM_BLAZE ) {
1180 ui->output("MEM: mapping in blaze flat-mem dump into sparse-memory model");
1181 msp->map(entry);
1182 return 0;
1183 }
1184
1185 // read sparse mem table sizes
1186 uint64_t r_l1size; read(entry->mfile, &r_l1size, 8);
1187 uint64_t r_l2size; read(entry->mfile, &r_l2size, 8);
1188 uint64_t r_l3size; read(entry->mfile, &r_l3size, 8);
1189
1190 // restored page counter
1191 uint64_t zpages = 0;
1192
1193 // check mem configuration
1194 if (r_l1size != msp->get_l1size() ||
1195 r_l2size != msp->get_l2size() ||
1196 r_l3size != msp->get_l3size() )
1197 {
1198 ui->output("MEM: restore : sparse mem configuration does not match\n");
1199 return 0;
1200 }
1201
1202 // add three table sizes and one page address 8 bytes each = 32
1203 uint8_t *faddr = entry->mem + 32;
1204
1205 while (1)
1206 {
1207 // read page address
1208 uint64_t page_addr; read(entry->mfile, &page_addr, 8);
1209
1210 if (page_addr == MAX_ADDR)
1211 break; // last page was restored
1212
1213
1214 // only l3size pages are written to the file
1215 if (msp->map_page(page_addr, faddr))
1216 {
1217 // page is mapped to the mem file, adjust the file pointer
1218 if (lseek(entry->mfile, r_l3size, SEEK_CUR) < 0)
1219 {
1220 ui->error("MEM: cannot set file pointer, %s \n", strerror(errno));
1221 return 0;
1222 }
1223 }
1224 else
1225 {
1226 // allocate a new mem page
1227 uint8_t* mem_page = msp->get_st_ptr( page_addr );
1228
1229 // read the page from the file
1230 uint64_t restore = read(entry->mfile, mem_page, r_l3size);
1231 if (restore != r_l3size)
1232 {
1233 ui->output("MEM: read 0x%llx bytes from 0x%llx block : restore failed\n", restore, r_l3size);
1234 return 0;
1235 }
1236 }
1237
1238 faddr += (r_l3size+8); // adjust for page address 8 bytes
1239
1240 zpages++;
1241 }
1242 return zpages;
1243}
1244
1245
1246// restore mem from the file;
1247// mem pages are set to point to mmaped file;
1248// use lseek() to find page addresses;
1249// speed of mem restore could be improved if addresses are
1250// stored in a serate file
1251bool_t mem_restore (DR_OPAQUE pdr)
1252{
1253 char *name = DR_get_name (pdr);
1254 char *dir = DR_get_rdir (pdr);
1255 char fname[PATH_MAX];
1256 char iname[PATH_MAX];
1257 char dmpfname[PATH_MAX];
1258 struct stat statbuf;
1259
1260 sprintf (fname, "%s/%s.dmp.img", dir, name);
1261 sprintf (iname, "%s/%s.dmp.idx", dir, name);
1262
1263 SMemory *msp = (SMemory*) DR_get_client_data (pdr);
1264
1265 MappedFileEntry *mm_entry = new MappedFileEntry(fname);
1266 if (!mm_entry->is_valid())
1267 {
1268 ui->error("mem_restore: cannot load ");
1269 ui->perror(fname);
1270 if (errno == ENOENT) {
1271 // check if a blaze-v4 style mm1.dmp file exists
1272 sprintf(dmpfname, "%s/%s.dmp", dir, name);
1273 if (stat(dmpfname, &statbuf) == 0) {
1274 ui->warning(
1275"This dump contains an old, unsupported version of the\n"
1276"memory dump file (%s). Please contact the owner of this\n"
1277"checkpoint to update this file.\n\n"
1278"If you are the owner, you can update this file using:\n\n"
1279" memdump2image -i %s -o %s\n\n",
1280 dmpfname, dmpfname, fname);
1281 } // if mm1.dmp exists
1282
1283 }
1284 delete mm_entry;
1285 return 0;
1286 }
1287
1288 // add the file to mmapped list
1289 msp->link(mm_entry);
1290
1291 // restored page counter
1292 uint64_t zpages = 0;
1293 uint64_t nblocks = 0;
1294
1295 // open index file
1296 int fidx = open ( iname, O_RDONLY|O_LARGEFILE );
1297
1298 if (fidx < 0)
1299 {
1300 ui->error("MEM: cannot open %s, this is an older checkpoint version.\n",iname);
1301 zpages = restore_mem_checkpoint(msp, mm_entry);
1302 }
1303 else
1304 {
1305 // read sparse mem table sizes
1306 uint64_t r_ver; read(fidx, &r_ver, 8);
1307 uint64_t r_l1size; read(fidx, &r_l1size, 8);
1308 uint64_t r_l2size; read(fidx, &r_l2size, 8);
1309 uint64_t r_l3size; read(fidx, &r_l3size, 8);
1310
1311
1312
1313 // check mem configuration
1314 if (r_l1size != msp->get_l1size() ||
1315 r_l2size != msp->get_l2size() ||
1316 r_l3size != msp->get_l3size() )
1317 {
1318 ui->error("MEM: restore : sparse mem configuration does not match\n");
1319 return 0;
1320 }
1321
1322 while (1)
1323 {
1324 // read page address, size, offset in the file
1325 uint64_t block_addr; read(fidx, &block_addr, 8);
1326 uint64_t block_offs; read(fidx, &block_offs, 8);
1327 uint64_t block_size; read(fidx, &block_size, 8);
1328
1329 if (block_addr == MAX_ADDR)
1330 {
1331 // check counters
1332 if ( nblocks != block_offs || zpages != block_size )
1333 {
1334 ui->error("MEM: read 0x%llx blocks, 0x%llx pages - there should be 0x%llx blocks, 0x%llx pages : restore failed\n",
1335 nblocks, zpages, block_offs, block_size);
1336 return 0;
1337 }
1338
1339 break; // last page was restored
1340 }
1341
1342 nblocks++;
1343
1344 uint64_t page_addr = block_addr;
1345 uint64_t page_offs = block_offs;
1346
1347 while(block_size>0)
1348 {
1349 // try to map page to the file
1350 if ( ! msp->map_page(page_addr, mm_entry->mem + page_offs) )
1351 {
1352 // cannot mmap, something is already there - need to do a copy
1353
1354 // get mem page pointer
1355 uint8_t* mem_page = msp->get_st_ptr( page_addr );
1356
1357 // adjust the file pointer
1358 if (lseek(mm_entry->mfile, page_offs, SEEK_SET) < 0)
1359 {
1360 ui->error("MEM: cannot set file pointer, %s \n", strerror(errno));
1361 return 0;
1362 }
1363
1364 // read the page from the file
1365 uint64_t restore = read(mm_entry->mfile, mem_page, r_l3size);
1366 if (restore != r_l3size)
1367 {
1368 ui->error("MEM: read 0x%llx bytes from 0x%llx block : restore failed\n", restore, r_l3size);
1369 return 0;
1370 }
1371 }
1372
1373 page_addr += r_l3size;
1374 page_offs += r_l3size;
1375 block_size -= r_l3size;
1376 zpages++;
1377 }
1378 }
1379 close(fidx);
1380 }
1381
1382 ui->output("MEM: restored 0x%llx pages, 0x%llx blocks \n", zpages, nblocks );
1383 return 1;
1384}
1385
1386
1387#endif // MEMORY_SPARSE
1388
1389/////////////////////////////////////////////
1390//
1391// ui mem command
1392//
1393#include "cpu_interface.h"
1394extern SMemory *mm1;
1395
1396void print_mem_cmd_usage()
1397{
1398 ui->output("%s", mem_help_string);
1399}
1400
1401
1402static int mem_cmd_action (void *, int argc, char **argv)
1403{
1404 uint64_t saddr=0;
1405 uint64_t eaddr=0;
1406 uint64_t size=16;
1407 int i;
1408
1409 int is_va = 0;
1410 int is_dis = 0;
1411 int cpuid = 0;
1412
1413
1414 if (mm1 == NULL)
1415 {
1416 ui->output("MEM: RAM is not allocated yet \n");
1417 return 0;
1418 }
1419 if (argc < 2)
1420 {
1421 print_mem_cmd_usage();
1422 return 1;
1423 }
1424
1425
1426 for (i=1; i<argc; i++)
1427 {
1428 if (strcmp(argv[i], "-dis")==0)
1429 {
1430 is_dis = 1;
1431 ui->output( " disassemble");
1432 continue;
1433 }
1434 else if (strcmp(argv[i], "-va" )==0)
1435 {
1436 is_va = 1;
1437 ui->output( " addr is virtual");
1438 continue;
1439 }
1440 else if ((strcmp(argv[i], "-cpu")==0) && ((i+1)<argc))
1441 {
1442 cpuid = int(strtol(argv[++i], NULL, 0));
1443
1444 Vcpu *vcpu = get_vcpu(cpuid);
1445 if (!vcpu)
1446 {
1447 ui->output( "cpu id %i is unknown \n",cpuid);
1448 return 0;
1449 }
1450 ui->output( " for cpu[%i]",cpuid);
1451 continue;
1452 }
1453 else if ((strcmp(argv[i], "-a")==0) && ((i+1)<argc))
1454 {
1455 saddr = strtoull(argv[++i], NULL, 0);
1456 saddr &= ~7LLU;
1457 eaddr = saddr + 16;
1458 continue;
1459 }
1460 else if ((strcmp(argv[i], "-s")==0) && ((i+1)<argc))
1461 {
1462 size = strtoull(argv[++i], NULL, 0);
1463 continue;
1464 }
1465 else if (strcmp(argv[i], "?" )==0)
1466 {
1467 print_mem_cmd_usage();
1468 return 1;
1469 }
1470 }
1471
1472
1473
1474 eaddr = saddr + size;
1475
1476 ui->output( " saddr=0x%llx eaddr=0x%llx\n",saddr, eaddr);
1477
1478
1479 uint64_t baddr = saddr;
1480 for (;saddr <= eaddr; saddr)
1481 {
1482 uint64_t v[4];
1483 for (i = 0; i < 4; i++, saddr += 8)
1484 {
1485 #if defined(MEMORY_FLAT)
1486 if (saddr >= mm1->get_size()-8)
1487 {
1488 ui->output( "MEM: phys address 0x%llx is beyond RAM \n", saddr);
1489 return 1;
1490 }
1491 #endif
1492
1493 if (is_va)
1494 {
1495 uint64_t val = 0;
1496 if(g_vcpu[cpuid]->read_mem(saddr, &val, 8) != 0)
1497 {
1498 ui->output( "MEM: cannot translate virtual address \n", saddr);
1499 return 1;
1500 }
1501 else
1502 {
1503 v[i] = val;
1504 }
1505 }
1506 else
1507 {
1508 v[i] = mm1->SMemory::ld64(saddr);
1509 }
1510
1511
1512 if (is_dis)
1513 {
1514 const int LSIZE = 128;
1515 char iline[LSIZE];
1516 uint32_t opc = uint32_t(v[i]>>32);
1517 disassemble(opc, saddr , iline, LSIZE);
1518 ui->output ("%i : 0x%llx: 0x%lx : %s \n", cpuid, saddr, opc, iline);
1519 opc = uint32_t(v[i] & 0xffffffff);
1520 disassemble(opc, saddr+4, iline, LSIZE);
1521 ui->output ("%i : 0x%llx: 0x%lx : %s \n", cpuid, saddr+4, opc, iline);
1522 }
1523 else
1524 {
1525 if ( i==0 ) ui->output ("0x%llx:", baddr);
1526 ui->output (" 0x%016llx", v[i] );
1527 if ( i==3 ) ui->output ("\n");
1528 }
1529
1530 }
1531
1532 baddr = saddr;
1533 }
1534
1535 return 0;
1536
1537} // mem_cmd_action()
1538
1539
1540
1541/////////////////////////////////////////////
1542//
1543// ui memdump command
1544//
1545void print_memdump_cmd_usage()
1546{
1547 ui->error ("Usage : memdump <filename> <start addr> <size> \n");
1548}
1549static int memdump_cmd_action (void *, int argc, char **argv)
1550{
1551 uint64_t saddr = ~0ull;
1552 uint64_t size = 0;
1553
1554 char *filename;
1555
1556 if (mm1 == NULL)
1557 {
1558 ui->error("MEM: RAM is not allocated yet \n");
1559 return 0;
1560 }
1561 if (argc < 4)
1562 {
1563 print_memdump_cmd_usage();
1564 return 1;
1565 }
1566
1567 filename = argv[1];
1568 saddr = strtoull(argv[2], NULL, 0);
1569 size = strtoull(argv[3], NULL, 0);
1570
1571 if (!saddr || !size)
1572#ifdef MEMORY_SPARSE
1573 if ((saddr == ~0ull) || (size==0))
1574#endif
1575
1576#ifdef MEMORY_FLAT
1577 if ((size==0) || (saddr > mm1->get_size()) || (saddr+size > mm1->get_size()))
1578#endif
1579
1580 {
1581 ui->error("memdump: incorrect start address or size \n");
1582 print_memdump_cmd_usage();
1583 return 1;
1584 }
1585
1586 ui->output("writing mem image to the file: %s, start address 0x%llx, size 0x%llx ... \n",
1587 filename, saddr, size);
1588 mm1->save(filename, saddr,size);
1589 ui->output("----- MEMDUMP COMPLETED -----\n");
1590
1591 return 0;
1592} // memdump_cmd_action()
1593