/[dynamips]/trunk/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 /trunk/ppc32.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (show annotations)
Sat Oct 6 16:23:47 2007 UTC (12 years, 1 month ago) by dpavlin
Original Path: upstream/dynamips-0.2.7-RC1/ppc32.c
File MIME type: text/plain
File size: 15112 byte(s)
dynamips-0.2.7-RC1

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

  ViewVC Help
Powered by ViewVC 1.1.26