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

  ViewVC Help
Powered by ViewVC 1.1.26