From 6a00d60127dc0e1d4efadafe1feb88f05030fe52 Mon Sep 17 00:00:00 2001 From: bellard Date: Mon, 21 Nov 2005 23:25:50 +0000 Subject: SMP support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1640 c046a42c-6fe2-441c-8c8c-71466251a162 --- cpu-defs.h | 5 +- cpu-exec.c | 4 ++ disas.c | 10 +++- exec-all.h | 2 +- exec.c | 104 ++++++++++++++++----------------- gdbstub.c | 13 +++-- monitor.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++------------- vl.c | 99 ++++++++++++++++++++------------ vl.h | 7 +++ 9 files changed, 296 insertions(+), 138 deletions(-) diff --git a/cpu-defs.h b/cpu-defs.h index fb4f8e8a2..c30825381 100644 --- a/cpu-defs.h +++ b/cpu-defs.h @@ -96,6 +96,7 @@ typedef struct CPUTLBEntry { #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ + int cpu_halted; /* TRUE if cpu is halted (sleep mode) */ \ /* soft mmu support */ \ /* in order to avoid passing too many arguments to the memory \ write helpers, we store some rarely used information in the CPU \ @@ -115,9 +116,9 @@ typedef struct CPUTLBEntry { int nb_breakpoints; \ int singlestep_enabled; \ \ + void *next_cpu; /* next CPU sharing TB cache */ \ + int cpu_index; /* CPU index (informative) */ \ /* user data */ \ void *opaque; - - #endif diff --git a/cpu-exec.c b/cpu-exec.c index 9f6487adf..72e32684a 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -251,6 +251,8 @@ int cpu_exec(CPUState *env1) TranslationBlock *tb; uint8_t *tc_ptr; + cpu_single_env = env1; + /* first we save global registers */ saved_env = env; env = env1; @@ -755,6 +757,8 @@ int cpu_exec(CPUState *env1) T2 = saved_T2; #endif env = saved_env; + /* fail safe : never use cpu_single_env outside cpu_exec() */ + cpu_single_env = NULL; return ret; } diff --git a/disas.c b/disas.c index feb8a93ca..78a72675d 100644 --- a/disas.c +++ b/disas.c @@ -138,6 +138,7 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) values: i386 - nonzero means 16 bit code arm - nonzero means thumb code + ppc - nonzero means little endian other targets - unused */ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) @@ -177,7 +178,7 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) disasm_info.mach = bfd_mach_sparc_v9b; #endif #elif defined(TARGET_PPC) - if (cpu_single_env->msr[MSR_LE]) + if (flags) disasm_info.endian = BFD_ENDIAN_LITTLE; #ifdef TARGET_PPC64 disasm_info.mach = bfd_mach_ppc64; @@ -314,6 +315,7 @@ void term_vprintf(const char *fmt, va_list ap); void term_printf(const char *fmt, ...); static int monitor_disas_is_physical; +static CPUState *monitor_disas_env; static int monitor_read_memory (memaddr, myaddr, length, info) @@ -325,7 +327,7 @@ monitor_read_memory (memaddr, myaddr, length, info) if (monitor_disas_is_physical) { cpu_physical_memory_rw(memaddr, myaddr, length, 0); } else { - cpu_memory_rw_debug(cpu_single_env, memaddr,myaddr, length, 0); + cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0); } return 0; } @@ -339,7 +341,8 @@ static int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags) +void monitor_disas(CPUState *env, + target_ulong pc, int nb_insn, int is_physical, int flags) { int count, i; struct disassemble_info disasm_info; @@ -347,6 +350,7 @@ void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags) INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf); + monitor_disas_env = env; monitor_disas_is_physical = is_physical; disasm_info.read_memory_func = monitor_read_memory; diff --git a/exec-all.h b/exec-all.h index 874e9d9a3..67aa227ad 100644 --- a/exec-all.h +++ b/exec-all.h @@ -91,7 +91,7 @@ int cpu_restore_state_copy(struct TranslationBlock *tb, CPUState *env, unsigned long searched_pc, void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); -void cpu_exec_init(void); +void cpu_exec_init(CPUState *env); int page_unprotect(unsigned long address, unsigned long pc, void *puc); void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, int is_cpu_write_access); diff --git a/exec.c b/exec.c index 8aed3d00c..3dbd3eb07 100644 --- a/exec.c +++ b/exec.c @@ -74,6 +74,11 @@ int phys_ram_fd; uint8_t *phys_ram_base; uint8_t *phys_ram_dirty; +CPUState *first_cpu; +/* current CPU in the current thread. It is only valid inside + cpu_exec() */ +CPUState *cpu_single_env; + typedef struct PageDesc { /* list of TBs intersecting this ram page */ TranslationBlock *first_tb; @@ -233,19 +238,30 @@ static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) } #if !defined(CONFIG_USER_ONLY) -static void tlb_protect_code(CPUState *env, ram_addr_t ram_addr, - target_ulong vaddr); +static void tlb_protect_code(ram_addr_t ram_addr); static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, target_ulong vaddr); #endif -void cpu_exec_init(void) +void cpu_exec_init(CPUState *env) { + CPUState **penv; + int cpu_index; + if (!code_gen_ptr) { code_gen_ptr = code_gen_buffer; page_init(); io_mem_init(); } + env->next_cpu = NULL; + penv = &first_cpu; + cpu_index = 0; + while (*penv != NULL) { + penv = (CPUState **)&(*penv)->next_cpu; + cpu_index++; + } + env->cpu_index = cpu_index; + *penv = env; } static inline void invalidate_page_bitmap(PageDesc *p) @@ -277,8 +293,9 @@ static void page_flush_tb(void) /* flush all the translation blocks */ /* XXX: tb_flush is currently not thread safe */ -void tb_flush(CPUState *env) +void tb_flush(CPUState *env1) { + CPUState *env; #if defined(DEBUG_FLUSH) printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", code_gen_ptr - code_gen_buffer, @@ -286,7 +303,10 @@ void tb_flush(CPUState *env) nb_tbs > 0 ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0); #endif nb_tbs = 0; - memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + } memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *)); page_flush_tb(); @@ -424,6 +444,7 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n) static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_addr) { + CPUState *env; PageDesc *p; unsigned int h, n1; target_ulong phys_pc; @@ -451,7 +472,10 @@ static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_ad /* remove the TB from the hash list */ h = tb_jmp_cache_hash_func(tb->pc); - cpu_single_env->tb_jmp_cache[h] = NULL; + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->tb_jmp_cache[h] == tb) + env->tb_jmp_cache[h] = NULL; + } /* suppress this TB from the two jump lists */ tb_jmp_remove(tb, 0); @@ -818,10 +842,7 @@ static inline void tb_alloc_page(TranslationBlock *tb, protected. So we handle the case where only the first TB is allocated in a physical page */ if (!last_first_tb) { - target_ulong virt_addr; - - virt_addr = (tb->pc & TARGET_PAGE_MASK) + (n << TARGET_PAGE_BITS); - tlb_protect_code(cpu_single_env, page_addr, virt_addr); + tlb_protect_code(page_addr); } #endif @@ -1246,40 +1267,13 @@ void tlb_flush_page(CPUState *env, target_ulong addr) #endif } -static inline void tlb_protect_code1(CPUTLBEntry *tlb_entry, target_ulong addr) -{ - if (addr == (tlb_entry->address & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) && - (tlb_entry->address & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { - tlb_entry->address = (tlb_entry->address & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; - } -} - /* update the TLBs so that writes to code in the virtual page 'addr' can be detected */ -static void tlb_protect_code(CPUState *env, ram_addr_t ram_addr, - target_ulong vaddr) +static void tlb_protect_code(ram_addr_t ram_addr) { - int i; - - vaddr &= TARGET_PAGE_MASK; - i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_protect_code1(&env->tlb_write[0][i], vaddr); - tlb_protect_code1(&env->tlb_write[1][i], vaddr); - -#ifdef USE_KQEMU - if (env->kqemu_enabled) { - kqemu_set_notdirty(env, ram_addr); - } -#endif - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] &= ~CODE_DIRTY_FLAG; - -#if !defined(CONFIG_SOFTMMU) - /* NOTE: as we generated the code for this page, it is already at - least readable */ - if (vaddr < MMAP_AREA_END) - mprotect((void *)vaddr, TARGET_PAGE_SIZE, PROT_READ); -#endif + cpu_physical_memory_reset_dirty(ram_addr, + ram_addr + TARGET_PAGE_SIZE, + CODE_DIRTY_FLAG); } /* update the TLB so that writes in physical page 'phys_addr' are no longer @@ -1317,8 +1311,9 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, if (length == 0) return; len = length >> TARGET_PAGE_BITS; - env = cpu_single_env; #ifdef USE_KQEMU + /* XXX: should not depend on cpu context */ + env = first_cpu; if (env->kqemu_enabled) { ram_addr_t addr; addr = start; @@ -1336,10 +1331,12 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, /* we modify the TLB cache so that the dirty bit will be set again when accessing the range */ start1 = start + (unsigned long)phys_ram_base; - for(i = 0; i < CPU_TLB_SIZE; i++) - tlb_reset_dirty_range(&env->tlb_write[0][i], start1, length); - for(i = 0; i < CPU_TLB_SIZE; i++) - tlb_reset_dirty_range(&env->tlb_write[1][i], start1, length); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_write[0][i], start1, length); + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_write[1][i], start1, length); + } #if !defined(CONFIG_SOFTMMU) /* XXX: this is expensive */ @@ -1407,9 +1404,9 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, /* update the TLB corresponding to virtual page vaddr and phys addr addr so that it is no longer dirty */ -static inline void tlb_set_dirty(unsigned long addr, target_ulong vaddr) +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) { - CPUState *env = cpu_single_env; int i; addr &= TARGET_PAGE_MASK; @@ -1723,7 +1720,8 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size) } } -static inline void tlb_set_dirty(unsigned long addr, target_ulong vaddr) +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) { } #endif /* defined(CONFIG_USER_ONLY) */ @@ -1787,7 +1785,7 @@ static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); } static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) @@ -1808,7 +1806,7 @@ static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); } static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) @@ -1829,7 +1827,7 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) - tlb_set_dirty(addr, cpu_single_env->mem_write_vaddr); + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); } static CPUReadMemoryFunc *error_mem_read[3] = { @@ -1953,6 +1951,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, if (is_write) { if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + /* XXX: could force cpu_single_env to NULL to avoid + potential bugs */ if (l >= 4 && ((addr & 3) == 0)) { /* 32 bit write access */ val = ldl_p(buf); diff --git a/gdbstub.c b/gdbstub.c index 4ddf021e6..ef76fb06a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -47,6 +47,7 @@ enum RSState { static int gdbserver_fd = -1; typedef struct GDBState { + CPUState *env; /* current CPU */ enum RSState state; /* parsing state */ int fd; char line_buf[4096]; @@ -576,10 +577,10 @@ static void gdb_vm_stopped(void *opaque, int reason) int ret; /* disable single step if it was enable */ - cpu_single_step(cpu_single_env, 0); + cpu_single_step(s->env, 0); if (reason == EXCP_DEBUG) { - tb_flush(cpu_single_env); + tb_flush(s->env); ret = SIGTRAP; } else @@ -589,8 +590,9 @@ static void gdb_vm_stopped(void *opaque, int reason) } #endif -static void gdb_read_byte(GDBState *s, CPUState *env, int ch) +static void gdb_read_byte(GDBState *s, int ch) { + CPUState *env = s->env; int i, csum; char reply[1]; @@ -676,7 +678,7 @@ gdb_handlesig (CPUState *env, int sig) int i; for (i = 0; i < n; i++) - gdb_read_byte (s, env, buf[i]); + gdb_read_byte (s, buf[i]); } else if (n == 0 || errno != EAGAIN) { @@ -721,7 +723,7 @@ static void gdb_read(void *opaque) vm_start(); } else { for(i = 0; i < size; i++) - gdb_read_byte(s, cpu_single_env, buf[i]); + gdb_read_byte(s, buf[i]); } } @@ -759,6 +761,7 @@ static void gdb_accept(void *opaque) return; } #endif + s->env = first_cpu; /* XXX: allow to change CPU */ s->fd = fd; fcntl(fd, F_SETFL, O_NONBLOCK); diff --git a/monitor.c b/monitor.c index 18f0c8991..85a997db8 100644 --- a/monitor.c +++ b/monitor.c @@ -64,6 +64,8 @@ static int term_outbuf_index; static void monitor_start_input(void); +CPUState *mon_cpu = NULL; + void term_flush(void) { if (term_outbuf_index > 0) { @@ -201,17 +203,69 @@ static void do_info_block(void) bdrv_info(); } +/* get the current CPU defined by the user */ +int mon_set_cpu(int cpu_index) +{ + CPUState *env; + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == cpu_index) { + mon_cpu = env; + return 0; + } + } + return -1; +} + +CPUState *mon_get_cpu(void) +{ + if (!mon_cpu) { + mon_set_cpu(0); + } + return mon_cpu; +} + static void do_info_registers(void) { + CPUState *env; + env = mon_get_cpu(); + if (!env) + return; #ifdef TARGET_I386 - cpu_dump_state(cpu_single_env, NULL, monitor_fprintf, + cpu_dump_state(env, NULL, monitor_fprintf, X86_DUMP_FPU); #else - cpu_dump_state(cpu_single_env, NULL, monitor_fprintf, + cpu_dump_state(env, NULL, monitor_fprintf, 0); #endif } +static void do_info_cpus(void) +{ + CPUState *env; + + /* just to set the default cpu if not already done */ + mon_get_cpu(); + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + term_printf("%c CPU #%d:", + (env == mon_cpu) ? '*' : ' ', + env->cpu_index); +#if defined(TARGET_I386) + term_printf(" pc=0x" TARGET_FMT_lx, env->eip + env->segs[R_CS].base); + if (env->cpu_halted) + term_printf(" (halted)"); +#endif + term_printf("\n"); + } +} + +static void do_cpu_set(int index) +{ + if (mon_set_cpu(index) < 0) + term_printf("Invalid CPU index\n"); +} + static void do_info_jit(void) { dump_exec_info(NULL, monitor_fprintf); @@ -381,6 +435,7 @@ static void term_printc(int c) static void memory_dump(int count, int format, int wsize, target_ulong addr, int is_physical) { + CPUState *env; int nb_per_line, l, line_size, i, max_digits, len; uint8_t buf[16]; uint64_t v; @@ -388,19 +443,22 @@ static void memory_dump(int count, int format, int wsize, if (format == 'i') { int flags; flags = 0; + env = mon_get_cpu(); + if (!env && !is_physical) + return; #ifdef TARGET_I386 if (wsize == 2) { flags = 1; } else if (wsize == 4) { flags = 0; } else { - /* as default we use the current CS size */ + /* as default we use the current CS size */ flags = 0; - if (!(cpu_single_env->segs[R_CS].flags & DESC_B_MASK)) + if (env && !(env->segs[R_CS].flags & DESC_B_MASK)) flags = 1; } #endif - monitor_disas(addr, count, is_physical, flags); + monitor_disas(env, addr, count, is_physical, flags); return; } @@ -437,7 +495,10 @@ static void memory_dump(int count, int format, int wsize, if (is_physical) { cpu_physical_memory_rw(addr, buf, l, 0); } else { - cpu_memory_rw_debug(cpu_single_env, addr, buf, l, 0); + env = mon_get_cpu(); + if (!env) + break; + cpu_memory_rw_debug(env, addr, buf, l, 0); } i = 0; while (i < l) { @@ -776,10 +837,14 @@ static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask) static void tlb_info(void) { - CPUState *env = cpu_single_env; + CPUState *env; int l1, l2; uint32_t pgd, pde, pte; + env = mon_get_cpu(); + if (!env) + return; + if (!(env->cr[0] & CR0_PG_MASK)) { term_printf("PG disabled\n"); return; @@ -830,10 +895,14 @@ static void mem_print(uint32_t *pstart, int *plast_prot, static void mem_info(void) { - CPUState *env = cpu_single_env; + CPUState *env; int l1, l2, prot, last_prot; uint32_t pgd, pde, pte, start, end; + env = mon_get_cpu(); + if (!env) + return; + if (!(env->cr[0] & CR0_PG_MASK)) { term_printf("PG disabled\n"); return; @@ -874,10 +943,15 @@ static void mem_info(void) static void do_info_kqemu(void) { #ifdef USE_KQEMU + CPUState *env; int val; val = 0; - if (cpu_single_env) - val = cpu_single_env->kqemu_enabled; + env = mon_get_cpu(); + if (!env) { + term_printf("No cpu initialized yet"); + return; + } + val = env->kqemu_enabled; term_printf("kqemu is %s\n", val ? "enabled" : "disabled"); #else term_printf("kqemu support is not compiled\n"); @@ -934,6 +1008,8 @@ static term_cmd_t term_cmds[] = { "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" }, { "usb_del", "s", do_usb_del, "device", "remove USB device 'bus.addr'" }, + { "cpu", "i", do_cpu_set, + "index", "set the default CPU" }, { NULL, NULL, }, }; @@ -946,6 +1022,8 @@ static term_cmd_t info_cmds[] = { "", "show the block devices" }, { "registers", "", do_info_registers, "", "show the cpu registers" }, + { "cpus", "", do_info_cpus, + "", "show infos for each CPU" }, { "history", "", do_info_history, "", "show the command line history", }, { "irq", "", irq_info, @@ -989,63 +1067,85 @@ typedef struct MonitorDef { #if defined(TARGET_I386) static target_long monitor_get_pc (struct MonitorDef *md, int val) { - return cpu_single_env->eip + cpu_single_env->segs[R_CS].base; + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->eip + env->segs[R_CS].base; } #endif #if defined(TARGET_PPC) static target_long monitor_get_ccr (struct MonitorDef *md, int val) { + CPUState *env = mon_get_cpu(); unsigned int u; int i; + if (!env) + return 0; + u = 0; for (i = 0; i < 8; i++) - u |= cpu_single_env->crf[i] << (32 - (4 * i)); + u |= env->crf[i] << (32 - (4 * i)); return u; } static target_long monitor_get_msr (struct MonitorDef *md, int val) { - return (cpu_single_env->msr[MSR_POW] << MSR_POW) | - (cpu_single_env->msr[MSR_ILE] << MSR_ILE) | - (cpu_single_env->msr[MSR_EE] << MSR_EE) | - (cpu_single_env->msr[MSR_PR] << MSR_PR) | - (cpu_single_env->msr[MSR_FP] << MSR_FP) | - (cpu_single_env->msr[MSR_ME] << MSR_ME) | - (cpu_single_env->msr[MSR_FE0] << MSR_FE0) | - (cpu_single_env->msr[MSR_SE] << MSR_SE) | - (cpu_single_env->msr[MSR_BE] << MSR_BE) | - (cpu_single_env->msr[MSR_FE1] << MSR_FE1) | - (cpu_single_env->msr[MSR_IP] << MSR_IP) | - (cpu_single_env->msr[MSR_IR] << MSR_IR) | - (cpu_single_env->msr[MSR_DR] << MSR_DR) | - (cpu_single_env->msr[MSR_RI] << MSR_RI) | - (cpu_single_env->msr[MSR_LE] << MSR_LE); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->msr[MSR_POW] << MSR_POW) | + (env->msr[MSR_ILE] << MSR_ILE) | + (env->msr[MSR_EE] << MSR_EE) | + (env->msr[MSR_PR] << MSR_PR) | + (env->msr[MSR_FP] << MSR_FP) | + (env->msr[MSR_ME] << MSR_ME) | + (env->msr[MSR_FE0] << MSR_FE0) | + (env->msr[MSR_SE] << MSR_SE) | + (env->msr[MSR_BE] << MSR_BE) | + (env->msr[MSR_FE1] << MSR_FE1) | + (env->msr[MSR_IP] << MSR_IP) | + (env->msr[MSR_IR] << MSR_IR) | + (env->msr[MSR_DR] << MSR_DR) | + (env->msr[MSR_RI] << MSR_RI) | + (env->msr[MSR_LE] << MSR_LE); } static target_long monitor_get_xer (struct MonitorDef *md, int val) { - return (cpu_single_env->xer[XER_SO] << XER_SO) | - (cpu_single_env->xer[XER_OV] << XER_OV) | - (cpu_single_env->xer[XER_CA] << XER_CA) | - (cpu_single_env->xer[XER_BC] << XER_BC); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->xer[XER_SO] << XER_SO) | + (env->xer[XER_OV] << XER_OV) | + (env->xer[XER_CA] << XER_CA) | + (env->xer[XER_BC] << XER_BC); } static target_long monitor_get_decr (struct MonitorDef *md, int val) { - return cpu_ppc_load_decr(cpu_single_env); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_decr(env); } static target_long monitor_get_tbu (struct MonitorDef *md, int val) { - return cpu_ppc_load_tbu(cpu_single_env); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbu(env); } static target_long monitor_get_tbl (struct MonitorDef *md, int val) { - return cpu_ppc_load_tbl(cpu_single_env); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbl(env); } #endif @@ -1053,13 +1153,19 @@ static target_long monitor_get_tbl (struct MonitorDef *md, int val) #ifndef TARGET_SPARC64 static target_long monitor_get_psr (struct MonitorDef *md, int val) { - return GET_PSR(cpu_single_env); + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return GET_PSR(env); } #endif static target_long monitor_get_reg(struct MonitorDef *md, int val) { - return cpu_single_env->regwptr[val]; + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->regwptr[val]; } #endif @@ -1269,6 +1375,7 @@ static void expr_error(const char *fmt) longjmp(expr_env, 1); } +/* return 0 if OK, -1 if not found, -2 if no CPU defined */ static int get_monitor_def(target_long *pval, const char *name) { MonitorDef *md; @@ -1279,7 +1386,10 @@ static int get_monitor_def(target_long *pval, const char *name) if (md->get_value) { *pval = md->get_value(md, md->offset); } else { - ptr = (uint8_t *)cpu_single_env + md->offset; + CPUState *env = mon_get_cpu(); + if (!env) + return -2; + ptr = (uint8_t *)env + md->offset; switch(md->type) { case MD_I32: *pval = *(int32_t *)ptr; @@ -1313,6 +1423,7 @@ static target_long expr_unary(void) { target_long n; char *p; + int ret; switch(*pch) { case '+': @@ -1362,8 +1473,11 @@ static target_long expr_unary(void) while (isspace(*pch)) pch++; *q = 0; - if (get_monitor_def(&n, buf)) + ret = get_monitor_def(&n, buf); + if (ret == -1) expr_error("unknown register"); + else if (ret == -2) + expr_error("no cpu defined"); } break; case '\0': diff --git a/vl.c b/vl.c index 725e954d3..b946390a1 100644 --- a/vl.c +++ b/vl.c @@ -83,8 +83,6 @@ #include "exec-all.h" -//#define DO_TB_FLUSH - #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" //#define DEBUG_UNUSED_IOPORT @@ -109,8 +107,6 @@ const char *bios_dir = CONFIG_QEMU_SHAREDIR; char phys_ram_file[1024]; -CPUState *global_env; -CPUState *cpu_single_env; void *ioport_opaque[MAX_IOPORTS]; IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; @@ -156,6 +152,7 @@ int usb_enabled = 0; USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; USBDevice *vm_usb_hub; static VLANState *first_vlan; +int smp_cpus = 1; /***********************************************************/ /* x86 ISA bus support */ @@ -427,16 +424,20 @@ int cpu_inl(CPUState *env, int addr) void hw_error(const char *fmt, ...) { va_list ap; + CPUState *env; va_start(ap, fmt); fprintf(stderr, "qemu: hardware error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + fprintf(stderr, "CPU #%d:\n", env->cpu_index); #ifdef TARGET_I386 - cpu_dump_state(global_env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); #else - cpu_dump_state(global_env, stderr, fprintf, 0); + cpu_dump_state(env, stderr, fprintf, 0); #endif + } va_end(ap); abort(); } @@ -879,13 +880,16 @@ static void host_alarm_handler(int host_signum) qemu_get_clock(vm_clock)) || qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], qemu_get_clock(rt_clock))) { - /* stop the cpu because a timer occured */ - cpu_interrupt(global_env, CPU_INTERRUPT_EXIT); + CPUState *env = cpu_single_env; + if (env) { + /* stop the currently executing cpu because a timer occured */ + cpu_interrupt(env, CPU_INTERRUPT_EXIT); #ifdef USE_KQEMU - if (global_env->kqemu_enabled) { - kqemu_cpu_interrupt(global_env); - } + if (env->kqemu_enabled) { + kqemu_cpu_interrupt(env); + } #endif + } } } @@ -2970,9 +2974,6 @@ int qemu_loadvm(const char *filename) goto the_end; } for(;;) { -#if defined (DO_TB_FLUSH) - tb_flush(global_env); -#endif len = qemu_get_byte(f); if (feof(f)) break; @@ -3583,27 +3584,22 @@ void qemu_system_reset(void) void qemu_system_reset_request(void) { reset_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_shutdown_request(void) { shutdown_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void qemu_system_powerdown_request(void) { powerdown_requested = 1; - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); -} - -static void main_cpu_reset(void *opaque) -{ -#if defined(TARGET_I386) || defined(TARGET_SPARC) - CPUState *env = opaque; - cpu_reset(env); -#endif + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); } void main_loop_wait(int timeout) @@ -3684,14 +3680,42 @@ void main_loop_wait(int timeout) qemu_get_clock(rt_clock)); } +static CPUState *cur_cpu; + +static CPUState *find_next_cpu(void) +{ + CPUState *env; + env = cur_cpu; + for(;;) { + /* get next cpu */ + env = env->next_cpu; + if (!env) + env = first_cpu; + if (!env->cpu_halted) + break; + /* all CPUs are halted ? */ + if (env == cur_cpu) + return NULL; + } + cur_cpu = env; + return env; +} + int main_loop(void) { int ret, timeout; - CPUState *env = global_env; + CPUState *env; + cur_cpu = first_cpu; for(;;) { if (vm_running) { - ret = cpu_exec(env); + /* find next cpu to run */ + /* XXX: handle HLT correctly */ + env = find_next_cpu(); + if (!env) + ret = EXCP_HLT; + else + ret = cpu_exec(env); if (shutdown_requested) { ret = EXCP_INTERRUPT; break; @@ -3774,7 +3798,7 @@ void help(void) " connect the host TAP network interface to VLAN 'n' and use\n" " the network script 'file' (default=%s);\n" " use 'fd=h' to connect to an already opened TAP interface\n" - "-net socket[,vlan=n][,fd=x][,listen=[host]:port][,connect=host:port]\n" + "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n" " connect the vlan 'n' to another VLAN using a socket connection\n" #endif "-net none use it alone to have zero network devices; if no -net option\n" @@ -3899,6 +3923,7 @@ enum { QEMU_OPTION_win2k_hack, QEMU_OPTION_usb, QEMU_OPTION_usbdevice, + QEMU_OPTION_smp, }; typedef struct QEMUOption { @@ -3965,6 +3990,7 @@ const QEMUOption qemu_options[] = { { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, { "win2k-hack", 0, QEMU_OPTION_win2k_hack }, { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, + { "smp", HAS_ARG, QEMU_OPTION_smp }, /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -4120,7 +4146,6 @@ int main(int argc, char **argv) #endif int i, cdrom_index; int snapshot, linux_boot; - CPUState *env; const char *initrd_filename; const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; const char *kernel_filename, *kernel_cmdline; @@ -4511,6 +4536,13 @@ int main(int argc, char **argv) optarg); usb_devices_index++; break; + case QEMU_OPTION_smp: + smp_cpus = atoi(optarg); + if (smp_cpus < 1 || smp_cpus > 8) { + fprintf(stderr, "Invalid number of CPUs\n"); + exit(1); + } + break; } } } @@ -4659,15 +4691,8 @@ int main(int argc, char **argv) } } - /* init CPU state */ - env = cpu_init(); - global_env = env; - cpu_single_env = env; - - register_savevm("timer", 0, 1, timer_save, timer_load, env); - register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + register_savevm("timer", 0, 1, timer_save, timer_load, NULL); register_savevm("ram", 0, 1, ram_save, ram_load, NULL); - qemu_register_reset(main_cpu_reset, global_env); init_ioports(); cpu_calibrate_ticks(); diff --git a/vl.h b/vl.h index d5efcd2e7..1daae5042 100644 --- a/vl.h +++ b/vl.h @@ -142,6 +142,7 @@ extern const char *keyboard_layout; extern int kqemu_allowed; extern int win2k_install_hack; extern int usb_enabled; +extern int smp_cpus; /* XXX: make it dynamic */ #if defined (TARGET_PPC) @@ -429,6 +430,9 @@ int register_savevm(const char *idstr, void qemu_get_timer(QEMUFile *f, QEMUTimer *ts); void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); +void cpu_save(QEMUFile *f, void *opaque); +int cpu_load(QEMUFile *f, void *opaque, int version_id); + /* block.c */ typedef struct BlockDriverState BlockDriverState; typedef struct BlockDriver BlockDriver; @@ -774,6 +778,9 @@ int pit_get_out(PITState *pit, int channel, int64_t current_time); extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +void ioport_set_a20(int enable); +int ioport_get_a20(void); + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; -- cgit v1.2.3