# HG changeset patch # User tristan.gingold@xxxxxxxx # Node ID 601fb1345c8fa8d2aae097806f21d063bd39029a # Parent 7ed6c203efe9c79446ec21a6ba288c04e2ccee55 Final SMP-guest patch: add IPI and boot rendez-vous support. Signed-off-by: Tristan Gingold diff -r 7ed6c203efe9 -r 601fb1345c8f linux-2.6-xen-sparse/arch/ia64/xen/hypercall.S --- a/linux-2.6-xen-sparse/arch/ia64/xen/hypercall.S Wed Apr 19 16:39:15 2006 +++ b/linux-2.6-xen-sparse/arch/ia64/xen/hypercall.S Fri Apr 21 06:48:28 2006 @@ -341,3 +341,13 @@ br.ret.sptk.many rp END(xen_set_eflag) #endif + +GLOBAL_ENTRY(xen_send_ipi) + mov r14=r32 + mov r15=r33 + mov r2=0x380 + break 0x1000 + ;; + br.ret.sptk.many rp + ;; +END(xen_send_ipi) diff -r 7ed6c203efe9 -r 601fb1345c8f xen/arch/ia64/xen/dom_fw.c --- a/xen/arch/ia64/xen/dom_fw.c Wed Apr 19 16:39:15 2006 +++ b/xen/arch/ia64/xen/dom_fw.c Fri Apr 21 06:48:28 2006 @@ -93,9 +93,24 @@ /* the following heavily leveraged from linux/arch/ia64/hp/sim/fw-emu.c */ -#define NUM_EFI_SYS_TABLES 6 -# define NUM_MEM_DESCS 5 - +/* Set IP and GR1 of not yet initialized vcpu. */ +static void +set_os_boot_rendez (struct domain *d, unsigned long pc, unsigned long gr1) +{ + struct vcpu *v; + int i; + + printf ("set_os_boot_rendez: %lx %lx\n", pc, gr1); + for (i = 1; i < MAX_VIRT_CPUS; i++) { + v = d->vcpu[i]; + if (v != NULL + && !test_bit(_VCPUF_initialised, &v->vcpu_flags)) { + struct pt_regs *regs = vcpu_regs (v); + regs->cr_iip = pc; + regs->r1 = gr1; + } + } +} struct sal_ret_values sal_emulator (long index, unsigned long in1, unsigned long in2, @@ -154,7 +169,18 @@ printf("NON-PRIV DOMAIN CALLED SAL_PCI_CONFIG_WRITE\n"); break; case SAL_SET_VECTORS: - printf("*** CALLED SAL_SET_VECTORS. IGNORED...\n"); + if (in1 == SAL_VECTOR_OS_BOOT_RENDEZ) { + if (in4 != 0 || in5 != 0 || in6 != 0 || in7 != 0) { + /* Sanity check: cs_length1 must be 0, + second vector is reserved. */ + status = -2; + } + else + set_os_boot_rendez (current->domain, in2, in3); + } + else + printf("*** CALLED SAL_SET_VECTORS %lu. IGNORED...\n", + in1); break; case SAL_GET_STATE_INFO: /* No more info. */ @@ -617,6 +643,9 @@ return; } +#define NUM_EFI_SYS_TABLES 6 +#define NUM_MEM_DESCS 5 + static struct ia64_boot_param * dom_fw_init (struct domain *d, const char *args, int arglen, char *fw_mem, int fw_mem_size) { @@ -624,8 +653,9 @@ efi_runtime_services_t *efi_runtime; efi_config_table_t *efi_tables; struct ia64_sal_systab *sal_systab; + struct ia64_sal_desc_entry_point *sal_ed; + struct ia64_sal_desc_ap_wakeup *sal_wakeup; efi_memory_desc_t *efi_memmap, *md; - struct ia64_sal_desc_entry_point *sal_ed; struct ia64_boot_param *bp; unsigned long *pfn; unsigned char checksum = 0; @@ -661,6 +691,7 @@ efi_tables = (void *) cp; cp += NUM_EFI_SYS_TABLES * sizeof(*efi_tables); sal_systab = (void *) cp; cp += sizeof(*sal_systab); sal_ed = (void *) cp; cp += sizeof(*sal_ed); + sal_wakeup = (void *) cp; cp += sizeof(*sal_wakeup); efi_memmap = (void *) cp; cp += NUM_MEM_DESCS*sizeof(*efi_memmap); bp = (void *) cp; cp += sizeof(*bp); pfn = (void *) cp; cp += NFUNCPTRS * 2 * sizeof(pfn); @@ -778,7 +809,7 @@ sal_systab->size = sizeof(*sal_systab); sal_systab->sal_rev_minor = 1; sal_systab->sal_rev_major = 0; - sal_systab->entry_count = 1; + sal_systab->entry_count = 2; strcpy((char *)sal_systab->oem_id, "Xen/ia64"); strcpy((char *)sal_systab->product_id, "Xen/ia64"); @@ -790,6 +821,11 @@ sal_ed->sal_proc = FW_HYPERCALL_SAL_CALL_PADDR + start_mpaddr; dom_fw_hypercall_patch (d, sal_ed->sal_proc, FW_HYPERCALL_SAL_CALL, 1); sal_ed->gp = 0; // will be ignored + + /* Fill an AP wakeup descriptor. */ + sal_wakeup->type = SAL_DESC_AP_WAKEUP; + sal_wakeup->mechanism = IA64_SAL_AP_EXTERNAL_INT; + sal_wakeup->vector = XEN_SAL_BOOT_RENDEZ_VEC; for (cp = (char *) sal_systab; cp < (char *) efi_memmap; ++cp) checksum += *cp; diff -r 7ed6c203efe9 -r 601fb1345c8f xen/arch/ia64/xen/hypercall.c --- a/xen/arch/ia64/xen/hypercall.c Wed Apr 19 16:39:15 2006 +++ b/xen/arch/ia64/xen/hypercall.c Fri Apr 21 06:48:28 2006 @@ -123,6 +123,65 @@ } +static void +fw_hypercall_ipi (struct pt_regs *regs) +{ + int cpu = regs->r14; + int vector = regs->r15; + struct vcpu *targ; + + if (0 && vector == 254) + printf ("send_ipi from %d to %d vector=%d\n", + current->vcpu_id, cpu, vector); + + if (cpu > MAX_VIRT_CPUS) + return; + + targ = current->domain->vcpu[cpu]; + if (targ == NULL) + return; + + if (vector == XEN_SAL_BOOT_RENDEZ_VEC + && !test_bit(_VCPUF_initialised, &targ->vcpu_flags)) { + struct pt_regs *targ_regs = vcpu_regs (targ); + struct vcpu_guest_context c; + + printf ("arch_boot_vcpu: %p %p\n", + (void *)targ_regs->cr_iip, + (void *)targ_regs->r1); + memset (&c, 0, sizeof (c)); + /* Copy regs. */ + c.regs.cr_iip = targ_regs->cr_iip; + c.regs.r1 = targ_regs->r1; + + /* Copy from vcpu 0. */ + c.vcpu.evtchn_vector = + current->domain->vcpu[0]->vcpu_info->arch.evtchn_vector; + if (arch_set_info_guest (targ, &c) != 0) { + printf ("arch_boot_vcpu: failure\n"); + return; + } + if (test_and_clear_bit(_VCPUF_down, + &targ->vcpu_flags)) { + vcpu_wake(targ); + printf ("arch_boot_vcpu: vcpu %d awaken %016lx!\n", + targ->vcpu_id, targ_regs->cr_iip); + } + else + printf ("arch_boot_vcpu: huu, already awaken!"); + } + else { + int running = test_bit(_VCPUF_running, + &targ->vcpu_flags); + + vcpu_pend_interrupt(targ, vector); + vcpu_unblock(targ); + if (running) + smp_send_event_check_cpu(targ->processor); + } + return; +} + static int fw_hypercall (struct pt_regs *regs) { @@ -223,6 +282,9 @@ // FIXME: need fixes in efi.h from 2.6.9 regs->r8 = EFI_UNSUPPORTED; break; + case FW_HYPERCALL_IPI: + fw_hypercall_ipi (regs); + break; default: printf("unknown ia64 fw hypercall %lx\n", regs->r2); regs->r8 = do_ni_hypercall(); diff -r 7ed6c203efe9 -r 601fb1345c8f xen/include/asm-ia64/dom_fw.h --- a/xen/include/asm-ia64/dom_fw.h Wed Apr 19 16:39:15 2006 +++ b/xen/include/asm-ia64/dom_fw.h Fri Apr 21 06:48:28 2006 @@ -126,8 +126,13 @@ */ #define FW_HYPERCALL_FIRST_ARCH 0x300UL +#define FW_HYPERCALL_IPI 0x380UL + /* Xen/ia64 user hypercalls. Only used for debugging. */ #define FW_HYPERCALL_FIRST_USER 0xff00UL + +/* Interrupt vector used for os boot rendez vous. */ +#define XEN_SAL_BOOT_RENDEZ_VEC 0xF3 extern struct ia64_pal_retval xen_pal_emulator(UINT64, u64, u64, u64); extern struct sal_ret_values sal_emulator (long index, unsigned long in1, unsigned long in2, unsigned long in3, unsigned long in4, unsigned long in5, unsigned long in6, unsigned long in7); diff -r 7ed6c203efe9 -r 601fb1345c8f linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c --- /dev/null Wed Apr 19 16:39:15 2006 +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c Fri Apr 21 06:48:28 2006 @@ -0,0 +1,285 @@ +/* + * linux/arch/ia64/kernel/irq.c + * + * Copyright (C) 1998-2001 Hewlett-Packard Co + * Stephane Eranian + * David Mosberger-Tang + * + * 6/10/99: Updated to bring in sync with x86 version to facilitate + * support for SMP and different interrupt controllers. + * + * 09/15/00 Goutham Rao Implemented pci_irq_to_vector + * PCI to vector allocation routine. + * 04/14/2004 Ashok Raj + * Added CPU Hotplug handling for IPF. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for rand_initialize_irq() */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PERFMON +# include +#endif + +#define IRQ_DEBUG 0 + +/* default base addr of IPI table */ +void __iomem *ipi_base_addr = ((void __iomem *) + (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR)); + +/* + * Legacy IRQ to IA-64 vector translation table. + */ +__u8 isa_irq_to_vector_map[16] = { + /* 8259 IRQ translation, first 16 entries */ + 0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, + 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21 +}; +EXPORT_SYMBOL(isa_irq_to_vector_map); + +static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)]; + +int +assign_irq_vector (int irq) +{ + int pos, vector; + again: + pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS); + vector = IA64_FIRST_DEVICE_VECTOR + pos; + if (vector > IA64_LAST_DEVICE_VECTOR) + return -ENOSPC; + if (test_and_set_bit(pos, ia64_vector_mask)) + goto again; + return vector; +} + +void +free_irq_vector (int vector) +{ + int pos; + + if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR) + return; + + pos = vector - IA64_FIRST_DEVICE_VECTOR; + if (!test_and_clear_bit(pos, ia64_vector_mask)) + printk(KERN_WARNING "%s: double free!\n", __FUNCTION__); +} + +#ifdef CONFIG_SMP +# define IS_RESCHEDULE(vec) (vec == IA64_IPI_RESCHEDULE) +#else +# define IS_RESCHEDULE(vec) (0) +#endif +/* + * That's where the IVT branches when we get an external + * interrupt. This branches to the correct hardware IRQ handler via + * function ptr. + */ +void +ia64_handle_irq (ia64_vector vector, struct pt_regs *regs) +{ + unsigned long saved_tpr; + +#if IRQ_DEBUG + { + unsigned long bsp, sp; + + /* + * Note: if the interrupt happened while executing in + * the context switch routine (ia64_switch_to), we may + * get a spurious stack overflow here. This is + * because the register and the memory stack are not + * switched atomically. + */ + bsp = ia64_getreg(_IA64_REG_AR_BSP); + sp = ia64_getreg(_IA64_REG_SP); + + if ((sp - bsp) < 1024) { + static unsigned char count; + static long last_time; + + if (jiffies - last_time > 5*HZ) + count = 0; + if (++count < 5) { + last_time = jiffies; + printk("ia64_handle_irq: DANGER: less than " + "1KB of free stack space!!\n" + "(bsp=0x%lx, sp=%lx)\n", bsp, sp); + } + } + } +#endif /* IRQ_DEBUG */ + + /* + * Always set TPR to limit maximum interrupt nesting depth to + * 16 (without this, it would be ~240, which could easily lead + * to kernel stack overflows). + */ + irq_enter(); + saved_tpr = ia64_getreg(_IA64_REG_CR_TPR); + ia64_srlz_d(); + while (vector != IA64_SPURIOUS_INT_VECTOR) { + if (!IS_RESCHEDULE(vector)) { + ia64_setreg(_IA64_REG_CR_TPR, vector); + ia64_srlz_d(); + + __do_IRQ(local_vector_to_irq(vector), regs); + + /* + * Disable interrupts and send EOI: + */ + local_irq_disable(); + ia64_setreg(_IA64_REG_CR_TPR, saved_tpr); + } + ia64_eoi(); + vector = ia64_get_ivr(); + } + /* + * This must be done *after* the ia64_eoi(). For example, the keyboard softirq + * handler needs to be able to wait for further keyboard interrupts, which can't + * come through until ia64_eoi() has been done. + */ + irq_exit(); +} + +#ifdef CONFIG_HOTPLUG_CPU +/* + * This function emulates a interrupt processing when a cpu is about to be + * brought down. + */ +void ia64_process_pending_intr(void) +{ + ia64_vector vector; + unsigned long saved_tpr; + extern unsigned int vectors_in_migration[NR_IRQS]; + + vector = ia64_get_ivr(); + + irq_enter(); + saved_tpr = ia64_getreg(_IA64_REG_CR_TPR); + ia64_srlz_d(); + + /* + * Perform normal interrupt style processing + */ + while (vector != IA64_SPURIOUS_INT_VECTOR) { + if (!IS_RESCHEDULE(vector)) { + ia64_setreg(_IA64_REG_CR_TPR, vector); + ia64_srlz_d(); + + /* + * Now try calling normal ia64_handle_irq as it would have got called + * from a real intr handler. Try passing null for pt_regs, hopefully + * it will work. I hope it works!. + * Probably could shared code. + */ + vectors_in_migration[local_vector_to_irq(vector)]=0; + __do_IRQ(local_vector_to_irq(vector), NULL); + + /* + * Disable interrupts and send EOI + */ + local_irq_disable(); + ia64_setreg(_IA64_REG_CR_TPR, saved_tpr); + } + ia64_eoi(); + vector = ia64_get_ivr(); + } + irq_exit(); +} +#endif + + +#ifdef CONFIG_SMP +extern irqreturn_t handle_IPI (int irq, void *dev_id, struct pt_regs *regs); + +static struct irqaction ipi_irqaction = { + .handler = handle_IPI, + .flags = SA_INTERRUPT, + .name = "IPI" +}; +#endif + +void +register_percpu_irq (ia64_vector vec, struct irqaction *action) +{ + irq_desc_t *desc; + unsigned int irq; + + for (irq = 0; irq < NR_IRQS; ++irq) + if (irq_to_vector(irq) == vec) { + desc = irq_descp(irq); + desc->status |= IRQ_PER_CPU; + desc->handler = &irq_type_ia64_lsapic; + if (action) + setup_irq(irq, action); + } +} + +void __init +init_IRQ (void) +{ + register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL); +#ifdef CONFIG_SMP + register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction); +#endif +#ifdef CONFIG_PERFMON + pfm_init_percpu(); +#endif + platform_irq_init(); +} + +void +ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect) +{ + void __iomem *ipi_addr; + unsigned long ipi_data; + unsigned long phys_cpu_id; + +#ifdef CONFIG_XEN + if (running_on_xen) { + extern void xen_send_ipi (int cpu, int vec); + xen_send_ipi (cpu, vector); + return; + } +#endif /* CONFIG_XEN */ + +#ifdef CONFIG_SMP + phys_cpu_id = cpu_physical_id(cpu); +#else + phys_cpu_id = (ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff; +#endif + + /* + * cpu number is in 8bit ID and 8bit EID + */ + + ipi_data = (delivery_mode << 8) | (vector & 0xff); + ipi_addr = ipi_base_addr + ((phys_cpu_id << 4) | ((redirect & 1) << 3)); + + writeq(ipi_data, ipi_addr); +}