/[dynamips]/upstream/dynamips-0.2.7/ppc32.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/dynamips-0.2.7/ppc32.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 8 - (show annotations)
Sat Oct 6 16:24:54 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.7-RC2/ppc32.c
File MIME type: text/plain
File size: 15716 byte(s)
dynamips-0.2.7-RC2

1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4 *
5 * PowerPC (32-bit) generic routines.
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/mman.h>
15 #include <fcntl.h>
16 #include <assert.h>
17
18 #include "rbtree.h"
19 #include "cpu.h"
20 #include "dynamips.h"
21 #include "memory.h"
22 #include "device.h"
23 #include "ppc32_mem.h"
24 #include "ppc32_exec.h"
25 #include "ppc32_jit.h"
26
27 /* Reset a PowerPC CPU */
28 int ppc32_reset(cpu_ppc_t *cpu)
29 {
30 cpu->ia = PPC32_ROM_START;
31 cpu->gpr[1] = PPC32_ROM_SP;
32 cpu->msr = PPC32_MSR_IP;
33
34 /* Restart the MTS subsystem */
35 ppc32_mem_restart(cpu);
36
37 /* Flush JIT structures */
38 ppc32_jit_flush(cpu,0);
39 return(0);
40 }
41
42 /* Initialize a PowerPC processor */
43 int ppc32_init(cpu_ppc_t *cpu)
44 {
45 /* Initialize idle timer */
46 cpu->gen->idle_max = 1500;
47 cpu->gen->idle_sleep_time = 30000;
48
49 /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */
50 cpu->timer_irq_check_itv = 1000;
51 cpu->timer_irq_freq = 250;
52
53 /* Enable/disable direct block jump */
54 cpu->exec_blk_direct_jump = cpu->vm->exec_blk_direct_jump;
55
56 /* Idle loop mutex and condition */
57 pthread_mutex_init(&cpu->gen->idle_mutex,NULL);
58 pthread_cond_init(&cpu->gen->idle_cond,NULL);
59
60 /* Set the CPU methods */
61 cpu->gen->reg_set = (void *)ppc32_reg_set;
62 cpu->gen->reg_dump = (void *)ppc32_dump_regs;
63 cpu->gen->mmu_dump = (void *)ppc32_dump_mmu;
64 cpu->gen->mmu_raw_dump = (void *)ppc32_dump_mmu;
65 cpu->gen->add_breakpoint = (void *)ppc32_add_breakpoint;
66 cpu->gen->remove_breakpoint = (void *)ppc32_remove_breakpoint;
67 cpu->gen->set_idle_pc = (void *)ppc32_set_idle_pc;
68 cpu->gen->get_idling_pc = (void *)ppc32_get_idling_pc;
69
70 /* Set the startup parameters */
71 ppc32_reset(cpu);
72 return(0);
73 }
74
75 /* Delete a PowerPC processor */
76 void ppc32_delete(cpu_ppc_t *cpu)
77 {
78 if (cpu) {
79 ppc32_mem_shutdown(cpu);
80 ppc32_jit_shutdown(cpu);
81 }
82 }
83
84 /* Set the processor version register (PVR) */
85 void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr)
86 {
87 cpu->pvr = pvr;
88 ppc32_mem_restart(cpu);
89 }
90
91 /* Set idle PC value */
92 void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr)
93 {
94 CPU_PPC32(cpu)->idle_pc = (m_uint32_t)addr;
95 }
96
97 /* Timer IRQ */
98 void *ppc32_timer_irq_run(cpu_ppc_t *cpu)
99 {
100 pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER;
101 pthread_cond_t ucond = PTHREAD_COND_INITIALIZER;
102 struct timespec t_spc;
103 m_tmcnt_t expire;
104 u_int interval;
105 u_int threshold;
106
107 #if 0
108 while(!cpu->timer_irq_armed)
109 sleep(1);
110 #endif
111
112 interval = 1000000 / cpu->timer_irq_freq;
113 threshold = cpu->timer_irq_freq * 10;
114 expire = m_gettime_usec() + interval;
115
116 while(cpu->gen->state != CPU_STATE_HALTED) {
117 pthread_mutex_lock(&umutex);
118 t_spc.tv_sec = expire / 1000000;
119 t_spc.tv_nsec = (expire % 1000000) * 1000;
120 pthread_cond_timedwait(&ucond,&umutex,&t_spc);
121 pthread_mutex_unlock(&umutex);
122
123 if (likely(!cpu->irq_disable) &&
124 likely(cpu->gen->state == CPU_STATE_RUNNING) &&
125 likely(cpu->msr & PPC32_MSR_EE))
126 {
127 cpu->timer_irq_pending++;
128
129 if (unlikely(cpu->timer_irq_pending > threshold)) {
130 cpu->timer_irq_pending = 0;
131 cpu->timer_drift++;
132 #if 0
133 printf("Timer IRQ not accurate (%u pending IRQ): "
134 "reduce the \"--timer-irq-check-itv\" parameter "
135 "(current value: %u)\n",
136 cpu->timer_irq_pending,cpu->timer_irq_check_itv);
137 #endif
138 }
139 }
140
141 expire += interval;
142 }
143
144 return NULL;
145 }
146
147 #define IDLE_HASH_SIZE 8192
148
149 /* Idle PC hash item */
150 struct ppc32_idle_pc_hash {
151 m_uint32_t ia;
152 u_int count;
153 struct ppc32_idle_pc_hash *next;
154 };
155
156 /* Determine an "idling" PC */
157 int ppc32_get_idling_pc(cpu_gen_t *cpu)
158 {
159 cpu_ppc_t *pcpu = CPU_PPC32(cpu);
160 struct ppc32_idle_pc_hash **pc_hash,*p;
161 struct cpu_idle_pc *res;
162 u_int h_index,res_count;
163 m_uint32_t cur_ia;
164 int i;
165
166 cpu->idle_pc_prop_count = 0;
167
168 if (pcpu->idle_pc != 0) {
169 printf("\nYou already use an idle PC, using the calibration would give "
170 "incorrect results.\n");
171 return(-1);
172 }
173
174 printf("\nPlease wait while gathering statistics...\n");
175
176 pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct ppc32_idle_pc_hash *));
177
178 /* Disable IRQ */
179 pcpu->irq_disable = TRUE;
180
181 /* Take 1000 measures, each mesure every 10ms */
182 for(i=0;i<1000;i++) {
183 cur_ia = pcpu->ia;
184 h_index = (cur_ia >> 2) & (IDLE_HASH_SIZE-1);
185
186 for(p=pc_hash[h_index];p;p=p->next)
187 if (p->ia == cur_ia) {
188 p->count++;
189 break;
190 }
191
192 if (!p) {
193 if ((p = malloc(sizeof(*p)))) {
194 p->ia = cur_ia;
195 p->count = 1;
196 p->next = pc_hash[h_index];
197 pc_hash[h_index] = p;
198 }
199 }
200
201 usleep(10000);
202 }
203
204 /* Select PCs */
205 for(i=0,res_count=0;i<IDLE_HASH_SIZE;i++) {
206 for(p=pc_hash[i];p;p=p->next)
207 if ((p->count >= 20) && (p->count <= 80)) {
208 res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++];
209
210 res->pc = p->ia;
211 res->count = p->count;
212
213 if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES)
214 goto done;
215 }
216 }
217
218 done:
219 /* Set idle PC */
220 if (cpu->idle_pc_prop_count) {
221 printf("Done. Suggested idling PC:\n");
222
223 for(i=0;i<cpu->idle_pc_prop_count;i++) {
224 printf(" 0x%llx (count=%u)\n",
225 cpu->idle_pc_prop[i].pc,
226 cpu->idle_pc_prop[i].count);
227 }
228
229 printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n",
230 cpu->idle_pc_prop[0].pc);
231 } else {
232 printf("Done. No suggestion for idling PC, dumping the full table:\n");
233
234 for(i=0;i<IDLE_HASH_SIZE;i++)
235 for(p=pc_hash[i];p;p=p->next) {
236 printf(" 0x%8.8x (%3u)\n",p->ia,p->count);
237
238 if (cpu->idle_pc_prop_count < CPU_IDLE_PC_MAX_RES) {
239 res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++];
240
241 res->pc = p->ia;
242 res->count = p->count;
243 }
244 }
245
246 printf("\n");
247 }
248
249 /* Re-enable IRQ */
250 pcpu->irq_disable = FALSE;
251 return(0);
252 }
253
254 #if 0
255 /* Set an IRQ (VM IRQ standard routing) */
256 void ppc32_vm_set_irq(vm_instance_t *vm,u_int irq)
257 {
258 cpu_ppc_t *boot_cpu;
259
260 boot_cpu = CPU_PPC32(vm->boot_cpu);
261
262 if (boot_cpu->irq_disable) {
263 boot_cpu->irq_pending = 0;
264 return;
265 }
266
267 ppc32_set_irq(boot_cpu,irq);
268
269 if (boot_cpu->irq_idle_preempt[irq])
270 cpu_idle_break_wait(vm->boot_cpu);
271 }
272
273 /* Clear an IRQ (VM IRQ standard routing) */
274 void ppc32_vm_clear_irq(vm_instance_t *vm,u_int irq)
275 {
276 cpu_ppc_t *boot_cpu;
277
278 boot_cpu = CPU_PPC32(vm->boot_cpu);
279 ppc32_clear_irq(boot_cpu,irq);
280 }
281 #endif
282
283 /* Generate an exception */
284 void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector)
285 {
286 //printf("TRIGGER_EXCEPTION: saving cpu->ia=0x%8.8x, msr=0x%8.8x\n",
287 // cpu->ia,cpu->msr);
288
289 /* Save the return instruction address */
290 cpu->srr0 = cpu->ia;
291
292 if (exc_vector == PPC32_EXC_SYSCALL)
293 cpu->srr0 += sizeof(ppc_insn_t);
294
295 //printf("SRR0 = 0x%8.8x\n",cpu->srr0);
296
297 /* Save Machine State Register (MSR) */
298 cpu->srr1 = cpu->msr & PPC32_EXC_SRR1_MASK;
299
300 //printf("SRR1 = 0x%8.8x\n",cpu->srr1);
301
302 /* Set the new SRR value */
303 cpu->msr &= ~PPC32_EXC_MSR_MASK;
304 cpu->irq_check = FALSE;
305
306 //printf("MSR = 0x%8.8x\n",cpu->msr);
307
308 /* Use bootstrap vectors ? */
309 if (cpu->msr & PPC32_MSR_IP)
310 cpu->ia = 0xFFF00000 + exc_vector;
311 else
312 cpu->ia = exc_vector;
313 }
314
315 /* Trigger IRQs */
316 fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu)
317 {
318 if (unlikely(cpu->irq_disable)) {
319 cpu->irq_pending = FALSE;
320 cpu->irq_check = FALSE;
321 return;
322 }
323
324 /* Clear the IRQ check flag */
325 cpu->irq_check = FALSE;
326
327 if (cpu->irq_pending && (cpu->msr & PPC32_MSR_EE)) {
328 cpu->irq_count++;
329 cpu->irq_pending = FALSE;
330 ppc32_trigger_exception(cpu,PPC32_EXC_EXT);
331 }
332 }
333
334 /* Trigger the decrementer exception */
335 void ppc32_trigger_timer_irq(cpu_ppc_t *cpu)
336 {
337 cpu->timer_irq_count++;
338
339 if (cpu->msr & PPC32_MSR_EE)
340 ppc32_trigger_exception(cpu,PPC32_EXC_DEC);
341 }
342
343 /* Virtual breakpoint */
344 fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu)
345 {
346 cpu_log(cpu->gen,"BREAKPOINT",
347 "Virtual breakpoint reached at IA=0x%8.8x\n",cpu->ia);
348
349 printf("[[[ Virtual Breakpoint reached at IA=0x%8.8x LR=0x%8.8x]]]\n",
350 cpu->ia,cpu->lr);
351
352 ppc32_dump_regs(cpu->gen);
353 }
354
355 /* Add a virtual breakpoint */
356 int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia)
357 {
358 cpu_ppc_t *pcpu = CPU_PPC32(cpu);
359 int i;
360
361 for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
362 if (!pcpu->breakpoints[i])
363 break;
364
365 if (i == PPC32_MAX_BREAKPOINTS)
366 return(-1);
367
368 pcpu->breakpoints[i] = ia;
369 pcpu->breakpoints_enabled = TRUE;
370 return(0);
371 }
372
373 /* Remove a virtual breakpoint */
374 void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia)
375 {
376 cpu_ppc_t *pcpu = CPU_PPC32(cpu);
377 int i,j;
378
379 for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
380 if (pcpu->breakpoints[i] == ia)
381 {
382 for(j=i;j<PPC32_MAX_BREAKPOINTS-1;j++)
383 pcpu->breakpoints[j] = pcpu->breakpoints[j+1];
384
385 pcpu->breakpoints[PPC32_MAX_BREAKPOINTS-1] = 0;
386 }
387
388 for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
389 if (pcpu->breakpoints[i] != 0)
390 return;
391
392 pcpu->breakpoints_enabled = FALSE;
393 }
394
395 /* Set a register */
396 void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val)
397 {
398 if (reg < PPC32_GPR_NR)
399 CPU_PPC32(cpu)->gpr[reg] = (m_uint32_t)val;
400 }
401
402 /* Dump registers of a PowerPC processor */
403 void ppc32_dump_regs(cpu_gen_t *cpu)
404 {
405 cpu_ppc_t *pcpu = CPU_PPC32(cpu);
406 int i;
407
408 printf("PowerPC Registers:\n");
409
410 for(i=0;i<PPC32_GPR_NR/4;i++) {
411 printf(" $%2d = 0x%8.8x $%2d = 0x%8.8x"
412 " $%2d = 0x%8.8x $%2d = 0x%8.8x\n",
413 i*4, pcpu->gpr[i*4], (i*4)+1, pcpu->gpr[(i*4)+1],
414 (i*4)+2, pcpu->gpr[(i*4)+2], (i*4)+3, pcpu->gpr[(i*4)+3]);
415 }
416
417 printf("\n");
418 printf(" ia = 0x%8.8x, lr = 0x%8.8x\n", pcpu->ia, pcpu->lr);
419 printf(" cr = 0x%8.8x, msr = 0x%8.8x, xer = 0x%8.8x, dec = 0x%8.8x\n",
420 ppc32_get_cr(pcpu), pcpu->msr,
421 pcpu->xer | (pcpu->xer_ca << PPC32_XER_CA_BIT),
422 pcpu->dec);
423
424 printf(" sprg[0] = 0x%8.8x, sprg[1] = 0x%8.8x\n",
425 pcpu->sprg[0],pcpu->sprg[1]);
426
427 printf(" sprg[2] = 0x%8.8x, sprg[3] = 0x%8.8x\n",
428 pcpu->sprg[2],pcpu->sprg[3]);
429
430 printf("\n IRQ count: %llu, IRQ false positives: %llu, "
431 "IRQ Pending: %u, IRQ Check: %s\n",
432 pcpu->irq_count,pcpu->irq_fp_count,pcpu->irq_pending,
433 pcpu->irq_check ? "yes" : "no");
434
435 printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n",
436 pcpu->timer_irq_count,pcpu->timer_irq_pending,pcpu->timer_drift);
437
438 printf(" Device access count: %llu\n",cpu->dev_access_counter);
439
440 printf("\n");
441 }
442
443 /* Dump BAT registers */
444 static void ppc32_dump_bat(cpu_ppc_t *cpu,int index)
445 {
446 int i;
447
448 for(i=0;i<PPC32_BAT_NR;i++)
449 printf(" BAT[%d] = 0x%8.8x 0x%8.8x\n",
450 i,cpu->bat[index][i].reg[0],cpu->bat[index][i].reg[1]);
451 }
452
453 /* Dump MMU registers */
454 void ppc32_dump_mmu(cpu_gen_t *cpu)
455 {
456 cpu_ppc_t *pcpu = CPU_PPC32(cpu);
457 int i;
458
459 printf("PowerPC MMU Registers:\n");
460
461 printf(" - IBAT Registers:\n");
462 ppc32_dump_bat(pcpu,PPC32_IBAT_IDX);
463
464 printf(" - DBAT Registers:\n");
465 ppc32_dump_bat(pcpu,PPC32_DBAT_IDX);
466
467 printf(" - Segment Registers:\n");
468 for(i=0;i<PPC32_SR_NR;i++)
469 printf(" SR[%d] = 0x%8.8x\n",i,pcpu->sr[i]);
470
471 printf(" - SDR1: 0x%8.8x\n",pcpu->sdr1);
472 }
473
474 /* Load a raw image into the simulated memory */
475 int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr)
476 {
477 struct stat file_info;
478 size_t len,clen;
479 m_uint32_t remain;
480 void *haddr;
481 FILE *bfd;
482
483 if (!(bfd = fopen(filename,"r"))) {
484 perror("fopen");
485 return(-1);
486 }
487
488 if (fstat(fileno(bfd),&file_info) == -1) {
489 perror("stat");
490 return(-1);
491 }
492
493 len = file_info.st_size;
494
495 printf("Loading RAW file '%s' at virtual address 0x%8.8x (size=%lu)\n",
496 filename,vaddr,(u_long)len);
497
498 while(len > 0)
499 {
500 haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE);
501
502 if (!haddr) {
503 fprintf(stderr,"load_raw_image: invalid load address 0x%8.8x\n",
504 vaddr);
505 return(-1);
506 }
507
508 if (len > PPC32_MIN_PAGE_SIZE)
509 clen = PPC32_MIN_PAGE_SIZE;
510 else
511 clen = len;
512
513 remain = MIPS_MIN_PAGE_SIZE;
514 remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK));
515
516 clen = m_min(clen,remain);
517
518 if (fread((u_char *)haddr,clen,1,bfd) != 1)
519 break;
520
521 vaddr += clen;
522 len -= clen;
523 }
524
525 fclose(bfd);
526 return(0);
527 }
528
529 /* Load an ELF image into the simulated memory */
530 int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load,
531 m_uint32_t *entry_point)
532 {
533 m_uint32_t vaddr,remain;
534 void *haddr;
535 Elf32_Ehdr *ehdr;
536 Elf32_Shdr *shdr;
537 Elf_Scn *scn;
538 Elf *img_elf;
539 size_t len,clen;
540 char *name;
541 int i,fd;
542 FILE *bfd;
543
544 if (!filename)
545 return(-1);
546
547 #ifdef __CYGWIN__
548 fd = open(filename,O_RDONLY|O_BINARY);
549 #else
550 fd = open(filename,O_RDONLY);
551 #endif
552
553 if (fd == -1) {
554 perror("load_elf_image: open");
555 return(-1);
556 }
557
558 if (elf_version(EV_CURRENT) == EV_NONE) {
559 fprintf(stderr,"load_elf_image: library out of date\n");
560 return(-1);
561 }
562
563 if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) {
564 fprintf(stderr,"load_elf_image: elf_begin: %s\n",
565 elf_errmsg(elf_errno()));
566 return(-1);
567 }
568
569 if (!(ehdr = elf32_getehdr(img_elf))) {
570 fprintf(stderr,"load_elf_image: invalid ELF file\n");
571 return(-1);
572 }
573
574 printf("Loading ELF file '%s'...\n",filename);
575 bfd = fdopen(fd,"rb");
576
577 if (!bfd) {
578 perror("load_elf_image: fdopen");
579 return(-1);
580 }
581
582 if (!skip_load) {
583 for(i=0;i<ehdr->e_shnum;i++) {
584 scn = elf_getscn(img_elf,i);
585
586 shdr = elf32_getshdr(scn);
587 name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name);
588 len = shdr->sh_size;
589
590 if (!(shdr->sh_flags & SHF_ALLOC) || !len)
591 continue;
592
593 fseek(bfd,shdr->sh_offset,SEEK_SET);
594 vaddr = shdr->sh_addr;
595
596 if (cpu->vm->debug_level > 0) {
597 printf(" * Adding section at virtual address 0x%8.8x "
598 "(len=0x%8.8lx)\n",vaddr,(u_long)len);
599 }
600
601 while(len > 0)
602 {
603 haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE);
604
605 if (!haddr) {
606 fprintf(stderr,"load_elf_image: invalid load address 0x%x\n",
607 vaddr);
608 return(-1);
609 }
610
611 if (len > PPC32_MIN_PAGE_SIZE)
612 clen = PPC32_MIN_PAGE_SIZE;
613 else
614 clen = len;
615
616 remain = PPC32_MIN_PAGE_SIZE;
617 remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK));
618
619 clen = m_min(clen,remain);
620
621 if (fread((u_char *)haddr,clen,1,bfd) < 1)
622 break;
623
624 vaddr += clen;
625 len -= clen;
626 }
627 }
628 } else {
629 printf("ELF loading skipped, using a ghost RAM file.\n");
630 }
631
632 printf("ELF entry point: 0x%x\n",ehdr->e_entry);
633
634 if (entry_point)
635 *entry_point = ehdr->e_entry;
636
637 elf_end(img_elf);
638 fclose(bfd);
639 return(0);
640 }

  ViewVC Help
Powered by ViewVC 1.1.26