📄 voyager_smp.c
字号:
/* -*- mode: c; c-basic-offset: 8 -*- *//* Copyright (C) 1999,2001 * * Author: J.E.J.Bottomley@HansenPartnership.com * * linux/arch/i386/kernel/voyager_smp.c * * This file provides all the same external entries as smp.c but uses * the voyager hal to provide the functionality */#include <linux/module.h>#include <linux/mm.h>#include <linux/kernel_stat.h>#include <linux/delay.h>#include <linux/mc146818rtc.h>#include <linux/cache.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/bootmem.h>#include <linux/completion.h>#include <asm/desc.h>#include <asm/voyager.h>#include <asm/vic.h>#include <asm/mtrr.h>#include <asm/pgalloc.h>#include <asm/tlbflush.h>#include <asm/arch_hooks.h>/* TLB state -- visible externally, indexed physically */DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm, 0 };/* CPU IRQ affinity -- set to all ones initially */static unsigned long cpu_irq_affinity[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = ~0UL };/* per CPU data structure (for /proc/cpuinfo et al), visible externally * indexed physically */DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuinfo_x86, cpu_info);EXPORT_PER_CPU_SYMBOL(cpu_info);/* physical ID of the CPU used to boot the system */unsigned char boot_cpu_id;/* The memory line addresses for the Quad CPIs */struct voyager_qic_cpi *voyager_quad_cpi_addr[NR_CPUS] __cacheline_aligned;/* The masks for the Extended VIC processors, filled in by cat_init */__u32 voyager_extended_vic_processors = 0;/* Masks for the extended Quad processors which cannot be VIC booted */__u32 voyager_allowed_boot_processors = 0;/* The mask for the Quad Processors (both extended and non-extended) */__u32 voyager_quad_processors = 0;/* Total count of live CPUs, used in process.c to display * the CPU information and in irq.c for the per CPU irq * activity count. Finally exported by i386_ksyms.c */static int voyager_extended_cpus = 1;/* Have we found an SMP box - used by time.c to do the profiling interrupt for timeslicing; do not set to 1 until the per CPU timer interrupt is active */int smp_found_config = 0;/* Used for the invalidate map that's also checked in the spinlock */static volatile unsigned long smp_invalidate_needed;/* Bitmask of currently online CPUs - used by setup.c for /proc/cpuinfo, visible externally but still physical */cpumask_t cpu_online_map = CPU_MASK_NONE;EXPORT_SYMBOL(cpu_online_map);/* Bitmask of CPUs present in the system - exported by i386_syms.c, used * by scheduler but indexed physically */cpumask_t phys_cpu_present_map = CPU_MASK_NONE;/* The internal functions */static void send_CPI(__u32 cpuset, __u8 cpi);static void ack_CPI(__u8 cpi);static int ack_QIC_CPI(__u8 cpi);static void ack_special_QIC_CPI(__u8 cpi);static void ack_VIC_CPI(__u8 cpi);static void send_CPI_allbutself(__u8 cpi);static void mask_vic_irq(unsigned int irq);static void unmask_vic_irq(unsigned int irq);static unsigned int startup_vic_irq(unsigned int irq);static void enable_local_vic_irq(unsigned int irq);static void disable_local_vic_irq(unsigned int irq);static void before_handle_vic_irq(unsigned int irq);static void after_handle_vic_irq(unsigned int irq);static void set_vic_irq_affinity(unsigned int irq, cpumask_t mask);static void ack_vic_irq(unsigned int irq);static void vic_enable_cpi(void);static void do_boot_cpu(__u8 cpuid);static void do_quad_bootstrap(void);int hard_smp_processor_id(void);int safe_smp_processor_id(void);/* Inline functions */static inline voidsend_one_QIC_CPI(__u8 cpu, __u8 cpi){ voyager_quad_cpi_addr[cpu]->qic_cpi[cpi].cpi = (smp_processor_id() << 16) + cpi;}static inline voidsend_QIC_CPI(__u32 cpuset, __u8 cpi){ int cpu; for_each_online_cpu(cpu) { if(cpuset & (1<<cpu)) {#ifdef VOYAGER_DEBUG if(!cpu_isset(cpu, cpu_online_map)) VDEBUG(("CPU%d sending cpi %d to CPU%d not in cpu_online_map\n", hard_smp_processor_id(), cpi, cpu));#endif send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET); } }}static inline voidwrapper_smp_local_timer_interrupt(void){ irq_enter(); smp_local_timer_interrupt(); irq_exit();}static inline voidsend_one_CPI(__u8 cpu, __u8 cpi){ if(voyager_quad_processors & (1<<cpu)) send_one_QIC_CPI(cpu, cpi - QIC_CPI_OFFSET); else send_CPI(1<<cpu, cpi);}static inline voidsend_CPI_allbutself(__u8 cpi){ __u8 cpu = smp_processor_id(); __u32 mask = cpus_addr(cpu_online_map)[0] & ~(1 << cpu); send_CPI(mask, cpi);}static inline intis_cpu_quad(void){ __u8 cpumask = inb(VIC_PROC_WHO_AM_I); return ((cpumask & QUAD_IDENTIFIER) == QUAD_IDENTIFIER);}static inline intis_cpu_extended(void){ __u8 cpu = hard_smp_processor_id(); return(voyager_extended_vic_processors & (1<<cpu));}static inline intis_cpu_vic_boot(void){ __u8 cpu = hard_smp_processor_id(); return(voyager_extended_vic_processors & voyager_allowed_boot_processors & (1<<cpu));}static inline voidack_CPI(__u8 cpi){ switch(cpi) { case VIC_CPU_BOOT_CPI: if(is_cpu_quad() && !is_cpu_vic_boot()) ack_QIC_CPI(cpi); else ack_VIC_CPI(cpi); break; case VIC_SYS_INT: case VIC_CMN_INT: /* These are slightly strange. Even on the Quad card, * They are vectored as VIC CPIs */ if(is_cpu_quad()) ack_special_QIC_CPI(cpi); else ack_VIC_CPI(cpi); break; default: printk("VOYAGER ERROR: CPI%d is in common CPI code\n", cpi); break; }}/* local variables *//* The VIC IRQ descriptors -- these look almost identical to the * 8259 IRQs except that masks and things must be kept per processor */static struct irq_chip vic_chip = { .name = "VIC", .startup = startup_vic_irq, .mask = mask_vic_irq, .unmask = unmask_vic_irq, .set_affinity = set_vic_irq_affinity,};/* used to count up as CPUs are brought on line (starts at 0) */static int cpucount = 0;/* steal a page from the bottom of memory for the trampoline and * squirrel its address away here. This will be in kernel virtual * space */static __u32 trampoline_base;/* The per cpu profile stuff - used in smp_local_timer_interrupt */static DEFINE_PER_CPU(int, prof_multiplier) = 1;static DEFINE_PER_CPU(int, prof_old_multiplier) = 1;static DEFINE_PER_CPU(int, prof_counter) = 1;/* the map used to check if a CPU has booted */static __u32 cpu_booted_map;/* the synchronize flag used to hold all secondary CPUs spinning in * a tight loop until the boot sequence is ready for them */static cpumask_t smp_commenced_mask = CPU_MASK_NONE;/* This is for the new dynamic CPU boot code */cpumask_t cpu_callin_map = CPU_MASK_NONE;cpumask_t cpu_callout_map = CPU_MASK_NONE;EXPORT_SYMBOL(cpu_callout_map);cpumask_t cpu_possible_map = CPU_MASK_NONE;EXPORT_SYMBOL(cpu_possible_map);/* The per processor IRQ masks (these are usually kept in sync) */static __u16 vic_irq_mask[NR_CPUS] __cacheline_aligned;/* the list of IRQs to be enabled by the VIC_ENABLE_IRQ_CPI */static __u16 vic_irq_enable_mask[NR_CPUS] __cacheline_aligned = { 0 };/* Lock for enable/disable of VIC interrupts */static __cacheline_aligned DEFINE_SPINLOCK(vic_irq_lock);/* The boot processor is correctly set up in PC mode when it * comes up, but the secondaries need their master/slave 8259 * pairs initializing correctly *//* Interrupt counters (per cpu) and total - used to try to * even up the interrupt handling routines */static long vic_intr_total = 0;static long vic_intr_count[NR_CPUS] __cacheline_aligned = { 0 };static unsigned long vic_tick[NR_CPUS] __cacheline_aligned = { 0 };/* Since we can only use CPI0, we fake all the other CPIs */static unsigned long vic_cpi_mailbox[NR_CPUS] __cacheline_aligned;/* debugging routine to read the isr of the cpu's pic */static inline __u16vic_read_isr(void){ __u16 isr; outb(0x0b, 0xa0); isr = inb(0xa0) << 8; outb(0x0b, 0x20); isr |= inb(0x20); return isr;}static __init voidqic_setup(void){ if(!is_cpu_quad()) { /* not a quad, no setup */ return; } outb(QIC_DEFAULT_MASK0, QIC_MASK_REGISTER0); outb(QIC_CPI_ENABLE, QIC_MASK_REGISTER1); if(is_cpu_extended()) { /* the QIC duplicate of the VIC base register */ outb(VIC_DEFAULT_CPI_BASE, QIC_VIC_CPI_BASE_REGISTER); outb(QIC_DEFAULT_CPI_BASE, QIC_CPI_BASE_REGISTER); /* FIXME: should set up the QIC timer and memory parity * error vectors here */ }}static __init voidvic_setup_pic(void){ outb(1, VIC_REDIRECT_REGISTER_1); /* clear the claim registers for dynamic routing */ outb(0, VIC_CLAIM_REGISTER_0); outb(0, VIC_CLAIM_REGISTER_1); outb(0, VIC_PRIORITY_REGISTER); /* Set the Primary and Secondary Microchannel vector * bases to be the same as the ordinary interrupts * * FIXME: This would be more efficient using separate * vectors. */ outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE); outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE); /* Now initiallise the master PIC belonging to this CPU by * sending the four ICWs */ /* ICW1: level triggered, ICW4 needed */ outb(0x19, 0x20); /* ICW2: vector base */ outb(FIRST_EXTERNAL_VECTOR, 0x21); /* ICW3: slave at line 2 */ outb(0x04, 0x21); /* ICW4: 8086 mode */ outb(0x01, 0x21); /* now the same for the slave PIC */ /* ICW1: level trigger, ICW4 needed */ outb(0x19, 0xA0); /* ICW2: slave vector base */ outb(FIRST_EXTERNAL_VECTOR + 8, 0xA1); /* ICW3: slave ID */ outb(0x02, 0xA1); /* ICW4: 8086 mode */ outb(0x01, 0xA1);}static voiddo_quad_bootstrap(void){ if(is_cpu_quad() && is_cpu_vic_boot()) { int i; unsigned long flags; __u8 cpuid = hard_smp_processor_id(); local_irq_save(flags); for(i = 0; i<4; i++) { /* FIXME: this would be >>3 &0x7 on the 32 way */ if(((cpuid >> 2) & 0x03) == i) /* don't lower our own mask! */ continue; /* masquerade as local Quad CPU */ outb(QIC_CPUID_ENABLE | i, QIC_PROCESSOR_ID); /* enable the startup CPI */ outb(QIC_BOOT_CPI_MASK, QIC_MASK_REGISTER1); /* restore cpu id */ outb(0, QIC_PROCESSOR_ID); } local_irq_restore(flags); }}/* Set up all the basic stuff: read the SMP config and make all the * SMP information reflect only the boot cpu. All others will be * brought on-line later. */void __init find_smp_config(void){ int i; boot_cpu_id = hard_smp_processor_id(); printk("VOYAGER SMP: Boot cpu is %d\n", boot_cpu_id); /* initialize the CPU structures (moved from smp_boot_cpus) */ for(i=0; i<NR_CPUS; i++) { cpu_irq_affinity[i] = ~0; } cpu_online_map = cpumask_of_cpu(boot_cpu_id); /* The boot CPU must be extended */ voyager_extended_vic_processors = 1<<boot_cpu_id; /* initially, all of the first 8 CPUs can boot */ voyager_allowed_boot_processors = 0xff; /* set up everything for just this CPU, we can alter * this as we start the other CPUs later */ /* now get the CPU disposition from the extended CMOS */ cpus_addr(phys_cpu_present_map)[0] = voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK); cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 1) << 8; cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 2) << 16; cpus_addr(phys_cpu_present_map)[0] |= voyager_extended_cmos_read(VOYAGER_PROCESSOR_PRESENT_MASK + 3) << 24; cpu_possible_map = phys_cpu_present_map; printk("VOYAGER SMP: phys_cpu_present_map = 0x%lx\n", cpus_addr(phys_cpu_present_map)[0]); /* Here we set up the VIC to enable SMP */ /* enable the CPIs by writing the base vector to their register */ outb(VIC_DEFAULT_CPI_BASE, VIC_CPI_BASE_REGISTER); outb(1, VIC_REDIRECT_REGISTER_1); /* set the claim registers for static routing --- Boot CPU gets * all interrupts untill all other CPUs started */ outb(0xff, VIC_CLAIM_REGISTER_0); outb(0xff, VIC_CLAIM_REGISTER_1); /* Set the Primary and Secondary Microchannel vector * bases to be the same as the ordinary interrupts * * FIXME: This would be more efficient using separate * vectors. */ outb(FIRST_EXTERNAL_VECTOR, VIC_PRIMARY_MC_BASE); outb(FIRST_EXTERNAL_VECTOR, VIC_SECONDARY_MC_BASE); /* Finally tell the firmware that we're driving */ outb(inb(VOYAGER_SUS_IN_CONTROL_PORT) | VOYAGER_IN_CONTROL_FLAG, VOYAGER_SUS_IN_CONTROL_PORT); current_thread_info()->cpu = boot_cpu_id; x86_write_percpu(cpu_number, boot_cpu_id);}/* * The bootstrap kernel entry code has set these up. Save them * for a given CPU, id is physical */void __initsmp_store_cpu_info(int id){ struct cpuinfo_x86 *c = &cpu_data(id); *c = boot_cpu_data; identify_secondary_cpu(c);}/* set up the trampoline and return the physical address of the code */static __u32 __initsetup_trampoline(void){ /* these two are global symbols in trampoline.S */ extern const __u8 trampoline_end[]; extern const __u8 trampoline_data[]; memcpy((__u8 *)trampoline_base, trampoline_data, trampoline_end - trampoline_data); return virt_to_phys((__u8 *)trampoline_base);}/* Routine initially called when a non-boot CPU is brought online */static void __initstart_secondary(void *unused){ __u8 cpuid = hard_smp_processor_id(); /* external functions not defined in the headers */ extern void calibrate_delay(void); cpu_init(); /* OK, we're in the routine */ ack_CPI(VIC_CPU_BOOT_CPI); /* setup the 8259 master slave pair belonging to this CPU --- * we won't actually receive any until the boot CPU * relinquishes it's static routing mask */ vic_setup_pic(); qic_setup(); if(is_cpu_quad() && !is_cpu_vic_boot()) { /* clear the boot CPI */ __u8 dummy; dummy = voyager_quad_cpi_addr[cpuid]->qic_cpi[VIC_CPU_BOOT_CPI].cpi; printk("read dummy %d\n", dummy); } /* lower the mask to receive CPIs */ vic_enable_cpi(); VDEBUG(("VOYAGER SMP: CPU%d, stack at about %p\n", cpuid, &cpuid)); /* enable interrupts */ local_irq_enable(); /* get our bogomips */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -