[IA64] cpu hotplug: return offlined cpus to SAL
[linux-3.10.git] / arch / ia64 / kernel / head.S
index 105c7fec8c6d20b128631c1e46fb4c892cd274f9..0d535d65eea6aa98bcd6e4e4b4f797c1fde76d7b 100644 (file)
@@ -15,6 +15,8 @@
  * Copyright (C) 1999 Don Dugger <Don.Dugger@intel.com>
  * Copyright (C) 2002 Fenghua Yu <fenghua.yu@intel.com>
  *   -Optimize __ia64_save_fpu() and __ia64_load_fpu() for Itanium 2.
+ * Copyright (C) 2004 Ashok Raj <ashok.raj@intel.com>
+ *   Support for CPU Hotplug
  */
 
 #include <linux/config.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/system.h>
+#include <asm/mca_asm.h>
+
+#ifdef CONFIG_HOTPLUG_CPU
+#define SAL_PSR_BITS_TO_SET                            \
+       (IA64_PSR_AC | IA64_PSR_BN | IA64_PSR_MFH | IA64_PSR_MFL)
+
+#define SAVE_FROM_REG(src, ptr, dest)  \
+       mov dest=src;;                                          \
+       st8 [ptr]=dest,0x08
+
+#define RESTORE_REG(reg, ptr, _tmp)            \
+       ld8 _tmp=[ptr],0x08;;                           \
+       mov reg=_tmp
+
+#define SAVE_BREAK_REGS(ptr, _idx, _breg, _dest)\
+       mov ar.lc=IA64_NUM_DBG_REGS-1;;                         \
+       mov _idx=0;;                                                            \
+1:                                                                                             \
+       SAVE_FROM_REG(_breg[_idx], ptr, _dest);;        \
+       add _idx=1,_idx;;                                                       \
+       br.cloop.sptk.many 1b
+
+#define RESTORE_BREAK_REGS(ptr, _idx, _breg, _tmp, _lbl)\
+       mov ar.lc=IA64_NUM_DBG_REGS-1;;                 \
+       mov _idx=0;;                                                    \
+_lbl:  RESTORE_REG(_breg[_idx], ptr, _tmp);;   \
+       add _idx=1, _idx;;                                              \
+       br.cloop.sptk.many _lbl
+
+#define SAVE_ONE_RR(num, _reg, _tmp) \
+       movl _tmp=(num<<61);;   \
+       mov _reg=rr[_tmp]
+
+#define SAVE_REGION_REGS(_tmp, _r0, _r1, _r2, _r3, _r4, _r5, _r6, _r7) \
+       SAVE_ONE_RR(0,_r0, _tmp);; \
+       SAVE_ONE_RR(1,_r1, _tmp);; \
+       SAVE_ONE_RR(2,_r2, _tmp);; \
+       SAVE_ONE_RR(3,_r3, _tmp);; \
+       SAVE_ONE_RR(4,_r4, _tmp);; \
+       SAVE_ONE_RR(5,_r5, _tmp);; \
+       SAVE_ONE_RR(6,_r6, _tmp);; \
+       SAVE_ONE_RR(7,_r7, _tmp);;
+
+#define STORE_REGION_REGS(ptr, _r0, _r1, _r2, _r3, _r4, _r5, _r6, _r7) \
+       st8 [ptr]=_r0, 8;; \
+       st8 [ptr]=_r1, 8;; \
+       st8 [ptr]=_r2, 8;; \
+       st8 [ptr]=_r3, 8;; \
+       st8 [ptr]=_r4, 8;; \
+       st8 [ptr]=_r5, 8;; \
+       st8 [ptr]=_r6, 8;; \
+       st8 [ptr]=_r7, 8;;
+
+#define RESTORE_REGION_REGS(ptr, _idx1, _idx2, _tmp) \
+       mov             ar.lc=0x08-1;;                                          \
+       movl    _idx1=0x00;;                                            \
+RestRR:                                                                                        \
+       dep.z   _idx2=_idx1,61,3;;                                      \
+       ld8             _tmp=[ptr],8;;                                          \
+       mov             rr[_idx2]=_tmp;;                                        \
+       srlz.d;;                                                                        \
+       add             _idx1=1,_idx1;;                                         \
+       br.cloop.sptk.few       RestRR
+
+/*
+ * Adjust region registers saved before starting to save
+ * break regs and rest of the states that need to be preserved.
+ */
+#define SAL_TO_OS_BOOT_HANDOFF_STATE_SAVE(_reg1,_reg2,_pred)  \
+       SAVE_FROM_REG(b0,_reg1,_reg2);;                                         \
+       SAVE_FROM_REG(b1,_reg1,_reg2);;                                         \
+       SAVE_FROM_REG(b2,_reg1,_reg2);;                                         \
+       SAVE_FROM_REG(b3,_reg1,_reg2);;                                         \
+       SAVE_FROM_REG(b4,_reg1,_reg2);;                                         \
+       SAVE_FROM_REG(b5,_reg1,_reg2);;                                         \
+       st8 [_reg1]=r1,0x08;;                                                           \
+       st8 [_reg1]=r12,0x08;;                                                          \
+       st8 [_reg1]=r13,0x08;;                                                          \
+       SAVE_FROM_REG(ar.fpsr,_reg1,_reg2);;                            \
+       SAVE_FROM_REG(ar.pfs,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(ar.rnat,_reg1,_reg2);;                            \
+       SAVE_FROM_REG(ar.unat,_reg1,_reg2);;                            \
+       SAVE_FROM_REG(ar.bspstore,_reg1,_reg2);;                        \
+       SAVE_FROM_REG(cr.dcr,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(cr.iva,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(cr.pta,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(cr.itv,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(cr.pmv,_reg1,_reg2);;                                     \
+       SAVE_FROM_REG(cr.cmcv,_reg1,_reg2);;                            \
+       SAVE_FROM_REG(cr.lrr0,_reg1,_reg2);;                            \
+       SAVE_FROM_REG(cr.lrr1,_reg1,_reg2);;                            \
+       st8 [_reg1]=r4,0x08;;                                                           \
+       st8 [_reg1]=r5,0x08;;                                                           \
+       st8 [_reg1]=r6,0x08;;                                                           \
+       st8 [_reg1]=r7,0x08;;                                                           \
+       st8 [_reg1]=_pred,0x08;;                                                        \
+       SAVE_FROM_REG(ar.lc, _reg1, _reg2);;                            \
+       stf.spill.nta [_reg1]=f2,16;;                                           \
+       stf.spill.nta [_reg1]=f3,16;;                                           \
+       stf.spill.nta [_reg1]=f4,16;;                                           \
+       stf.spill.nta [_reg1]=f5,16;;                                           \
+       stf.spill.nta [_reg1]=f16,16;;                                          \
+       stf.spill.nta [_reg1]=f17,16;;                                          \
+       stf.spill.nta [_reg1]=f18,16;;                                          \
+       stf.spill.nta [_reg1]=f19,16;;                                          \
+       stf.spill.nta [_reg1]=f20,16;;                                          \
+       stf.spill.nta [_reg1]=f21,16;;                                          \
+       stf.spill.nta [_reg1]=f22,16;;                                          \
+       stf.spill.nta [_reg1]=f23,16;;                                          \
+       stf.spill.nta [_reg1]=f24,16;;                                          \
+       stf.spill.nta [_reg1]=f25,16;;                                          \
+       stf.spill.nta [_reg1]=f26,16;;                                          \
+       stf.spill.nta [_reg1]=f27,16;;                                          \
+       stf.spill.nta [_reg1]=f28,16;;                                          \
+       stf.spill.nta [_reg1]=f29,16;;                                          \
+       stf.spill.nta [_reg1]=f30,16;;                                          \
+       stf.spill.nta [_reg1]=f31,16;;
+
+#else
+#define SAL_TO_OS_BOOT_HANDOFF_STATE_SAVE(a1,a2)
+#define SAVE_REGION_REGS(_tmp, _r0, _r1, _r2, _r3, _r4, _r5, _r6, _r7)
+#define STORE_REGION_REGS(ptr, _r0, _r1, _r2, _r3, _r4, _r5, _r6, _r7)
+#endif
+
+#define SET_ONE_RR(num, pgsize, _tmp1, _tmp2, vhpt) \
+       movl _tmp1=(num << 61);;        \
+       mov _tmp2=((ia64_rid(IA64_REGION_ID_KERNEL, (num<<61)) << 8) | (pgsize << 2) | vhpt);; \
+       mov rr[_tmp1]=_tmp2
 
        .section __special_page_section,"ax"
 
@@ -63,6 +193,12 @@ start_ap:
        ;;
        srlz.i
        ;;
+       /*
+        * Save the region registers, predicate before they get clobbered
+        */
+       SAVE_REGION_REGS(r2, r8,r9,r10,r11,r12,r13,r14,r15);
+       mov r25=pr;;
+
        /*
         * Initialize kernel region registers:
         *      rr[0]: VHPT enabled, page size = PAGE_SHIFT
@@ -76,32 +212,14 @@ start_ap:
         * We initialize all of them to prevent inadvertently assuming
         * something about the state of address translation early in boot.
         */
-       mov r6=((ia64_rid(IA64_REGION_ID_KERNEL, (0<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r7=(0<<61)
-       mov r8=((ia64_rid(IA64_REGION_ID_KERNEL, (1<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r9=(1<<61)
-       mov r10=((ia64_rid(IA64_REGION_ID_KERNEL, (2<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r11=(2<<61)
-       mov r12=((ia64_rid(IA64_REGION_ID_KERNEL, (3<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r13=(3<<61)
-       mov r14=((ia64_rid(IA64_REGION_ID_KERNEL, (4<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r15=(4<<61)
-       mov r16=((ia64_rid(IA64_REGION_ID_KERNEL, (5<<61)) << 8) | (PAGE_SHIFT << 2) | 1)
-       movl r17=(5<<61)
-       mov r18=((ia64_rid(IA64_REGION_ID_KERNEL, (6<<61)) << 8) | (IA64_GRANULE_SHIFT << 2))
-       movl r19=(6<<61)
-       mov r20=((ia64_rid(IA64_REGION_ID_KERNEL, (7<<61)) << 8) | (IA64_GRANULE_SHIFT << 2))
-       movl r21=(7<<61)
-       ;;
-       mov rr[r7]=r6
-       mov rr[r9]=r8
-       mov rr[r11]=r10
-       mov rr[r13]=r12
-       mov rr[r15]=r14
-       mov rr[r17]=r16
-       mov rr[r19]=r18
-       mov rr[r21]=r20
-       ;;
+       SET_ONE_RR(0, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(1, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(2, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(3, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(4, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(5, PAGE_SHIFT, r2, r16, 1);;
+       SET_ONE_RR(6, IA64_GRANULE_SHIFT, r2, r16, 0);;
+       SET_ONE_RR(7, IA64_GRANULE_SHIFT, r2, r16, 0);;
        /*
         * Now pin mappings into the TLB for kernel text and data
         */
@@ -142,6 +260,13 @@ start_ap:
        ;;
 1:     // now we are in virtual mode
 
+       movl r2=sal_state_for_booting_cpu;;
+       ld8 r16=[r2];;
+
+       STORE_REGION_REGS(r16, r8,r9,r10,r11,r12,r13,r14,r15);
+       SAL_TO_OS_BOOT_HANDOFF_STATE_SAVE(r16,r17,r25)
+       ;;
+
        // set IVT entry point---can't access I/O ports without it
        movl r3=ia64_ivt
        ;;
@@ -211,12 +336,13 @@ start_ap:
        mov IA64_KR(CURRENT_STACK)=r16
        mov r13=r2
        /*
-        * Reserve space at the top of the stack for "struct pt_regs".  Kernel threads
-        * don't store interesting values in that structure, but the space still needs
-        * to be there because time-critical stuff such as the context switching can
-        * be implemented more efficiently (for example, __switch_to()
+        * Reserve space at the top of the stack for "struct pt_regs".  Kernel
+        * threads don't store interesting values in that structure, but the space
+        * still needs to be there because time-critical stuff such as the context
+        * switching can be implemented more efficiently (for example, __switch_to()
         * always sets the psr.dfh bit of the task it is switching to).
         */
+
        addl r12=IA64_STK_OFFSET-IA64_PT_REGS_SIZE-16,r2
        addl r2=IA64_RBS_OFFSET,r2      // initialize the RSE
        mov ar.rsc=0            // place RSE in enforced lazy mode
@@ -993,4 +1119,98 @@ END(ia64_spinlock_contention)
 
 #endif
 
+#ifdef CONFIG_HOTPLUG_CPU
+GLOBAL_ENTRY(ia64_jump_to_sal)
+       alloc r16=ar.pfs,1,0,0,0;;
+       rsm psr.i  | psr.ic
+{
+       flushrs
+       srlz.i
+}
+       tpa r25=in0
+       movl r18=tlb_purge_done;;
+       DATA_VA_TO_PA(r18);;
+       mov b1=r18      // Return location
+       movl r18=ia64_do_tlb_purge;;
+       DATA_VA_TO_PA(r18);;
+       mov b2=r18      // doing tlb_flush work
+       mov ar.rsc=0  // Put RSE  in enforced lazy, LE mode
+       movl r17=1f;;
+       DATA_VA_TO_PA(r17);;
+       mov cr.iip=r17
+       movl r16=SAL_PSR_BITS_TO_SET;;
+       mov cr.ipsr=r16
+       mov cr.ifs=r0;;
+       rfi;;
+1:
+       /*
+        * Invalidate all TLB data/inst
+        */
+       br.sptk.many b2;; // jump to tlb purge code
+
+tlb_purge_done:
+       RESTORE_REGION_REGS(r25, r17,r18,r19);;
+       RESTORE_REG(b0, r25, r17);;
+       RESTORE_REG(b1, r25, r17);;
+       RESTORE_REG(b2, r25, r17);;
+       RESTORE_REG(b3, r25, r17);;
+       RESTORE_REG(b4, r25, r17);;
+       RESTORE_REG(b5, r25, r17);;
+       ld8 r1=[r25],0x08;;
+       ld8 r12=[r25],0x08;;
+       ld8 r13=[r25],0x08;;
+       RESTORE_REG(ar.fpsr, r25, r17);;
+       RESTORE_REG(ar.pfs, r25, r17);;
+       RESTORE_REG(ar.rnat, r25, r17);;
+       RESTORE_REG(ar.unat, r25, r17);;
+       RESTORE_REG(ar.bspstore, r25, r17);;
+       RESTORE_REG(cr.dcr, r25, r17);;
+       RESTORE_REG(cr.iva, r25, r17);;
+       RESTORE_REG(cr.pta, r25, r17);;
+       RESTORE_REG(cr.itv, r25, r17);;
+       RESTORE_REG(cr.pmv, r25, r17);;
+       RESTORE_REG(cr.cmcv, r25, r17);;
+       RESTORE_REG(cr.lrr0, r25, r17);;
+       RESTORE_REG(cr.lrr1, r25, r17);;
+       ld8 r4=[r25],0x08;;
+       ld8 r5=[r25],0x08;;
+       ld8 r6=[r25],0x08;;
+       ld8 r7=[r25],0x08;;
+       ld8 r17=[r25],0x08;;
+       mov pr=r17,-1;;
+       RESTORE_REG(ar.lc, r25, r17);;
+       /*
+        * Now Restore floating point regs
+        */
+       ldf.fill.nta f2=[r25],16;;
+       ldf.fill.nta f3=[r25],16;;
+       ldf.fill.nta f4=[r25],16;;
+       ldf.fill.nta f5=[r25],16;;
+       ldf.fill.nta f16=[r25],16;;
+       ldf.fill.nta f17=[r25],16;;
+       ldf.fill.nta f18=[r25],16;;
+       ldf.fill.nta f19=[r25],16;;
+       ldf.fill.nta f20=[r25],16;;
+       ldf.fill.nta f21=[r25],16;;
+       ldf.fill.nta f22=[r25],16;;
+       ldf.fill.nta f23=[r25],16;;
+       ldf.fill.nta f24=[r25],16;;
+       ldf.fill.nta f25=[r25],16;;
+       ldf.fill.nta f26=[r25],16;;
+       ldf.fill.nta f27=[r25],16;;
+       ldf.fill.nta f28=[r25],16;;
+       ldf.fill.nta f29=[r25],16;;
+       ldf.fill.nta f30=[r25],16;;
+       ldf.fill.nta f31=[r25],16;;
+
+       /*
+        * Now that we have done all the register restores
+        * we are now ready for the big DIVE to SAL Land
+        */
+       ssm psr.ic;;
+       srlz.d;;
+       br.ret.sptk.many b0;;
+END(ia64_jump_to_sal)
+#endif /* CONFIG_HOTPLUG_CPU */
+
 #endif /* CONFIG_SMP */