mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
remove ia64
It retains IA64-specific bits in code imported from elsewhere (e.g. ACPI, EFI) as well as in the public headers. It also doesn't touch the tools, mini-os, and unmodified_drivers sub-trees. Signed-off-by: Jan Beulich <jbeulich@suse.com> Acked-by: Keir Fraser <keir@xen.org>
This commit is contained in:
1 parent
62872b0f03
commit
570c311ca2
488 files changed
+24
-117309
No files matched your search
@@ -138,14 +138,6 @@ M: Tim Deegan <tim@xen.org>
|
||||
S: Supported
|
||||
F: tools/debugger/kdd/
|
||||
|
||||
IA64 ARCHITECTURE
|
||||
M: KUWAMURA Shin'ya <kuwa@jp.fujitsu.com>
|
||||
S: Supported
|
||||
L: xen-ia64-devel@lists.xensource.com
|
||||
F: xen/arch/ia64/*
|
||||
F: xen/include/asm-ia64/*
|
||||
F: tools/libxc/ia64/*
|
||||
|
||||
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
|
||||
M: Joseph Cihula <joseph.cihula@intel.com>
|
||||
M: Gang Wei <gang.wei@intel.com>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
CONFIG_IA64 := y
|
||||
CONFIG_IA64_$(XEN_OS) := y
|
||||
|
||||
CONFIG_IOEMU := y
|
||||
CONFIG_XCUTILS := y
|
||||
CONFIG_XENCOMM := y
|
||||
@@ -1,71 +0,0 @@
|
||||
subdir-y += xen
|
||||
subdir-y += vmx
|
||||
subdir-y += linux
|
||||
subdir-y += linux-xen
|
||||
|
||||
ALL_OBJS := linux-xen/head.o $(ALL_OBJS)
|
||||
|
||||
$(TARGET)-syms: $(ALL_OBJS) xen.lds.s
|
||||
$(MAKE) -f $(BASEDIR)/Rules.mk $(BASEDIR)/common/symbols-dummy.o
|
||||
$(LD) $(LDFLAGS) -T xen.lds.s -N -Map $(@D)/.$(@F).0.map $(ALL_OBJS) \
|
||||
$(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).0
|
||||
$(NM) -n $(@D)/.$(@F).0 | $(BASEDIR)/tools/symbols >$(@D)/.$(@F).0.S
|
||||
$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0.o
|
||||
$(LD) $(LDFLAGS) -T xen.lds.s -N -Map $(@D)/.$(@F).1.map $(ALL_OBJS) \
|
||||
$(@D)/.$(@F).0.o -o $(@D)/.$(@F).1
|
||||
$(NM) -n $(@D)/.$(@F).1 | $(BASEDIR)/tools/symbols >$(@D)/.$(@F).1.S
|
||||
$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1.o
|
||||
$(LD) $(LDFLAGS) -T xen.lds.s -N -Map $@.map $(ALL_OBJS) \
|
||||
$(@D)/.$(@F).1.o -o $@
|
||||
rm -f $(@D)/.$(@F).[0-9]*
|
||||
|
||||
$(TARGET): $(TARGET)-syms
|
||||
$(NM) -n $< | grep -v ' [aUw] ' > $(@D)/System.map
|
||||
$(OBJCOPY) -R .note -R .comment -S $< $@
|
||||
|
||||
# Headers do not depend on auto-generated header, but object files do.
|
||||
$(ALL_OBJS): $(BASEDIR)/include/asm-ia64/asm-xsi-offsets.h
|
||||
|
||||
asm-offsets.s: asm-offsets.c \
|
||||
$(BASEDIR)/include/asm-ia64/.offsets.h.stamp
|
||||
$(CC) $(CFLAGS) -DGENERATE_ASM_OFFSETS -DIA64_TASK_SIZE=0 -S -o $@ $<
|
||||
|
||||
asm-xsi-offsets.s: asm-xsi-offsets.c
|
||||
$(CC) $(CFLAGS) -S -o $@ $<
|
||||
|
||||
$(BASEDIR)/include/asm-ia64/asm-xsi-offsets.h: asm-xsi-offsets.s
|
||||
@(set -e; \
|
||||
echo "/*"; \
|
||||
echo " * DO NOT MODIFY."; \
|
||||
echo " *"; \
|
||||
echo " * This file was auto-generated from $<"; \
|
||||
echo " *"; \
|
||||
echo " */"; \
|
||||
echo ""; \
|
||||
echo "#ifndef __ASM_XSI_OFFSETS_H__"; \
|
||||
echo "#define __ASM_XSI_OFFSETS_H__"; \
|
||||
echo ""; \
|
||||
sed -ne "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"; \
|
||||
echo ""; \
|
||||
echo "#endif") <$< >$@
|
||||
|
||||
$(BASEDIR)/include/asm-ia64/.offsets.h.stamp:
|
||||
# Need such symbol link to make linux headers available
|
||||
[ -e $(BASEDIR)/include/linux ] \
|
||||
|| ln -sf $(BASEDIR)/include/xen $(BASEDIR)/include/linux
|
||||
[ -e $(BASEDIR)/include/asm-ia64/xen ] \
|
||||
|| ln -sf $(BASEDIR)/include/asm-ia64/linux $(BASEDIR)/include/asm-ia64/xen
|
||||
touch $@
|
||||
|
||||
# I'm sure a Makefile wizard would know a better way to do this
|
||||
xen.lds.s: xen/xen.lds.S
|
||||
$(CC) -E $(CPPFLAGS) -P -DXEN $(AFLAGS) \
|
||||
-o xen.lds.s xen/xen.lds.S
|
||||
|
||||
.PHONY: clean
|
||||
clean::
|
||||
rm -f *.o *~ core xen.lds.s $(BASEDIR)/include/asm-ia64/.offsets.h.stamp asm-offsets.s map.out
|
||||
rm -f asm-xsi-offsets.s $(BASEDIR)/include/asm-ia64/asm-xsi-offsets.h
|
||||
rm -f $(BASEDIR)/System.map
|
||||
rm -f $(BASEDIR)/include/linux
|
||||
rm -f $(BASEDIR)/include/asm-ia64/xen
|
||||
@@ -1,93 +0,0 @@
|
||||
########################################
|
||||
# ia64-specific definitions
|
||||
|
||||
ia64 := y
|
||||
HAS_ACPI := y
|
||||
HAS_VGA := y
|
||||
HAS_CPUFREQ := y
|
||||
HAS_PCI := y
|
||||
HAS_PASSTHROUGH := y
|
||||
HAS_NS16550 := y
|
||||
HAS_KEXEC := y
|
||||
xenoprof := y
|
||||
no_warns ?= n
|
||||
vti_debug ?= n
|
||||
vmx_panic ?= n
|
||||
vhpt_disable ?= n
|
||||
xen_ia64_expose_p2m ?= y
|
||||
xen_ia64_pervcpu_vhpt ?= y
|
||||
xen_ia64_tlb_track ?= y
|
||||
xen_ia64_tlb_track_cnt ?= n
|
||||
xen_ia64_tlbflush_clock ?= y
|
||||
xen_ia64_disable_optvfault ?= n
|
||||
|
||||
# If they are enabled,
|
||||
# shrink struct page_info assuming all mfn can be addressed by 32 bits.
|
||||
# However, with 50bit ia64 architected physical address and 16KB page size,
|
||||
# mfn isn't always assessed by 32bit. So they are disabled by default.
|
||||
xen_ia64_shrink_page_list ?= n
|
||||
xen_ia64_pickle_domain ?= n
|
||||
|
||||
# Used only by linux/Makefile.
|
||||
AFLAGS_KERNEL += -mconstant-gp -nostdinc $(CPPFLAGS)
|
||||
|
||||
CFLAGS += -nostdinc -fno-builtin -fno-common
|
||||
CFLAGS += -mconstant-gp
|
||||
#CFLAGS += -O3 # -O3 over-inlines making debugging tough!
|
||||
CFLAGS += -O2 # but no optimization causes compile errors!
|
||||
CFLAGS += -fomit-frame-pointer -D__KERNEL__
|
||||
CFLAGS += -iwithprefix include
|
||||
CPPFLAGS+= -I$(BASEDIR)/include \
|
||||
-I$(BASEDIR)/include/asm-ia64 \
|
||||
-I$(BASEDIR)/include/asm-ia64/linux \
|
||||
-I$(BASEDIR)/include/asm-ia64/linux-xen \
|
||||
-I$(BASEDIR)/include/asm-ia64/linux-null \
|
||||
-I$(BASEDIR)/arch/ia64/linux -I$(BASEDIR)/arch/ia64/linux-xen
|
||||
CFLAGS += $(CPPFLAGS)
|
||||
#CFLAGS += -Wno-pointer-arith -Wredundant-decls
|
||||
CFLAGS += -DIA64 -DXEN -DLINUX_2_6
|
||||
CFLAGS += -ffixed-r13 -mfixed-range=f2-f5,f12-f127,b2-b5
|
||||
CFLAGS += -g
|
||||
ifeq ($(vti_debug),y)
|
||||
CFLAGS += -DVTI_DEBUG
|
||||
endif
|
||||
ifeq ($(vmx_panic),y)
|
||||
CFLAGS += -DCONFIG_VMX_PANIC
|
||||
endif
|
||||
ifeq ($(xen_ia64_expose_p2m),y)
|
||||
CFLAGS += -DCONFIG_XEN_IA64_EXPOSE_P2M
|
||||
endif
|
||||
ifeq ($(xen_ia64_pervcpu_vhpt),y)
|
||||
CFLAGS += -DCONFIG_XEN_IA64_PERVCPU_VHPT
|
||||
ifeq ($(vhpt_disable),y)
|
||||
$(error "both xen_ia64_pervcpu_vhpt=y and vhpt_disable=y are enabled. they can't be enabled simultaneously. disable one of them.")
|
||||
endif
|
||||
endif
|
||||
ifeq ($(xen_ia64_tlb_track),y)
|
||||
CFLAGS += -DCONFIG_XEN_IA64_TLB_TRACK
|
||||
endif
|
||||
ifeq ($(xen_ia64_tlb_track_cnt),y)
|
||||
CFLAGS += -DCONFIG_TLB_TRACK_CNT
|
||||
endif
|
||||
ifeq ($(xen_ia64_tlbflush_clock),y)
|
||||
CFLAGS += -DCONFIG_XEN_IA64_TLBFLUSH_CLOCK
|
||||
endif
|
||||
ifeq ($(no_warns),y)
|
||||
CFLAGS += -Wa,--fatal-warnings -Werror -Wno-uninitialized
|
||||
endif
|
||||
ifneq ($(vhpt_disable),y)
|
||||
CFLAGS += -DVHPT_ENABLED=1
|
||||
else
|
||||
CFLAGS += -DVHPT_ENABLED=0
|
||||
endif
|
||||
ifeq ($(xen_ia64_disable_optvfault),y)
|
||||
CFLAGS += -DCONFIG_XEN_IA64_DISABLE_OPTVFAULT
|
||||
endif
|
||||
ifeq ($(xen_ia64_shrink_page_list),y)
|
||||
CFLAGS += -DCONFIG_IA64_SHRINK_PAGE_LIST
|
||||
endif
|
||||
ifeq ($(xen_ia64_pickle_domain),y)
|
||||
CFLAGS += -DCONFIG_IA64_PICKLE_DOMAIN
|
||||
endif
|
||||
|
||||
LDFLAGS = -g
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* Generate definitions needed by assembly language modules.
|
||||
* This code generates raw asm output which is post-processed
|
||||
* to extract and format the required data.
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/sched.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/mca.h>
|
||||
#include <public/xen.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/regs.h>
|
||||
#include <asm/xenmca.h>
|
||||
|
||||
#define task_struct vcpu
|
||||
|
||||
#define DEFINE(sym, val) \
|
||||
asm volatile("\n->" #sym " (%0) " #val : : "i" (val))
|
||||
|
||||
#define BLANK() asm volatile("\n->" : : )
|
||||
|
||||
#define OFFSET(_sym, _str, _mem) \
|
||||
DEFINE(_sym, offsetof(_str, _mem));
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
DEFINE(IA64_TASK_SIZE, sizeof (struct task_struct));
|
||||
DEFINE(IA64_THREAD_INFO_SIZE, sizeof (struct thread_info));
|
||||
DEFINE(IA64_PT_REGS_SIZE, sizeof (struct pt_regs));
|
||||
DEFINE(IA64_SWITCH_STACK_SIZE, sizeof (struct switch_stack));
|
||||
DEFINE(IA64_CPU_SIZE, sizeof (struct cpuinfo_ia64));
|
||||
DEFINE(UNW_FRAME_INFO_SIZE, sizeof (struct unw_frame_info));
|
||||
DEFINE(MAPPED_REGS_T_SIZE, sizeof (mapped_regs_t));
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_MCA_CPU_INIT_STACK_OFFSET, offsetof (struct ia64_mca_cpu, init_stack));
|
||||
|
||||
BLANK();
|
||||
DEFINE(VCPU_VTM_OFFSET_OFS, offsetof(struct vcpu, arch.arch_vmx.vtm.vtm_offset));
|
||||
DEFINE(VCPU_VTM_LAST_ITC_OFS, offsetof(struct vcpu, arch.arch_vmx.vtm.last_itc));
|
||||
DEFINE(VCPU_VRR0_OFS, offsetof(struct vcpu, arch.arch_vmx.vrr[0]));
|
||||
DEFINE(VCPU_ITR0_OFS, offsetof(struct vcpu, arch.itrs[0]));
|
||||
DEFINE(VCPU_CALLBACK_OFS, offsetof(struct vcpu, arch.event_callback_ip));
|
||||
#ifdef VTI_DEBUG
|
||||
DEFINE(IVT_CUR_OFS, offsetof(struct vcpu, arch.arch_vmx.ivt_current));
|
||||
DEFINE(IVT_DBG_OFS, offsetof(struct vcpu, arch.arch_vmx.ivt_debug));
|
||||
DEFINE(IVT_DEBUG_SIZE, sizeof(struct ivt_debug));
|
||||
#endif
|
||||
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
|
||||
DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_TASK_THREAD_KSP_OFFSET, offsetof (struct vcpu, arch._thread.ksp));
|
||||
DEFINE(IA64_TASK_THREAD_ON_USTACK_OFFSET, offsetof (struct vcpu, arch._thread.on_ustack));
|
||||
|
||||
DEFINE(IA64_VCPU_HYPERCALL_CONTINUATION_OFS, offsetof (struct vcpu, arch.hypercall_continuation));
|
||||
DEFINE(IA64_VCPU_FP_PSR_OFFSET, offsetof (struct vcpu, arch.fp_psr));
|
||||
DEFINE(IA64_VCPU_META_RID_DT_OFFSET, offsetof (struct vcpu, arch.metaphysical_rid_dt));
|
||||
DEFINE(IA64_VCPU_META_RID_D_OFFSET, offsetof (struct vcpu, arch.metaphysical_rid_d));
|
||||
DEFINE(IA64_VCPU_META_SAVED_RR0_OFFSET, offsetof (struct vcpu, arch.metaphysical_saved_rr0));
|
||||
DEFINE(IA64_VCPU_BREAKIMM_OFFSET, offsetof (struct vcpu, arch.breakimm));
|
||||
DEFINE(IA64_VCPU_IVA_OFFSET, offsetof (struct vcpu, arch.iva));
|
||||
DEFINE(IA64_VCPU_EVENT_CALLBACK_IP_OFFSET, offsetof (struct vcpu, arch.event_callback_ip));
|
||||
DEFINE(IA64_VCPU_IRR0_OFFSET, offsetof (struct vcpu, arch.irr[0]));
|
||||
DEFINE(IA64_VCPU_IRR3_OFFSET, offsetof (struct vcpu, arch.irr[3]));
|
||||
DEFINE(IA64_VCPU_INSVC3_OFFSET, offsetof (struct vcpu, arch.insvc[3]));
|
||||
DEFINE(IA64_VCPU_STARTING_RID_OFFSET, offsetof (struct vcpu, arch.starting_rid));
|
||||
DEFINE(IA64_VCPU_ENDING_RID_OFFSET, offsetof (struct vcpu, arch.ending_rid));
|
||||
DEFINE(IA64_VCPU_RID_BITS_OFFSET, offsetof (struct vcpu, arch.rid_bits));
|
||||
DEFINE(IA64_VCPU_DOMAIN_ITM_OFFSET, offsetof (struct vcpu, arch.domain_itm));
|
||||
DEFINE(IA64_VCPU_DOMAIN_ITM_LAST_OFFSET, offsetof (struct vcpu, arch.domain_itm_last));
|
||||
DEFINE(IA64_VCPU_ITLB_OFFSET, offsetof (struct vcpu, arch.itlb));
|
||||
DEFINE(IA64_VCPU_DTLB_OFFSET, offsetof (struct vcpu, arch.dtlb));
|
||||
DEFINE(IA64_VCPU_VHPT_PG_SHIFT_OFFSET, offsetof (struct vcpu, arch.vhpt_pg_shift));
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_VCPU_SHADOW_BITMAP_OFFSET, offsetof (struct vcpu, arch.shadow_bitmap));
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_CPUINFO_ITM_NEXT_OFFSET, offsetof (struct cpuinfo_ia64, itm_next));
|
||||
DEFINE(IA64_CPUINFO_KSOFTIRQD_OFFSET, offsetof (struct cpuinfo_ia64, ksoftirqd));
|
||||
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_PT_REGS_B6_OFFSET, offsetof (struct pt_regs, b6));
|
||||
DEFINE(IA64_PT_REGS_B7_OFFSET, offsetof (struct pt_regs, b7));
|
||||
DEFINE(IA64_PT_REGS_AR_CSD_OFFSET, offsetof (struct pt_regs, ar_csd));
|
||||
DEFINE(IA64_PT_REGS_AR_SSD_OFFSET, offsetof (struct pt_regs, ar_ssd));
|
||||
DEFINE(IA64_PT_REGS_R8_OFFSET, offsetof (struct pt_regs, r8));
|
||||
DEFINE(IA64_PT_REGS_R9_OFFSET, offsetof (struct pt_regs, r9));
|
||||
DEFINE(IA64_PT_REGS_R10_OFFSET, offsetof (struct pt_regs, r10));
|
||||
DEFINE(IA64_PT_REGS_R11_OFFSET, offsetof (struct pt_regs, r11));
|
||||
DEFINE(IA64_PT_REGS_CR_IPSR_OFFSET, offsetof (struct pt_regs, cr_ipsr));
|
||||
DEFINE(IA64_PT_REGS_CR_IIP_OFFSET, offsetof (struct pt_regs, cr_iip));
|
||||
DEFINE(IA64_PT_REGS_CR_IFS_OFFSET, offsetof (struct pt_regs, cr_ifs));
|
||||
DEFINE(IA64_PT_REGS_AR_UNAT_OFFSET, offsetof (struct pt_regs, ar_unat));
|
||||
DEFINE(IA64_PT_REGS_AR_PFS_OFFSET, offsetof (struct pt_regs, ar_pfs));
|
||||
DEFINE(IA64_PT_REGS_AR_RSC_OFFSET, offsetof (struct pt_regs, ar_rsc));
|
||||
DEFINE(IA64_PT_REGS_AR_RNAT_OFFSET, offsetof (struct pt_regs, ar_rnat));
|
||||
|
||||
DEFINE(IA64_PT_REGS_AR_BSPSTORE_OFFSET, offsetof (struct pt_regs, ar_bspstore));
|
||||
DEFINE(IA64_PT_REGS_PR_OFFSET, offsetof (struct pt_regs, pr));
|
||||
DEFINE(IA64_PT_REGS_B0_OFFSET, offsetof (struct pt_regs, b0));
|
||||
DEFINE(IA64_PT_REGS_LOADRS_OFFSET, offsetof (struct pt_regs, loadrs));
|
||||
DEFINE(IA64_PT_REGS_R1_OFFSET, offsetof (struct pt_regs, r1));
|
||||
DEFINE(IA64_PT_REGS_R12_OFFSET, offsetof (struct pt_regs, r12));
|
||||
DEFINE(IA64_PT_REGS_R13_OFFSET, offsetof (struct pt_regs, r13));
|
||||
DEFINE(IA64_PT_REGS_AR_FPSR_OFFSET, offsetof (struct pt_regs, ar_fpsr));
|
||||
DEFINE(IA64_PT_REGS_R15_OFFSET, offsetof (struct pt_regs, r15));
|
||||
DEFINE(IA64_PT_REGS_R14_OFFSET, offsetof (struct pt_regs, r14));
|
||||
DEFINE(IA64_PT_REGS_R2_OFFSET, offsetof (struct pt_regs, r2));
|
||||
DEFINE(IA64_PT_REGS_R3_OFFSET, offsetof (struct pt_regs, r3));
|
||||
DEFINE(IA64_PT_REGS_R16_OFFSET, offsetof (struct pt_regs, r16));
|
||||
DEFINE(IA64_PT_REGS_R17_OFFSET, offsetof (struct pt_regs, r17));
|
||||
DEFINE(IA64_PT_REGS_R18_OFFSET, offsetof (struct pt_regs, r18));
|
||||
DEFINE(IA64_PT_REGS_R19_OFFSET, offsetof (struct pt_regs, r19));
|
||||
DEFINE(IA64_PT_REGS_R20_OFFSET, offsetof (struct pt_regs, r20));
|
||||
DEFINE(IA64_PT_REGS_R21_OFFSET, offsetof (struct pt_regs, r21));
|
||||
DEFINE(IA64_PT_REGS_R22_OFFSET, offsetof (struct pt_regs, r22));
|
||||
DEFINE(IA64_PT_REGS_R23_OFFSET, offsetof (struct pt_regs, r23));
|
||||
DEFINE(IA64_PT_REGS_R24_OFFSET, offsetof (struct pt_regs, r24));
|
||||
DEFINE(IA64_PT_REGS_R25_OFFSET, offsetof (struct pt_regs, r25));
|
||||
DEFINE(IA64_PT_REGS_R26_OFFSET, offsetof (struct pt_regs, r26));
|
||||
DEFINE(IA64_PT_REGS_R27_OFFSET, offsetof (struct pt_regs, r27));
|
||||
DEFINE(IA64_PT_REGS_R28_OFFSET, offsetof (struct pt_regs, r28));
|
||||
DEFINE(IA64_PT_REGS_R29_OFFSET, offsetof (struct pt_regs, r29));
|
||||
DEFINE(IA64_PT_REGS_R30_OFFSET, offsetof (struct pt_regs, r30));
|
||||
DEFINE(IA64_PT_REGS_R31_OFFSET, offsetof (struct pt_regs, r31));
|
||||
DEFINE(IA64_PT_REGS_AR_CCV_OFFSET, offsetof (struct pt_regs, ar_ccv));
|
||||
DEFINE(IA64_PT_REGS_F6_OFFSET, offsetof (struct pt_regs, f6));
|
||||
DEFINE(IA64_PT_REGS_F7_OFFSET, offsetof (struct pt_regs, f7));
|
||||
DEFINE(IA64_PT_REGS_F8_OFFSET, offsetof (struct pt_regs, f8));
|
||||
DEFINE(IA64_PT_REGS_F9_OFFSET, offsetof (struct pt_regs, f9));
|
||||
DEFINE(IA64_PT_REGS_F10_OFFSET, offsetof (struct pt_regs, f10));
|
||||
DEFINE(IA64_PT_REGS_F11_OFFSET, offsetof (struct pt_regs, f11));
|
||||
DEFINE(IA64_PT_REGS_R4_OFFSET, offsetof (struct pt_regs, r4));
|
||||
DEFINE(IA64_PT_REGS_R5_OFFSET, offsetof (struct pt_regs, r5));
|
||||
DEFINE(IA64_PT_REGS_R6_OFFSET, offsetof (struct pt_regs, r6));
|
||||
DEFINE(IA64_PT_REGS_R7_OFFSET, offsetof (struct pt_regs, r7));
|
||||
DEFINE(IA64_PT_REGS_EML_UNAT_OFFSET, offsetof (struct pt_regs, eml_unat));
|
||||
DEFINE(IA64_VCPU_IIPA_OFFSET, offsetof (struct vcpu, arch.arch_vmx.cr_iipa));
|
||||
DEFINE(IA64_VCPU_ISR_OFFSET, offsetof (struct vcpu, arch.arch_vmx.cr_isr));
|
||||
DEFINE(IA64_VCPU_CAUSE_OFFSET, offsetof (struct vcpu, arch.arch_vmx.cause));
|
||||
DEFINE(IA64_VCPU_OPCODE_OFFSET, offsetof (struct vcpu, arch.arch_vmx.opcode));
|
||||
DEFINE(SWITCH_MPTA_OFFSET,offsetof(struct vcpu ,arch.arch_vmx.mpta));
|
||||
DEFINE(IA64_PT_REGS_R16_SLOT, (((offsetof(struct pt_regs, r16)-sizeof(struct pt_regs))>>3)&0x3f));
|
||||
DEFINE(IA64_PT_REGS_R2_SLOT, (((offsetof(struct pt_regs, r16)-sizeof(struct pt_regs))>>3)&0x3f));
|
||||
DEFINE(IA64_PT_REGS_R8_SLOT, (((offsetof(struct pt_regs, r16)-sizeof(struct pt_regs))>>3)&0x3f));
|
||||
DEFINE(IA64_VCPU_FLAGS_OFFSET,offsetof(struct vcpu ,arch.arch_vmx.flags));
|
||||
DEFINE(IA64_VCPU_MMU_MODE_OFFSET,offsetof(struct vcpu, arch.arch_vmx.mmu_mode));
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_SWITCH_STACK_CALLER_UNAT_OFFSET, offsetof (struct switch_stack, caller_unat));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_FPSR_OFFSET, offsetof (struct switch_stack, ar_fpsr));
|
||||
DEFINE(IA64_SWITCH_STACK_F2_OFFSET, offsetof (struct switch_stack, f2));
|
||||
DEFINE(IA64_SWITCH_STACK_F3_OFFSET, offsetof (struct switch_stack, f3));
|
||||
DEFINE(IA64_SWITCH_STACK_F4_OFFSET, offsetof (struct switch_stack, f4));
|
||||
DEFINE(IA64_SWITCH_STACK_F5_OFFSET, offsetof (struct switch_stack, f5));
|
||||
DEFINE(IA64_SWITCH_STACK_F12_OFFSET, offsetof (struct switch_stack, f12));
|
||||
DEFINE(IA64_SWITCH_STACK_F13_OFFSET, offsetof (struct switch_stack, f13));
|
||||
DEFINE(IA64_SWITCH_STACK_F14_OFFSET, offsetof (struct switch_stack, f14));
|
||||
DEFINE(IA64_SWITCH_STACK_F15_OFFSET, offsetof (struct switch_stack, f15));
|
||||
DEFINE(IA64_SWITCH_STACK_F16_OFFSET, offsetof (struct switch_stack, f16));
|
||||
DEFINE(IA64_SWITCH_STACK_F17_OFFSET, offsetof (struct switch_stack, f17));
|
||||
DEFINE(IA64_SWITCH_STACK_F18_OFFSET, offsetof (struct switch_stack, f18));
|
||||
DEFINE(IA64_SWITCH_STACK_F19_OFFSET, offsetof (struct switch_stack, f19));
|
||||
DEFINE(IA64_SWITCH_STACK_F20_OFFSET, offsetof (struct switch_stack, f20));
|
||||
DEFINE(IA64_SWITCH_STACK_F21_OFFSET, offsetof (struct switch_stack, f21));
|
||||
DEFINE(IA64_SWITCH_STACK_F22_OFFSET, offsetof (struct switch_stack, f22));
|
||||
DEFINE(IA64_SWITCH_STACK_F23_OFFSET, offsetof (struct switch_stack, f23));
|
||||
DEFINE(IA64_SWITCH_STACK_F24_OFFSET, offsetof (struct switch_stack, f24));
|
||||
DEFINE(IA64_SWITCH_STACK_F25_OFFSET, offsetof (struct switch_stack, f25));
|
||||
DEFINE(IA64_SWITCH_STACK_F26_OFFSET, offsetof (struct switch_stack, f26));
|
||||
DEFINE(IA64_SWITCH_STACK_F27_OFFSET, offsetof (struct switch_stack, f27));
|
||||
DEFINE(IA64_SWITCH_STACK_F28_OFFSET, offsetof (struct switch_stack, f28));
|
||||
DEFINE(IA64_SWITCH_STACK_F29_OFFSET, offsetof (struct switch_stack, f29));
|
||||
DEFINE(IA64_SWITCH_STACK_F30_OFFSET, offsetof (struct switch_stack, f30));
|
||||
DEFINE(IA64_SWITCH_STACK_F31_OFFSET, offsetof (struct switch_stack, f31));
|
||||
DEFINE(IA64_SWITCH_STACK_R4_OFFSET, offsetof (struct switch_stack, r4));
|
||||
DEFINE(IA64_SWITCH_STACK_R5_OFFSET, offsetof (struct switch_stack, r5));
|
||||
DEFINE(IA64_SWITCH_STACK_R6_OFFSET, offsetof (struct switch_stack, r6));
|
||||
DEFINE(IA64_SWITCH_STACK_R7_OFFSET, offsetof (struct switch_stack, r7));
|
||||
DEFINE(IA64_SWITCH_STACK_B0_OFFSET, offsetof (struct switch_stack, b0));
|
||||
DEFINE(IA64_SWITCH_STACK_B1_OFFSET, offsetof (struct switch_stack, b1));
|
||||
DEFINE(IA64_SWITCH_STACK_B2_OFFSET, offsetof (struct switch_stack, b2));
|
||||
DEFINE(IA64_SWITCH_STACK_B3_OFFSET, offsetof (struct switch_stack, b3));
|
||||
DEFINE(IA64_SWITCH_STACK_B4_OFFSET, offsetof (struct switch_stack, b4));
|
||||
DEFINE(IA64_SWITCH_STACK_B5_OFFSET, offsetof (struct switch_stack, b5));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_PFS_OFFSET, offsetof (struct switch_stack, ar_pfs));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_LC_OFFSET, offsetof (struct switch_stack, ar_lc));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_UNAT_OFFSET, offsetof (struct switch_stack, ar_unat));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_RNAT_OFFSET, offsetof (struct switch_stack, ar_rnat));
|
||||
DEFINE(IA64_SWITCH_STACK_AR_BSPSTORE_OFFSET, offsetof (struct switch_stack, ar_bspstore));
|
||||
DEFINE(IA64_SWITCH_STACK_PR_OFFSET, offsetof (struct switch_stack, pr));
|
||||
|
||||
BLANK();
|
||||
|
||||
DEFINE(IA64_VPD_BASE_OFFSET, offsetof (struct vcpu, arch.privregs));
|
||||
DEFINE(IA64_VPD_VIFS_OFFSET, offsetof (mapped_regs_t, ifs));
|
||||
DEFINE(IA64_VPD_VHPI_OFFSET, offsetof (mapped_regs_t, vhpi));
|
||||
DEFINE(IA64_VPD_VB1REG_OFFSET, offsetof (mapped_regs_t, bank1_regs[0]));
|
||||
DEFINE(IA64_VPD_VB0REG_OFFSET, offsetof (mapped_regs_t, bank0_regs[0]));
|
||||
DEFINE(IA64_VPD_VB1NAT_OFFSET, offsetof (mapped_regs_t, vnat));
|
||||
DEFINE(IA64_VPD_VB0NAT_OFFSET, offsetof (mapped_regs_t, vbnat));
|
||||
DEFINE(IA64_VLSAPIC_INSVC_BASE_OFFSET, offsetof (struct vcpu, arch.insvc[0]));
|
||||
DEFINE(IA64_VPD_VPTA_OFFSET, offsetof (struct mapped_regs, pta));
|
||||
DEFINE(XXX_THASH_SIZE, sizeof (thash_data_t));
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc));
|
||||
DEFINE(IA64_CPUINFO_PTCE_BASE_OFFSET, offsetof (struct cpuinfo_ia64, ptce_base));
|
||||
DEFINE(IA64_CPUINFO_PTCE_COUNT_OFFSET, offsetof (struct cpuinfo_ia64, ptce_count));
|
||||
DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec));
|
||||
|
||||
|
||||
DEFINE(CLONE_IDLETASK_BIT, 12);
|
||||
DEFINE(CLONE_SETTLS_BIT, 19);
|
||||
DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc));
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_KR_CURRENT_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_CURRENT]));
|
||||
DEFINE(IA64_KR_PT_BASE_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_PT_BASE]));
|
||||
DEFINE(IA64_KR_IO_BASE_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_IO_BASE]));
|
||||
DEFINE(IA64_KR_PERCPU_DATA_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_PER_CPU_DATA]));
|
||||
DEFINE(IA64_KR_IO_BASE_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_IO_BASE]));
|
||||
DEFINE(IA64_KR_CURRENT_STACK_OFFSET, offsetof (cpu_kr_ia64_t, _kr[IA64_KR_CURRENT_STACK]));
|
||||
|
||||
#ifdef PERF_COUNTERS
|
||||
BLANK();
|
||||
DEFINE(IA64_PERFC_recover_to_page_fault, PERFC_recover_to_page_fault);
|
||||
DEFINE(IA64_PERFC_recover_to_break_fault, PERFC_recover_to_break_fault);
|
||||
DEFINE(IA64_PERFC_fast_vhpt_translate, PERFC_fast_vhpt_translate);
|
||||
DEFINE(IA64_PERFC_fast_hyperprivop, PERFC_fast_hyperprivop);
|
||||
DEFINE(IA64_PERFC_fast_reflect, PERFC_fast_reflect);
|
||||
#endif
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_CPUINFO_PTCE_BASE_OFFSET,
|
||||
offsetof(struct cpuinfo_ia64, ptce_base));
|
||||
DEFINE(IA64_CPUINFO_PTCE_COUNT_OFFSET,
|
||||
offsetof(struct cpuinfo_ia64, ptce_count));
|
||||
DEFINE(IA64_CPUINFO_PTCE_STRIDE_OFFSET,
|
||||
offsetof(struct cpuinfo_ia64, ptce_stride));
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_MCA_CPU_PROC_STATE_DUMP_OFFSET,
|
||||
offsetof(struct ia64_mca_cpu, proc_state_dump));
|
||||
DEFINE(IA64_MCA_CPU_STACK_OFFSET,
|
||||
offsetof(struct ia64_mca_cpu, stack));
|
||||
DEFINE(IA64_MCA_CPU_STACKFRAME_OFFSET,
|
||||
offsetof(struct ia64_mca_cpu, stackframe));
|
||||
DEFINE(IA64_MCA_CPU_RBSTORE_OFFSET,
|
||||
offsetof(struct ia64_mca_cpu, rbstore));
|
||||
|
||||
#if VHPT_ENABLED
|
||||
DEFINE(IA64_VCPU_VHPT_PAGE_OFFSET,
|
||||
offsetof(struct vcpu, arch.vhpt_page));
|
||||
DEFINE(IA64_VCPU_VHPT_MADDR_OFFSET,
|
||||
offsetof(struct vcpu, arch.vhpt_maddr));
|
||||
#endif
|
||||
|
||||
BLANK();
|
||||
DEFINE(IA64_MCA_TLB_INFO_SIZE, sizeof(struct ia64_mca_tlb_info));
|
||||
DEFINE(IA64_MCA_PERCPU_OFFSET,
|
||||
offsetof(struct ia64_mca_tlb_info, percpu_paddr));
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/* -*- Mode:C; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
* asm-xsi-offsets.c_
|
||||
* Copyright (c) 2005, Intel Corporation.
|
||||
* Kun Tian (Kevin Tian) <kevin.tian@intel.com>
|
||||
* Eddie Dong <eddie.dong@intel.com>
|
||||
* Fred Yang <fred.yang@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Generate definitions needed by assembly language modules.
|
||||
* This code generates raw asm output which is post-processed
|
||||
* to extract and format the required data.
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/sched.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <public/xen.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/regs.h>
|
||||
|
||||
#define task_struct vcpu
|
||||
|
||||
#define DEFINE(sym, val) \
|
||||
asm volatile("\n->" #sym " (%0) " #val : : "i" (val))
|
||||
|
||||
#define BLANK() asm volatile("\n->" : : )
|
||||
|
||||
#define DEFINE_MAPPED_REG_OFS(sym, field) \
|
||||
DEFINE(sym, (XMAPPEDREGS_OFS + offsetof(mapped_regs_t, field)))
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
DEFINE_MAPPED_REG_OFS(XSI_PSR_I_ADDR_OFS, interrupt_mask_addr);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IPSR_OFS, ipsr);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IIP_OFS, iip);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IFS_OFS, ifs);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_PRECOVER_IFS_OFS, precover_ifs);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_ISR_OFS, isr);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IFA_OFS, ifa);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IIPA_OFS, iipa);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IIM_OFS, iim);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_TPR_OFS, tpr);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_IHA_OFS, iha);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_ITIR_OFS, itir);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_ITV_OFS, itv);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_PTA_OFS, pta);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_VPSR_DFH_OFS, vpsr_dfh);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_HPSR_DFH_OFS, hpsr_dfh);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_PSR_IC_OFS, interrupt_collection_enabled);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_VPSR_PP_OFS, vpsr_pp);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_METAPHYS_OFS, metaphysical_mode);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_BANKNUM_OFS, banknum);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_BANK0_R16_OFS, bank0_regs[0]);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_BANK1_R16_OFS, bank1_regs[0]);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_B0NATS_OFS, vbnat);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_B1NATS_OFS, vnat);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_RR0_OFS, rrs[0]);
|
||||
DEFINE_MAPPED_REG_OFS(XSI_KR0_OFS, krs[0]);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
subdir-y += sn
|
||||
|
||||
obj-y += cmdline.o
|
||||
obj-y += efi.o
|
||||
obj-y += entry.o
|
||||
obj-y += irq_ia64.o
|
||||
obj-y += mca.o
|
||||
obj-y += mca_asm.o
|
||||
obj-y += mm_contig.o
|
||||
obj-y += process-linux-xen.o
|
||||
obj-y += sal.o
|
||||
obj-y += setup.o
|
||||
obj-y += smpboot.o
|
||||
obj-y += smp.o
|
||||
obj-y += time.o
|
||||
obj-y += tlb.o
|
||||
obj-y += unaligned.o
|
||||
obj-y += unwind.o
|
||||
obj-y += iosapic.o
|
||||
obj-y += numa.o
|
||||
obj-y += perfmon.o
|
||||
obj-y += perfmon_default_smpl.o
|
||||
obj-y += acpi.o
|
||||
obj-y += acpi_numa.o
|
||||
@@ -1,47 +0,0 @@
|
||||
# Source files in this directory are near-identical copies of linux-2.6.13
|
||||
# files:
|
||||
|
||||
# NOTE: ALL changes to these files should be clearly marked
|
||||
# (e.g. with #ifdef XEN or XEN in a comment) so that they can be
|
||||
# easily updated to future versions of the corresponding Linux files.
|
||||
|
||||
cmdline.c -> linux/lib/cmdline.c
|
||||
entry.h -> linux/arch/ia64/kernel/entry.h
|
||||
entry.S -> linux/arch/ia64/kernel/entry.S
|
||||
head.S -> linux/arch/ia64/kernel/head.S
|
||||
hpsim_ssc.h -> linux/arch/ia64/hp/sim/hpsim_ssc.h
|
||||
irq_ia64.c -> linux/arch/ia64/kernel/irq_ia64.c
|
||||
mca.c -> linux/arch/ia64/kernel/mca.c
|
||||
mca_asm.S -> linux/arch/ia64/kernel/mca_asm.S
|
||||
minstate.h -> linux/arch/ia64/kernel/minstate.h
|
||||
mm_contig.c -> linux/arch/ia64/mm/contig.c
|
||||
numa.c -> linux/arch/ia64/kernel/numa.c
|
||||
process-linux-xen.c -> linux/arch/ia64/kernel/process.c
|
||||
sal.c -> linux/arch/ia64/kernel/sal.c
|
||||
setup.c -> linux/arch/ia64/kernel/setup.c
|
||||
smp.c -> linux/arch/ia64/kernel/smp.c
|
||||
smpboot.c -> linux/arch/ia64/kernel/smpboot.c
|
||||
time.c -> linux/arch/ia64/kernel/time.c
|
||||
tlb.c -> linux/arch/ia64/mm/tlb.c
|
||||
unaligned.c -> linux/arch/ia64/kernel/unaligned.c
|
||||
unwind.c -> linux/arch/ia64/kernel/unwind.c
|
||||
unwind_decoder.c -> linux/arch/ia64/kernel/unwind_decoder.c
|
||||
unwind_i.h -> linux/arch/ia64/kernel/unwind_i.h
|
||||
|
||||
# The files below are from Linux-2.6.16
|
||||
iosapic.c -> linux/arch/ia64/kernel/iosapic.c
|
||||
|
||||
# The files below are from Linux-2.6.16.33
|
||||
perfmon.c -> linux/arch/kernel/perfmon.c
|
||||
perfmon_default_smpl.c -> linux/arch/kernel/perfmon_default_smpl.c
|
||||
perfmon_generic.h -> linux/arch/kernel/perfmon_generic.h
|
||||
perfmon_itanium.h -> linux/arch/kernel/perfmon_itanium.h
|
||||
perfmon_mckinley.h -> linux/arch/kernel/perfmon_mckinley.h
|
||||
perfmon_montecito.h -> linux/arch/kernel/perfmon_montecito.h
|
||||
|
||||
# The files below are from Linux-2.6.21
|
||||
efi.c -> linux/arch/ia64/kernel/efi.c
|
||||
|
||||
# The files below are from Linux-2.6.26-rc5
|
||||
acpi.c -> linux/arch/ia64/kernel/acpi.c
|
||||
acpi_numa.c -> linux/drivers/acpi/numa.c
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* acpi_numa.c - ACPI NUMA support
|
||||
*
|
||||
* Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acmacros.h>
|
||||
|
||||
#ifndef XEN
|
||||
#define ACPI_NUMA 0x80000000
|
||||
#define _COMPONENT ACPI_NUMA
|
||||
ACPI_MODULE_NAME("numa");
|
||||
#else
|
||||
#define NID_INVAL -1
|
||||
#define PXM_INVAL -1
|
||||
#endif
|
||||
|
||||
#ifndef XEN
|
||||
static nodemask_t nodes_found_map = NODE_MASK_NONE;
|
||||
#else
|
||||
/* the above causes error: initializer element is not constant
|
||||
* anyway NODE_MASK_NONE is 0 filled array.
|
||||
*/
|
||||
static nodemask_t nodes_found_map;
|
||||
#endif
|
||||
|
||||
/* maps to convert between proximity domain and logical node ID */
|
||||
static int pxm_to_node_map[MAX_PXM_DOMAINS]
|
||||
= { [0 ... MAX_PXM_DOMAINS - 1] = NID_INVAL };
|
||||
static int node_to_pxm_map[MAX_NUMNODES]
|
||||
= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
|
||||
|
||||
int pxm_to_node(int pxm)
|
||||
{
|
||||
if (pxm < 0)
|
||||
return NID_INVAL;
|
||||
return pxm_to_node_map[pxm];
|
||||
}
|
||||
|
||||
int node_to_pxm(int node)
|
||||
{
|
||||
if (node < 0)
|
||||
return PXM_INVAL;
|
||||
return node_to_pxm_map[node];
|
||||
}
|
||||
|
||||
void __acpi_map_pxm_to_node(int pxm, int node)
|
||||
{
|
||||
pxm_to_node_map[pxm] = node;
|
||||
node_to_pxm_map[node] = pxm;
|
||||
}
|
||||
|
||||
int acpi_map_pxm_to_node(int pxm)
|
||||
{
|
||||
int node = pxm_to_node_map[pxm];
|
||||
|
||||
if (node < 0){
|
||||
if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
|
||||
return NID_INVAL;
|
||||
node = first_unset_node(nodes_found_map);
|
||||
__acpi_map_pxm_to_node(pxm, node);
|
||||
node_set(node, nodes_found_map);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
#if 0
|
||||
void __cpuinit acpi_unmap_pxm_to_node(int node)
|
||||
{
|
||||
int pxm = node_to_pxm_map[node];
|
||||
pxm_to_node_map[pxm] = NID_INVAL;
|
||||
node_to_pxm_map[node] = PXM_INVAL;
|
||||
node_clear(node, nodes_found_map);
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
static void __init
|
||||
acpi_table_print_srat_entry(struct acpi_subtable_header *header)
|
||||
{
|
||||
|
||||
ACPI_FUNCTION_NAME("acpi_table_print_srat_entry");
|
||||
|
||||
if (!header)
|
||||
return;
|
||||
|
||||
switch (header->type) {
|
||||
|
||||
case ACPI_SRAT_TYPE_CPU_AFFINITY:
|
||||
#ifdef ACPI_DEBUG_OUTPUT
|
||||
{
|
||||
struct acpi_srat_cpu_affinity *p =
|
||||
(struct acpi_srat_cpu_affinity *)header;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
|
||||
p->apic_id, p->local_sapic_eid,
|
||||
p->proximity_domain_lo,
|
||||
(p->flags & ACPI_SRAT_CPU_ENABLED)?
|
||||
"enabled" : "disabled"));
|
||||
}
|
||||
#endif /* ACPI_DEBUG_OUTPUT */
|
||||
break;
|
||||
|
||||
case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
|
||||
#ifdef ACPI_DEBUG_OUTPUT
|
||||
{
|
||||
struct acpi_srat_mem_affinity *p =
|
||||
(struct acpi_srat_mem_affinity *)header;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"SRAT Memory (0x%lx length 0x%lx type 0x%x) in proximity domain %d %s%s\n",
|
||||
(unsigned long)p->base_address,
|
||||
(unsigned long)p->length,
|
||||
p->memory_type, p->proximity_domain,
|
||||
(p->flags & ACPI_SRAT_MEM_ENABLED)?
|
||||
"enabled" : "disabled",
|
||||
(p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)?
|
||||
" hot-pluggable" : ""));
|
||||
}
|
||||
#endif /* ACPI_DEBUG_OUTPUT */
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING PREFIX
|
||||
"Found unsupported SRAT entry (type = 0x%x)\n",
|
||||
header->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init acpi_parse_slit(struct acpi_table_header *table)
|
||||
{
|
||||
struct acpi_table_slit *slit;
|
||||
u32 localities;
|
||||
|
||||
if (!table)
|
||||
return -EINVAL;
|
||||
|
||||
slit = (struct acpi_table_slit *)table;
|
||||
|
||||
/* downcast just for %llu vs %lu for i386/ia64 */
|
||||
localities = (u32) slit->locality_count;
|
||||
|
||||
acpi_numa_slit_init(slit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
acpi_parse_processor_affinity(struct acpi_subtable_header * header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_srat_cpu_affinity *processor_affinity;
|
||||
|
||||
processor_affinity = (struct acpi_srat_cpu_affinity *)header;
|
||||
if (!processor_affinity)
|
||||
return -EINVAL;
|
||||
|
||||
acpi_table_print_srat_entry(header);
|
||||
|
||||
/* let architecture-dependent part to do it */
|
||||
acpi_numa_processor_affinity_init(processor_affinity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
acpi_parse_memory_affinity(struct acpi_subtable_header * header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_srat_mem_affinity *memory_affinity;
|
||||
|
||||
memory_affinity = (struct acpi_srat_mem_affinity *)header;
|
||||
if (!memory_affinity)
|
||||
return -EINVAL;
|
||||
|
||||
acpi_table_print_srat_entry(header);
|
||||
|
||||
/* let architecture-dependent part to do it */
|
||||
acpi_numa_memory_affinity_init(memory_affinity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init acpi_parse_srat(struct acpi_table_header *table)
|
||||
{
|
||||
struct acpi_table_srat *srat;
|
||||
|
||||
if (!table)
|
||||
return -EINVAL;
|
||||
|
||||
srat = (struct acpi_table_srat *)table;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
acpi_table_parse_srat(enum acpi_srat_type id,
|
||||
acpi_table_entry_handler handler, unsigned int max_entries)
|
||||
{
|
||||
return acpi_table_parse_entries(ACPI_SIG_SRAT,
|
||||
sizeof(struct acpi_table_srat), id,
|
||||
handler, max_entries);
|
||||
}
|
||||
|
||||
int __init acpi_numa_init(void)
|
||||
{
|
||||
/* SRAT: Static Resource Affinity Table */
|
||||
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
|
||||
acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY,
|
||||
acpi_parse_processor_affinity, NR_CPUS);
|
||||
acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
|
||||
acpi_parse_memory_affinity,
|
||||
NR_NODE_MEMBLKS);
|
||||
}
|
||||
|
||||
/* SLIT: System Locality Information Table */
|
||||
acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
|
||||
|
||||
acpi_numa_arch_fixup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_get_pxm(acpi_handle h)
|
||||
{
|
||||
unsigned long pxm;
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
acpi_handle phandle = h;
|
||||
|
||||
do {
|
||||
handle = phandle;
|
||||
status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return pxm;
|
||||
status = acpi_get_parent(handle, &phandle);
|
||||
} while (ACPI_SUCCESS(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int acpi_get_node(acpi_handle *handle)
|
||||
{
|
||||
int pxm, node = -1;
|
||||
|
||||
pxm = acpi_get_pxm(handle);
|
||||
if (pxm >= 0)
|
||||
node = acpi_map_pxm_to_node(pxm);
|
||||
|
||||
return node;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_get_node);
|
||||
#endif /* XEN */
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* linux/lib/cmdline.c
|
||||
* Helper functions generally used for parsing kernel command line
|
||||
* and module options.
|
||||
*
|
||||
* Code and copyrights come from init/main.c and arch/i386/kernel/setup.c.
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*
|
||||
* GNU Indent formatting options for this file: -kr -i8 -npsl -pcs
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#ifdef XEN
|
||||
#include <xen/lib.h>
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* get_option - Parse integer from an option string
|
||||
* @str: option string
|
||||
* @pint: (output) integer value parsed from @str
|
||||
*
|
||||
* Read an int from an option string; if available accept a subsequent
|
||||
* comma as well.
|
||||
*
|
||||
* Return values:
|
||||
* 0 : no int in string
|
||||
* 1 : int found, no subsequent comma
|
||||
* 2 : int found including a subsequent comma
|
||||
*/
|
||||
|
||||
int get_option (char **str, int *pint)
|
||||
{
|
||||
char *cur = *str;
|
||||
|
||||
if (!cur || !(*cur))
|
||||
return 0;
|
||||
#ifndef XEN
|
||||
*pint = simple_strtol (cur, str, 0);
|
||||
#else
|
||||
*pint = simple_strtol (cur, (const char**)str, 0);
|
||||
#endif
|
||||
if (cur == *str)
|
||||
return 0;
|
||||
if (**str == ',') {
|
||||
(*str)++;
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_options - Parse a string into a list of integers
|
||||
* @str: String to be parsed
|
||||
* @nints: size of integer array
|
||||
* @ints: integer array
|
||||
*
|
||||
* This function parses a string containing a comma-separated
|
||||
* list of integers. The parse halts when the array is
|
||||
* full, or when no more numbers can be retrieved from the
|
||||
* string.
|
||||
*
|
||||
* Return value is the character in the string which caused
|
||||
* the parse to end (typically a null terminator, if @str is
|
||||
* completely parseable).
|
||||
*/
|
||||
|
||||
char *get_options(const char *str, int nints, int *ints)
|
||||
{
|
||||
int res, i = 1;
|
||||
|
||||
while (i < nints) {
|
||||
res = get_option ((char **)&str, ints + i);
|
||||
if (res == 0)
|
||||
break;
|
||||
i++;
|
||||
if (res == 1)
|
||||
break;
|
||||
}
|
||||
ints[0] = i - 1;
|
||||
return (char *)str;
|
||||
}
|
||||
|
||||
/**
|
||||
* memparse - parse a string with mem suffixes into a number
|
||||
* @ptr: Where parse begins
|
||||
* @retptr: (output) Pointer to next char after parse completes
|
||||
*
|
||||
* Parses a string into a number. The number stored at @ptr is
|
||||
* potentially suffixed with %K (for kilobytes, or 1024 bytes),
|
||||
* %M (for megabytes, or 1048576 bytes), or %G (for gigabytes, or
|
||||
* 1073741824). If the number is suffixed with K, M, or G, then
|
||||
* the return value is the number multiplied by one kilobyte, one
|
||||
* megabyte, or one gigabyte, respectively.
|
||||
*/
|
||||
|
||||
unsigned long long memparse (char *ptr, char **retptr)
|
||||
{
|
||||
#ifndef XEN
|
||||
unsigned long long ret = simple_strtoull (ptr, retptr, 0);
|
||||
#else
|
||||
unsigned long long ret = simple_strtoull (ptr, (const char**)retptr, 0);
|
||||
#endif
|
||||
|
||||
switch (**retptr) {
|
||||
case 'G':
|
||||
case 'g':
|
||||
ret <<= 10;
|
||||
case 'M':
|
||||
case 'm':
|
||||
ret <<= 10;
|
||||
case 'K':
|
||||
case 'k':
|
||||
ret <<= 10;
|
||||
(*retptr)++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(memparse);
|
||||
EXPORT_SYMBOL(get_option);
|
||||
EXPORT_SYMBOL(get_options);
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,85 +0,0 @@
|
||||
#include <linux/config.h>
|
||||
|
||||
/*
|
||||
* Preserved registers that are shared between code in ivt.S and
|
||||
* entry.S. Be careful not to step on these!
|
||||
*/
|
||||
#define PRED_LEAVE_SYSCALL 1 /* TRUE iff leave from syscall */
|
||||
#define PRED_KERNEL_STACK 2 /* returning to kernel-stacks? */
|
||||
#define PRED_USER_STACK 3 /* returning to user-stacks? */
|
||||
#define PRED_SYSCALL 4 /* inside a system call? */
|
||||
#define PRED_NON_SYSCALL 5 /* complement of PRED_SYSCALL */
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
# define PASTE2(x,y) x##y
|
||||
# define PASTE(x,y) PASTE2(x,y)
|
||||
|
||||
# define pLvSys PASTE(p,PRED_LEAVE_SYSCALL)
|
||||
# define pKStk PASTE(p,PRED_KERNEL_STACK)
|
||||
# define pUStk PASTE(p,PRED_USER_STACK)
|
||||
# define pSys PASTE(p,PRED_SYSCALL)
|
||||
# define pNonSys PASTE(p,PRED_NON_SYSCALL)
|
||||
#endif
|
||||
|
||||
#define PT(f) (IA64_PT_REGS_##f##_OFFSET)
|
||||
#define SW(f) (IA64_SWITCH_STACK_##f##_OFFSET)
|
||||
#ifdef XEN
|
||||
#define VPD(f) (VPD_##f##_START_OFFSET)
|
||||
#endif
|
||||
|
||||
#define PT_REGS_SAVES(off) \
|
||||
.unwabi 3, 'i'; \
|
||||
.fframe IA64_PT_REGS_SIZE+16+(off); \
|
||||
.spillsp rp, PT(CR_IIP)+16+(off); \
|
||||
.spillsp ar.pfs, PT(CR_IFS)+16+(off); \
|
||||
.spillsp ar.unat, PT(AR_UNAT)+16+(off); \
|
||||
.spillsp ar.fpsr, PT(AR_FPSR)+16+(off); \
|
||||
.spillsp pr, PT(PR)+16+(off);
|
||||
|
||||
#define PT_REGS_UNWIND_INFO(off) \
|
||||
.prologue; \
|
||||
PT_REGS_SAVES(off); \
|
||||
.body
|
||||
|
||||
#define SWITCH_STACK_SAVES(off) \
|
||||
.savesp ar.unat,SW(CALLER_UNAT)+16+(off); \
|
||||
.savesp ar.fpsr,SW(AR_FPSR)+16+(off); \
|
||||
.spillsp f2,SW(F2)+16+(off); .spillsp f3,SW(F3)+16+(off); \
|
||||
.spillsp f4,SW(F4)+16+(off); .spillsp f5,SW(F5)+16+(off); \
|
||||
.spillsp f16,SW(F16)+16+(off); .spillsp f17,SW(F17)+16+(off); \
|
||||
.spillsp f18,SW(F18)+16+(off); .spillsp f19,SW(F19)+16+(off); \
|
||||
.spillsp f20,SW(F20)+16+(off); .spillsp f21,SW(F21)+16+(off); \
|
||||
.spillsp f22,SW(F22)+16+(off); .spillsp f23,SW(F23)+16+(off); \
|
||||
.spillsp f24,SW(F24)+16+(off); .spillsp f25,SW(F25)+16+(off); \
|
||||
.spillsp f26,SW(F26)+16+(off); .spillsp f27,SW(F27)+16+(off); \
|
||||
.spillsp f28,SW(F28)+16+(off); .spillsp f29,SW(F29)+16+(off); \
|
||||
.spillsp f30,SW(F30)+16+(off); .spillsp f31,SW(F31)+16+(off); \
|
||||
.spillsp r4,SW(R4)+16+(off); .spillsp r5,SW(R5)+16+(off); \
|
||||
.spillsp r6,SW(R6)+16+(off); .spillsp r7,SW(R7)+16+(off); \
|
||||
.spillsp b0,SW(B0)+16+(off); .spillsp b1,SW(B1)+16+(off); \
|
||||
.spillsp b2,SW(B2)+16+(off); .spillsp b3,SW(B3)+16+(off); \
|
||||
.spillsp b4,SW(B4)+16+(off); .spillsp b5,SW(B5)+16+(off); \
|
||||
.spillsp ar.pfs,SW(AR_PFS)+16+(off); .spillsp ar.lc,SW(AR_LC)+16+(off); \
|
||||
.spillsp @priunat,SW(AR_UNAT)+16+(off); \
|
||||
.spillsp ar.rnat,SW(AR_RNAT)+16+(off); \
|
||||
.spillsp ar.bspstore,SW(AR_BSPSTORE)+16+(off); \
|
||||
.spillsp pr,SW(PR)+16+(off)
|
||||
|
||||
#define DO_SAVE_SWITCH_STACK \
|
||||
movl r28=1f; \
|
||||
;; \
|
||||
.fframe IA64_SWITCH_STACK_SIZE; \
|
||||
adds sp=-IA64_SWITCH_STACK_SIZE,sp; \
|
||||
mov.ret.sptk b7=r28,1f; \
|
||||
SWITCH_STACK_SAVES(0); \
|
||||
br.cond.sptk.many save_switch_stack; \
|
||||
1:
|
||||
|
||||
#define DO_LOAD_SWITCH_STACK \
|
||||
movl r28=1f; \
|
||||
;; \
|
||||
invala; \
|
||||
mov.ret.sptk b7=r28,1f; \
|
||||
br.cond.sptk.many load_switch_stack; \
|
||||
1: .restore sp; \
|
||||
adds sp=IA64_SWITCH_STACK_SIZE,sp
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Platform dependent support for HP simulator.
|
||||
*
|
||||
* Copyright (C) 1998, 1999 Hewlett-Packard Co
|
||||
* Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
|
||||
*/
|
||||
#ifndef _IA64_PLATFORM_HPSIM_SSC_H
|
||||
#define _IA64_PLATFORM_HPSIM_SSC_H
|
||||
|
||||
/* Simulator system calls: */
|
||||
|
||||
#define SSC_CONSOLE_INIT 20
|
||||
#define SSC_GETCHAR 21
|
||||
#define SSC_PUTCHAR 31
|
||||
#define SSC_CONNECT_INTERRUPT 58
|
||||
#define SSC_GENERATE_INTERRUPT 59
|
||||
#define SSC_SET_PERIODIC_INTERRUPT 60
|
||||
#define SSC_GET_RTC 65
|
||||
#define SSC_EXIT 66
|
||||
#define SSC_LOAD_SYMBOLS 69
|
||||
#define SSC_GET_TOD 74
|
||||
#define SSC_CTL_TRACE 76
|
||||
|
||||
#define SSC_NETDEV_PROBE 100
|
||||
#define SSC_NETDEV_SEND 101
|
||||
#define SSC_NETDEV_RECV 102
|
||||
#define SSC_NETDEV_ATTACH 103
|
||||
#define SSC_NETDEV_DETACH 104
|
||||
|
||||
/*
|
||||
* Simulator system call.
|
||||
*/
|
||||
extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
|
||||
|
||||
#ifdef XEN
|
||||
/* Note: These are declared in linux/arch/ia64/hp/sim/simscsi.c but belong
|
||||
* in linux/include/asm-ia64/hpsim_ssc.h, hence their addition here */
|
||||
#define SSC_OPEN 50
|
||||
#define SSC_CLOSE 51
|
||||
#define SSC_READ 52
|
||||
#define SSC_WRITE 53
|
||||
#define SSC_GET_COMPLETION 54
|
||||
#define SSC_WAIT_COMPLETION 55
|
||||
|
||||
#define SSC_WRITE_ACCESS 2
|
||||
#define SSC_READ_ACCESS 1
|
||||
|
||||
struct ssc_disk_req {
|
||||
unsigned long addr;
|
||||
unsigned long len;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* _IA64_PLATFORM_HPSIM_SSC_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,350 +0,0 @@
|
||||
/*
|
||||
* linux/arch/ia64/kernel/irq.c
|
||||
*
|
||||
* Copyright (C) 1998-2001 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* 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 <goutham.rao@intel.com> Implemented pci_irq_to_vector
|
||||
* PCI to vector allocation routine.
|
||||
* 04/14/2004 Ashok Raj <ashok.raj@intel.com>
|
||||
* Added CPU Hotplug handling for IPF.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/random.h> /* for rand_initialize_irq() */
|
||||
#include <linux/signal.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/delay.h>
|
||||
#include <asm/intrinsics.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#ifdef XEN
|
||||
#include <xen/perfc.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
# include <asm/perfmon.h>
|
||||
#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);
|
||||
|
||||
#ifdef XEN
|
||||
unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
|
||||
#else
|
||||
static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
#ifdef XEN
|
||||
perfc_incr(irqs);
|
||||
#endif
|
||||
#if IRQ_DEBUG
|
||||
#ifdef XEN
|
||||
xen_debug_irq(vector, regs);
|
||||
#endif
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
#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
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
extern irqreturn_t handle_IPI (int irq, void *dev_id, struct pt_regs *regs);
|
||||
|
||||
static struct irqaction __read_mostly ipi_irqaction = {
|
||||
.handler = handle_IPI,
|
||||
#ifndef XEN
|
||||
.flags = SA_INTERRUPT,
|
||||
#endif
|
||||
.name = "IPI"
|
||||
};
|
||||
#endif
|
||||
|
||||
static hw_irq_controller irq_type_ia64_lsapic = {
|
||||
.typename = "LSAPIC",
|
||||
.startup = irq_startup_none,
|
||||
.shutdown = irq_shutdown_none,
|
||||
.enable = irq_enable_none,
|
||||
.disable = irq_disable_none,
|
||||
.ack = irq_actor_none,
|
||||
.end = irq_actor_none
|
||||
};
|
||||
|
||||
void
|
||||
register_percpu_irq (ia64_vector vec, struct irqaction *action)
|
||||
{
|
||||
irq_desc_t *desc;
|
||||
#ifndef XEN
|
||||
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);
|
||||
}
|
||||
#else
|
||||
desc = irq_descp(vec);
|
||||
desc->status |= IRQ_PER_CPU;
|
||||
desc->handler = &irq_type_ia64_lsapic;
|
||||
if (action)
|
||||
setup_vector(vec, action);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef XEN
|
||||
int __init request_irq_vector(unsigned int vector,
|
||||
void (*handler)(int, void *, struct cpu_user_regs *),
|
||||
unsigned long irqflags, const char * devname, void *dev_id)
|
||||
{
|
||||
struct irqaction * action;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Sanity-check: shared interrupts must pass in a real dev-ID,
|
||||
* otherwise we'll have trouble later trying to figure out
|
||||
* which interrupt is which (messes up the interrupt freeing logic etc).
|
||||
* */
|
||||
if (vector >= NR_VECTORS)
|
||||
return -EINVAL;
|
||||
if (!handler)
|
||||
return -EINVAL;
|
||||
|
||||
action = xmalloc(struct irqaction);
|
||||
if (!action)
|
||||
return -ENOMEM;
|
||||
|
||||
action->handler = handler;
|
||||
action->name = devname;
|
||||
action->dev_id = dev_id;
|
||||
|
||||
retval = setup_vector(vector, action);
|
||||
if (retval)
|
||||
xfree(action);
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init
|
||||
init_IRQ (void)
|
||||
{
|
||||
#ifdef XEN
|
||||
BUG_ON(init_irq_data());
|
||||
#endif
|
||||
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_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);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,306 +0,0 @@
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <asm/cache.h>
|
||||
|
||||
#include "entry.h"
|
||||
|
||||
/*
|
||||
* For ivt.s we want to access the stack virtually so we don't have to disable translation
|
||||
* on interrupts.
|
||||
*
|
||||
* On entry:
|
||||
* r1: pointer to current task (ar.k6)
|
||||
*/
|
||||
#define MINSTATE_START_SAVE_MIN_VIRT \
|
||||
(pUStk) mov ar.rsc=0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \
|
||||
;; \
|
||||
(pUStk) mov.m r24=ar.rnat; \
|
||||
(pUStk) addl r22=IA64_RBS_OFFSET,r1; /* compute base of RBS */ \
|
||||
(pKStk) mov r1=sp; /* get sp */ \
|
||||
;; \
|
||||
(pUStk) lfetch.fault.excl.nt1 [r22]; \
|
||||
(pUStk) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \
|
||||
(pUStk) mov r23=ar.bspstore; /* save ar.bspstore */ \
|
||||
;; \
|
||||
(pUStk) mov ar.bspstore=r22; /* switch to kernel RBS */ \
|
||||
(pKStk) addl r1=-IA64_PT_REGS_SIZE,r1; /* if in kernel mode, use sp (r12) */ \
|
||||
;; \
|
||||
(pUStk) mov r18=ar.bsp; \
|
||||
(pUStk) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */
|
||||
|
||||
#define MINSTATE_END_SAVE_MIN_VIRT \
|
||||
bsw.1; /* switch back to bank 1 (must be last in insn group) */ \
|
||||
;;
|
||||
|
||||
/*
|
||||
* For mca_asm.S we want to access the stack physically since the state is saved before we
|
||||
* go virtual and don't want to destroy the iip or ipsr.
|
||||
*/
|
||||
#ifdef XEN
|
||||
# define MINSTATE_START_SAVE_MIN_PHYS \
|
||||
(pKStk) tbit.z pKStk,pUStk=r29,IA64_PSR_VM_BIT; \
|
||||
;; \
|
||||
(pKStk) movl r3=THIS_CPU(ia64_mca_data);; \
|
||||
(pKStk) tpa r3 = r3;; \
|
||||
(pKStk) ld8 r3 = [r3];; \
|
||||
(pKStk) addl r3=IA64_MCA_CPU_INIT_STACK_OFFSET,r3;; \
|
||||
(pKStk) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r3; \
|
||||
(pUStk) mov ar.rsc=0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \
|
||||
(pUStk) addl r22=IA64_RBS_OFFSET,r1; /* compute base of register backing store */ \
|
||||
;; \
|
||||
(pUStk) mov r24=ar.rnat; \
|
||||
(pUStk) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \
|
||||
(pUStk) mov r23=ar.bspstore; /* save ar.bspstore */ \
|
||||
(pUStk) dep r22=-1,r22,60,4; /* compute Xen virtual addr of RBS */ \
|
||||
;; \
|
||||
(pUStk) mov ar.bspstore=r22; /* switch to Xen RBS */ \
|
||||
;; \
|
||||
(pUStk) mov r18=ar.bsp; \
|
||||
(pUStk) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \
|
||||
|
||||
# define MINSTATE_END_SAVE_MIN_PHYS \
|
||||
dep r12=-1,r12,60,4; /* make sp a Xen virtual address */ \
|
||||
;;
|
||||
#else
|
||||
# define MINSTATE_START_SAVE_MIN_PHYS \
|
||||
(pKStk) mov r3=IA64_KR(PER_CPU_DATA);; \
|
||||
(pKStk) addl r3=THIS_CPU(ia64_mca_data),r3;; \
|
||||
(pKStk) ld8 r3 = [r3];; \
|
||||
(pKStk) addl r3=IA64_MCA_CPU_INIT_STACK_OFFSET,r3;; \
|
||||
(pKStk) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r3; \
|
||||
(pUStk) mov ar.rsc=0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \
|
||||
(pUStk) addl r22=IA64_RBS_OFFSET,r1; /* compute base of register backing store */ \
|
||||
;; \
|
||||
(pUStk) mov r24=ar.rnat; \
|
||||
(pUStk) addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1; /* compute base of memory stack */ \
|
||||
(pUStk) mov r23=ar.bspstore; /* save ar.bspstore */ \
|
||||
(pUStk) dep r22=-1,r22,61,3; /* compute kernel virtual addr of RBS */ \
|
||||
;; \
|
||||
(pUStk) mov ar.bspstore=r22; /* switch to kernel RBS */ \
|
||||
;; \
|
||||
(pUStk) mov r18=ar.bsp; \
|
||||
(pUStk) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \
|
||||
|
||||
# define MINSTATE_END_SAVE_MIN_PHYS \
|
||||
dep r12=-1,r12,61,3; /* make sp a kernel virtual address */ \
|
||||
;;
|
||||
#endif /* XEN */
|
||||
|
||||
#ifdef MINSTATE_VIRT
|
||||
#ifdef XEN
|
||||
# define MINSTATE_GET_CURRENT(reg) \
|
||||
movl reg=THIS_CPU(cpu_kr)+IA64_KR_CURRENT_OFFSET;; \
|
||||
ld8 reg=[reg]
|
||||
# define MINSTATE_GET_CURRENT_VIRT(reg) MINSTATE_GET_CURRENT(reg)
|
||||
#else
|
||||
# define MINSTATE_GET_CURRENT(reg) mov reg=IA64_KR(CURRENT)
|
||||
#endif
|
||||
# define MINSTATE_START_SAVE_MIN MINSTATE_START_SAVE_MIN_VIRT
|
||||
# define MINSTATE_END_SAVE_MIN MINSTATE_END_SAVE_MIN_VIRT
|
||||
#endif
|
||||
|
||||
#ifdef MINSTATE_PHYS
|
||||
# ifdef XEN
|
||||
# define MINSTATE_GET_CURRENT(reg) \
|
||||
movl reg=THIS_CPU(cpu_kr)+IA64_KR_CURRENT_OFFSET;; \
|
||||
tpa reg=reg;; \
|
||||
ld8 reg=[reg];; \
|
||||
tpa reg=reg;;
|
||||
# define MINSTATE_GET_CURRENT_VIRT(reg) \
|
||||
movl reg=THIS_CPU(cpu_kr)+IA64_KR_CURRENT_OFFSET;; \
|
||||
tpa reg=reg;; \
|
||||
ld8 reg=[reg];;
|
||||
#else
|
||||
# define MINSTATE_GET_CURRENT(reg) mov reg=IA64_KR(CURRENT);; tpa reg=reg
|
||||
#endif /* XEN */
|
||||
# define MINSTATE_START_SAVE_MIN MINSTATE_START_SAVE_MIN_PHYS
|
||||
# define MINSTATE_END_SAVE_MIN MINSTATE_END_SAVE_MIN_PHYS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DO_SAVE_MIN switches to the kernel stacks (if necessary) and saves
|
||||
* the minimum state necessary that allows us to turn psr.ic back
|
||||
* on.
|
||||
*
|
||||
* Assumed state upon entry:
|
||||
* psr.ic: off
|
||||
* r31: contains saved predicates (pr)
|
||||
*
|
||||
* Upon exit, the state is as follows:
|
||||
* psr.ic: off
|
||||
* r2 = points to &pt_regs.r16
|
||||
* r8 = contents of ar.ccv
|
||||
* r9 = contents of ar.csd
|
||||
* r10 = contents of ar.ssd
|
||||
* r11 = FPSR_DEFAULT
|
||||
* r12 = kernel sp (kernel virtual address)
|
||||
* r13 = points to current task_struct (kernel virtual address)
|
||||
* p15 = TRUE if psr.i is set in cr.ipsr
|
||||
* predicate registers (other than p2, p3, and p15), b6, r3, r14, r15:
|
||||
* preserved
|
||||
*
|
||||
* Note that psr.ic is NOT turned on by this macro. This is so that
|
||||
* we can pass interruption state as arguments to a handler.
|
||||
*/
|
||||
#define DO_SAVE_MIN(COVER,SAVE_IFS,EXTRA) \
|
||||
MINSTATE_GET_CURRENT(r16); /* M (or M;;I) */ \
|
||||
mov r27=ar.rsc; /* M */ \
|
||||
mov r20=r1; /* A */ \
|
||||
mov r25=ar.unat; /* M */ \
|
||||
mov r29=cr.ipsr; /* M */ \
|
||||
mov r26=ar.pfs; /* I */ \
|
||||
mov r28=cr.iip; /* M */ \
|
||||
mov r21=ar.fpsr; /* M */ \
|
||||
COVER; /* B;; (or nothing) */ \
|
||||
;; \
|
||||
adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r16; \
|
||||
;; \
|
||||
ld1 r17=[r16]; /* load current->thread.on_ustack flag */ \
|
||||
st1 [r16]=r0; /* clear current->thread.on_ustack flag */ \
|
||||
adds r1=-IA64_TASK_THREAD_ON_USTACK_OFFSET,r16 \
|
||||
/* switch from user to kernel RBS: */ \
|
||||
;; \
|
||||
invala; /* M */ \
|
||||
SAVE_IFS; \
|
||||
cmp.eq pKStk,pUStk=r0,r17; /* are we in kernel mode already? */ \
|
||||
;; \
|
||||
MINSTATE_START_SAVE_MIN \
|
||||
adds r17=2*L1_CACHE_BYTES,r1; /* really: biggest cache-line size */ \
|
||||
adds r16=PT(CR_IPSR),r1; \
|
||||
;; \
|
||||
lfetch.fault.excl.nt1 [r17],L1_CACHE_BYTES; \
|
||||
st8 [r16]=r29; /* save cr.ipsr */ \
|
||||
;; \
|
||||
lfetch.fault.excl.nt1 [r17]; \
|
||||
tbit.nz p15,p0=r29,IA64_PSR_I_BIT; \
|
||||
mov r29=b0 \
|
||||
;; \
|
||||
adds r16=PT(R8),r1; /* initialize first base pointer */ \
|
||||
adds r17=PT(R9),r1; /* initialize second base pointer */ \
|
||||
(pKStk) mov r18=r0; /* make sure r18 isn't NaT */ \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r8,16; \
|
||||
.mem.offset 8,0; st8.spill [r17]=r9,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r10,24; \
|
||||
.mem.offset 8,0; st8.spill [r17]=r11,24; \
|
||||
;; \
|
||||
st8 [r16]=r28,16; /* save cr.iip */ \
|
||||
st8 [r17]=r30,16; /* save cr.ifs */ \
|
||||
(pUStk) sub r18=r18,r22; /* r18=RSE.ndirty*8 */ \
|
||||
mov r8=ar.ccv; \
|
||||
mov r9=ar.csd; \
|
||||
mov r10=ar.ssd; \
|
||||
movl r11=FPSR_DEFAULT; /* L-unit */ \
|
||||
;; \
|
||||
st8 [r16]=r25,16; /* save ar.unat */ \
|
||||
st8 [r17]=r26,16; /* save ar.pfs */ \
|
||||
shl r18=r18,16; /* compute ar.rsc to be used for "loadrs" */ \
|
||||
;; \
|
||||
st8 [r16]=r27,16; /* save ar.rsc */ \
|
||||
(pUStk) st8 [r17]=r24,16; /* save ar.rnat */ \
|
||||
(pKStk) adds r17=16,r17; /* skip over ar_rnat field */ \
|
||||
;; /* avoid RAW on r16 & r17 */ \
|
||||
(pUStk) st8 [r16]=r23,16; /* save ar.bspstore */ \
|
||||
st8 [r17]=r31,16; /* save predicates */ \
|
||||
(pKStk) adds r16=16,r16; /* skip over ar_bspstore field */ \
|
||||
;; \
|
||||
st8 [r16]=r29,16; /* save b0 */ \
|
||||
st8 [r17]=r18,16; /* save ar.rsc value for "loadrs" */ \
|
||||
cmp.eq pNonSys,pSys=r0,r0 /* initialize pSys=0, pNonSys=1 */ \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r20,16; /* save original r1 */ \
|
||||
.mem.offset 8,0; st8.spill [r17]=r12,16; \
|
||||
adds r12=-16,r1; /* switch to kernel memory stack (with 16 bytes of scratch) */ \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r13,16; \
|
||||
.mem.offset 8,0; st8.spill [r17]=r21,16; /* save ar.fpsr */ \
|
||||
/* XEN mov r13=IA64_KR(CURRENT);*/ /* establish `current' */ \
|
||||
MINSTATE_GET_CURRENT_VIRT(r13); /* XEN establish `current' */ \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r15,16; \
|
||||
.mem.offset 8,0; st8.spill [r17]=r14,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r16]=r2,16; \
|
||||
.mem.offset 8,0; st8.spill [r17]=r3,16; \
|
||||
adds r2=IA64_PT_REGS_R16_OFFSET,r1; \
|
||||
;; \
|
||||
EXTRA; \
|
||||
movl r1=__gp; /* establish kernel global pointer */ \
|
||||
;; \
|
||||
MINSTATE_END_SAVE_MIN
|
||||
|
||||
/*
|
||||
* SAVE_REST saves the remainder of pt_regs (with psr.ic on).
|
||||
*
|
||||
* Assumed state upon entry:
|
||||
* psr.ic: on
|
||||
* r2: points to &pt_regs.r16
|
||||
* r3: points to &pt_regs.r17
|
||||
* r8: contents of ar.ccv
|
||||
* r9: contents of ar.csd
|
||||
* r10: contents of ar.ssd
|
||||
* r11: FPSR_DEFAULT
|
||||
*
|
||||
* Registers r14 and r15 are guaranteed not to be touched by SAVE_REST.
|
||||
*/
|
||||
#define SAVE_REST \
|
||||
.mem.offset 0,0; st8.spill [r2]=r16,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r17,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r18,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r19,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r20,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r21,16; \
|
||||
mov r18=b6; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r22,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r23,16; \
|
||||
mov r19=b7; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r24,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r25,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r26,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r27,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r28,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r29,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r30,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r31,32; \
|
||||
;; \
|
||||
mov ar.fpsr=r11; /* M-unit */ \
|
||||
st8 [r2]=r8,8; /* ar.ccv */ \
|
||||
adds r24=PT(B6)-PT(F7),r3; \
|
||||
;; \
|
||||
stf.spill [r2]=f6,32; \
|
||||
stf.spill [r3]=f7,32; \
|
||||
;; \
|
||||
stf.spill [r2]=f8,32; \
|
||||
stf.spill [r3]=f9,32; \
|
||||
;; \
|
||||
stf.spill [r2]=f10,32; \
|
||||
stf.spill [r3]=f11,24; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r4,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r5,16; \
|
||||
;; \
|
||||
.mem.offset 0,0; st8.spill [r2]=r6,16; \
|
||||
.mem.offset 8,0; st8.spill [r3]=r7; \
|
||||
adds r25=PT(B7)-PT(R7),r3; \
|
||||
;; \
|
||||
st8 [r24]=r18,16; /* b6 */ \
|
||||
st8 [r25]=r19,16; /* b7 */ \
|
||||
;; \
|
||||
st8 [r24]=r9; /* ar.csd */ \
|
||||
mov r26=ar.unat; \
|
||||
;; \
|
||||
st8 [r25]=r10; /* ar.ssd */ \
|
||||
st8 [r2]=r26; /* eml_unat */ \
|
||||
;;
|
||||
|
||||
#define SAVE_MIN_WITH_COVER DO_SAVE_MIN(cover, mov r30=cr.ifs,)
|
||||
#define SAVE_MIN_WITH_COVER_R19 DO_SAVE_MIN(cover, mov r30=cr.ifs, mov r15=r19)
|
||||
#define SAVE_MIN DO_SAVE_MIN( , mov r30=r0, )
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 1998-2003 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
* Copyright (C) 2000, Rohit Seth <rohit.seth@intel.com>
|
||||
* Copyright (C) 1999 VA Linux Systems
|
||||
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
|
||||
* Copyright (C) 2003 Silicon Graphics, Inc. All rights reserved.
|
||||
*
|
||||
* Routines used by ia64 machines with contiguous (or virtually contiguous)
|
||||
* memory.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/swap.h>
|
||||
|
||||
#include <asm/meminit.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/mca.h>
|
||||
|
||||
#include <linux/efi.h>
|
||||
#ifdef CONFIG_VIRTUAL_MEM_MAP
|
||||
static unsigned long num_dma_physpages;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* show_mem - display a memory statistics summary
|
||||
*
|
||||
* Just walks the pages in the system and describes where they're allocated.
|
||||
*/
|
||||
#ifndef XEN
|
||||
void
|
||||
show_mem (void)
|
||||
{
|
||||
int i, total = 0, reserved = 0;
|
||||
int shared = 0, cached = 0;
|
||||
|
||||
printk("Mem-info:\n");
|
||||
show_free_areas();
|
||||
|
||||
printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
|
||||
i = max_mapnr;
|
||||
while (i-- > 0) {
|
||||
if (!mfn_valid(i))
|
||||
continue;
|
||||
total++;
|
||||
if (PageReserved(mem_map+i))
|
||||
reserved++;
|
||||
else if (PageSwapCache(mem_map+i))
|
||||
cached++;
|
||||
else if (page_count(mem_map + i))
|
||||
shared += page_count(mem_map + i) - 1;
|
||||
}
|
||||
printk("%d pages of RAM\n", total);
|
||||
printk("%d reserved pages\n", reserved);
|
||||
printk("%d pages shared\n", shared);
|
||||
printk("%d pages swap cached\n", cached);
|
||||
printk("%ld pages in page table cache\n",
|
||||
pgtable_quicklist_total_size());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* physical address where the bootmem map is located */
|
||||
unsigned long bootmap_start;
|
||||
|
||||
/**
|
||||
* find_max_pfn - adjust the maximum page number callback
|
||||
* @start: start of range
|
||||
* @end: end of range
|
||||
* @arg: address of pointer to global max_pfn variable
|
||||
*
|
||||
* Passed as a callback function to efi_memmap_walk() to determine the highest
|
||||
* available page frame number in the system.
|
||||
*/
|
||||
int
|
||||
find_max_pfn (unsigned long start, unsigned long end, void *arg)
|
||||
{
|
||||
unsigned long *max_pfnp = arg, pfn;
|
||||
|
||||
pfn = (PAGE_ALIGN(end - 1) - PAGE_OFFSET) >> PAGE_SHIFT;
|
||||
if (pfn > *max_pfnp)
|
||||
*max_pfnp = pfn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_bootmap_location - callback to find a memory area for the bootmap
|
||||
* @start: start of region
|
||||
* @end: end of region
|
||||
* @arg: unused callback data
|
||||
*
|
||||
* Find a place to put the bootmap and return its starting address in
|
||||
* bootmap_start. This address must be page-aligned.
|
||||
*/
|
||||
int
|
||||
find_bootmap_location (unsigned long start, unsigned long end, void *arg)
|
||||
{
|
||||
unsigned long needed = *(unsigned long *)arg;
|
||||
unsigned long range_start, range_end, free_start;
|
||||
int i;
|
||||
|
||||
#if IGNORE_PFN0
|
||||
if (start == PAGE_OFFSET) {
|
||||
start += PAGE_SIZE;
|
||||
if (start >= end)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
free_start = PAGE_OFFSET;
|
||||
|
||||
for (i = 0; i < num_rsvd_regions; i++) {
|
||||
range_start = max(start, free_start);
|
||||
range_end = min(end, rsvd_region[i].start & PAGE_MASK);
|
||||
|
||||
free_start = PAGE_ALIGN(rsvd_region[i].end);
|
||||
|
||||
if (range_end <= range_start)
|
||||
continue; /* skip over empty range */
|
||||
|
||||
if (range_end - range_start >= needed) {
|
||||
bootmap_start = __pa(range_start);
|
||||
return -1; /* done */
|
||||
}
|
||||
|
||||
/* nothing more available in this segment */
|
||||
if (range_end == end)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_memory - setup memory map
|
||||
*
|
||||
* Walk the EFI memory map and find usable memory for the system, taking
|
||||
* into account reserved areas.
|
||||
*/
|
||||
#ifndef XEN
|
||||
void
|
||||
find_memory (void)
|
||||
{
|
||||
unsigned long bootmap_size;
|
||||
|
||||
reserve_memory();
|
||||
|
||||
/* first find highest page frame number */
|
||||
max_pfn = 0;
|
||||
efi_memmap_walk(find_max_pfn, &max_pfn);
|
||||
|
||||
/* how many bytes to cover all the pages */
|
||||
bootmap_size = bootmem_bootmap_pages(max_pfn) << PAGE_SHIFT;
|
||||
|
||||
/* look for a location to hold the bootmap */
|
||||
bootmap_start = ~0UL;
|
||||
efi_memmap_walk(find_bootmap_location, &bootmap_size);
|
||||
if (bootmap_start == ~0UL)
|
||||
panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
|
||||
|
||||
bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn);
|
||||
|
||||
/* Free all available memory, then mark bootmem-map as being in use. */
|
||||
efi_memmap_walk(filter_rsvd_memory, free_bootmem);
|
||||
reserve_bootmem(bootmap_start, bootmap_size);
|
||||
|
||||
find_initrd();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#ifdef XEN
|
||||
#include <asm/elf.h>
|
||||
|
||||
void *percpu_area __initdata = NULL;
|
||||
|
||||
void* __init
|
||||
per_cpu_allocate(void *xen_heap_start, unsigned long end_in_pa)
|
||||
{
|
||||
int order = get_order((NR_CPUS - 1) * PERCPU_PAGE_SIZE);
|
||||
unsigned long size = 1UL << (order + PAGE_SHIFT);
|
||||
unsigned long start = ALIGN_UP((unsigned long)xen_heap_start,
|
||||
PERCPU_PAGE_SIZE);
|
||||
unsigned long end = start + size;
|
||||
|
||||
if (__pa(end) < end_in_pa) {
|
||||
init_boot_pages(__pa(xen_heap_start), __pa(start));
|
||||
xen_heap_start = (void*)end;
|
||||
percpu_area = (void*)virt_to_xenva(start);
|
||||
printk("allocate percpu area 0x%lx@0x%lx 0x%p\n",
|
||||
size, start, percpu_area);
|
||||
} else {
|
||||
panic("can't allocate percpu area. size 0x%lx\n", size);
|
||||
}
|
||||
return xen_heap_start;
|
||||
}
|
||||
|
||||
static void* __init
|
||||
get_per_cpu_area(void)
|
||||
{
|
||||
return percpu_area;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* per_cpu_init - setup per-cpu variables
|
||||
*
|
||||
* Allocate and setup per-cpu data areas.
|
||||
*/
|
||||
void *
|
||||
per_cpu_init (void)
|
||||
{
|
||||
void *cpu_data;
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* get_free_pages() cannot be used before cpu_init() done. BSP
|
||||
* allocates "NR_CPUS" pages for all CPUs to avoid that AP calls
|
||||
* get_zeroed_page().
|
||||
*/
|
||||
if (smp_processor_id() == 0) {
|
||||
#ifdef XEN
|
||||
void *cpu0_data = __cpu0_per_cpu;
|
||||
|
||||
__per_cpu_offset[0] = (char *)cpu0_data - __per_cpu_start;
|
||||
per_cpu(local_per_cpu_offset, 0) = __per_cpu_offset[0];
|
||||
|
||||
cpu_data = get_per_cpu_area();
|
||||
if (cpu_data == NULL)
|
||||
panic("can't allocate per cpu area.\n");
|
||||
|
||||
for (cpu = 1; cpu < NR_CPUS; cpu++) {
|
||||
memcpy(cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start);
|
||||
__per_cpu_offset[cpu] = (char *) cpu_data - __per_cpu_start;
|
||||
cpu_data += PERCPU_PAGE_SIZE;
|
||||
per_cpu(local_per_cpu_offset, cpu) = __per_cpu_offset[cpu];
|
||||
}
|
||||
#else
|
||||
cpu_data = __alloc_bootmem(PERCPU_PAGE_SIZE * NR_CPUS,
|
||||
PERCPU_PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++) {
|
||||
memcpy(cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start);
|
||||
__per_cpu_offset[cpu] = (char *) cpu_data - __per_cpu_start;
|
||||
cpu_data += PERCPU_PAGE_SIZE;
|
||||
per_cpu(local_per_cpu_offset, cpu) = __per_cpu_offset[cpu];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return __per_cpu_start + __per_cpu_offset[smp_processor_id()];
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#ifndef XEN
|
||||
static int
|
||||
count_pages (u64 start, u64 end, void *arg)
|
||||
{
|
||||
unsigned long *count = arg;
|
||||
|
||||
*count += (end - start) >> PAGE_SHIFT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIRTUAL_MEM_MAP
|
||||
static int
|
||||
count_dma_pages (u64 start, u64 end, void *arg)
|
||||
{
|
||||
unsigned long *count = arg;
|
||||
|
||||
if (start < MAX_DMA_ADDRESS)
|
||||
*count += (min(end, MAX_DMA_ADDRESS) - start) >> PAGE_SHIFT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set up the page tables.
|
||||
*/
|
||||
|
||||
void
|
||||
paging_init (void)
|
||||
{
|
||||
unsigned long max_dma;
|
||||
unsigned long zones_size[MAX_NR_ZONES];
|
||||
#ifdef CONFIG_VIRTUAL_MEM_MAP
|
||||
unsigned long zholes_size[MAX_NR_ZONES];
|
||||
unsigned long max_gap;
|
||||
#endif
|
||||
|
||||
/* initialize mem_map[] */
|
||||
|
||||
memset(zones_size, 0, sizeof(zones_size));
|
||||
|
||||
num_physpages = 0;
|
||||
efi_memmap_walk(count_pages, &num_physpages);
|
||||
|
||||
max_dma = virt_to_maddr((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT;
|
||||
|
||||
#ifdef CONFIG_VIRTUAL_MEM_MAP
|
||||
memset(zholes_size, 0, sizeof(zholes_size));
|
||||
|
||||
num_dma_physpages = 0;
|
||||
efi_memmap_walk(count_dma_pages, &num_dma_physpages);
|
||||
|
||||
if (max_low_pfn < max_dma) {
|
||||
zones_size[ZONE_DMA] = max_low_pfn;
|
||||
zholes_size[ZONE_DMA] = max_low_pfn - num_dma_physpages;
|
||||
} else {
|
||||
zones_size[ZONE_DMA] = max_dma;
|
||||
zholes_size[ZONE_DMA] = max_dma - num_dma_physpages;
|
||||
if (num_physpages > num_dma_physpages) {
|
||||
zones_size[ZONE_NORMAL] = max_low_pfn - max_dma;
|
||||
zholes_size[ZONE_NORMAL] =
|
||||
((max_low_pfn - max_dma) -
|
||||
(num_physpages - num_dma_physpages));
|
||||
}
|
||||
}
|
||||
|
||||
max_gap = 0;
|
||||
efi_memmap_walk(find_largest_hole, (u64 *)&max_gap);
|
||||
if (max_gap < LARGE_GAP) {
|
||||
vmem_map = (struct page *) 0;
|
||||
free_area_init_node(0, &contig_page_data, zones_size, 0,
|
||||
zholes_size);
|
||||
} else {
|
||||
unsigned long map_size;
|
||||
|
||||
/* allocate virtual_mem_map */
|
||||
|
||||
map_size = PAGE_ALIGN(max_low_pfn * sizeof(struct page));
|
||||
vmalloc_end -= map_size;
|
||||
vmem_map = (struct page *) vmalloc_end;
|
||||
efi_memmap_walk(create_mem_map_page_table, NULL);
|
||||
|
||||
NODE_DATA(0)->node_mem_map = vmem_map;
|
||||
free_area_init_node(0, &contig_page_data, zones_size,
|
||||
0, zholes_size);
|
||||
|
||||
printk("Virtual mem_map starts at 0x%p\n", mem_map);
|
||||
}
|
||||
#else /* !CONFIG_VIRTUAL_MEM_MAP */
|
||||
if (max_low_pfn < max_dma)
|
||||
zones_size[ZONE_DMA] = max_low_pfn;
|
||||
else {
|
||||
zones_size[ZONE_DMA] = max_dma;
|
||||
zones_size[ZONE_NORMAL] = max_low_pfn - max_dma;
|
||||
}
|
||||
free_area_init(zones_size);
|
||||
#endif /* !CONFIG_VIRTUAL_MEM_MAP */
|
||||
zero_page_memmap_ptr = virt_to_page(ia64_imva(empty_zero_page));
|
||||
}
|
||||
#endif /* XEN */
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* ia64 kernel NUMA specific stuff
|
||||
*
|
||||
* Copyright (C) 2002 Erich Focht <efocht@ess.nec.de>
|
||||
* Copyright (C) 2004 Silicon Graphics, Inc.
|
||||
* Jesse Barnes <jbarnes@sgi.com>
|
||||
*/
|
||||
#ifdef XEN
|
||||
#include <xen/types.h>
|
||||
#endif
|
||||
#include <linux/config.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/smp.h>
|
||||
#ifdef XEN
|
||||
#include <xen/nodemask.h>
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
nodemask_t __read_mostly node_online_map = { { [0] = 1UL } };
|
||||
#endif
|
||||
|
||||
u8 cpu_to_node_map[NR_CPUS] __cacheline_aligned;
|
||||
EXPORT_SYMBOL(cpu_to_node_map);
|
||||
|
||||
cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned;
|
||||
|
||||
/**
|
||||
* build_cpu_to_node_map - setup cpu to node and node to cpumask arrays
|
||||
*
|
||||
* Build cpu to node mapping and initialize the per node cpu masks using
|
||||
* info from the node_cpuid array handed to us by ACPI.
|
||||
*/
|
||||
void __init build_cpu_to_node_map(void)
|
||||
{
|
||||
int cpu, i, node;
|
||||
|
||||
for(node=0; node < MAX_NUMNODES; node++)
|
||||
cpumask_clear(&node_to_cpu_mask[node]);
|
||||
|
||||
for(cpu = 0; cpu < NR_CPUS; ++cpu) {
|
||||
node = -1;
|
||||
for (i = 0; i < NR_CPUS; ++i)
|
||||
if (cpu_physical_id(cpu) == node_cpuid[i].phys_id) {
|
||||
node = node_cpuid[i].nid;
|
||||
break;
|
||||
}
|
||||
cpu_to_node_map[cpu] = (node >= 0) ? node : 0;
|
||||
if (node >= 0)
|
||||
cpumask_set_cpu(cpu, &node_to_cpu_mask[node]);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2003 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*
|
||||
* This file implements the default sampling buffer format
|
||||
* for the Linux/ia64 perfmon-2 subsystem.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/delay.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <asm/perfmon.h>
|
||||
#include <asm/perfmon_default_smpl.h>
|
||||
|
||||
#ifndef XEN
|
||||
MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
|
||||
MODULE_DESCRIPTION("perfmon default sampling format");
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
#define pid vcpu_id
|
||||
#endif
|
||||
|
||||
#define DEFAULT_DEBUG 1
|
||||
|
||||
#ifdef DEFAULT_DEBUG
|
||||
#define DPRINT(a) \
|
||||
do { \
|
||||
if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \
|
||||
} while (0)
|
||||
|
||||
#define DPRINT_ovfl(a) \
|
||||
do { \
|
||||
if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define DPRINT(a)
|
||||
#define DPRINT_ovfl(a)
|
||||
#endif
|
||||
|
||||
static int
|
||||
default_validate(struct task_struct *task, unsigned int flags, int cpu, void *data)
|
||||
{
|
||||
pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t*)data;
|
||||
int ret = 0;
|
||||
|
||||
if (data == NULL) {
|
||||
DPRINT(("[%d] no argument passed\n", task->pid));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DPRINT(("[%d] validate flags=0x%x CPU%d\n", task->pid, flags, cpu));
|
||||
|
||||
/*
|
||||
* must hold at least the buffer header + one minimally sized entry
|
||||
*/
|
||||
if (arg->buf_size < PFM_DEFAULT_SMPL_MIN_BUF_SIZE) return -EINVAL;
|
||||
|
||||
DPRINT(("buf_size=%lu\n", arg->buf_size));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
default_get_size(struct task_struct *task, unsigned int flags, int cpu, void *data, unsigned long *size)
|
||||
{
|
||||
pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
|
||||
|
||||
/*
|
||||
* size has been validated in default_validate
|
||||
*/
|
||||
*size = arg->buf_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
default_init(struct task_struct *task, void *buf, unsigned int flags, int cpu, void *data)
|
||||
{
|
||||
pfm_default_smpl_hdr_t *hdr;
|
||||
pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data;
|
||||
|
||||
hdr = (pfm_default_smpl_hdr_t *)buf;
|
||||
|
||||
hdr->hdr_version = PFM_DEFAULT_SMPL_VERSION;
|
||||
hdr->hdr_buf_size = arg->buf_size;
|
||||
hdr->hdr_cur_offs = sizeof(*hdr);
|
||||
hdr->hdr_overflows = 0UL;
|
||||
hdr->hdr_count = 0UL;
|
||||
|
||||
DPRINT(("[%d] buffer=%p buf_size=%lu hdr_size=%lu hdr_version=%u cur_offs=%lu\n",
|
||||
task->pid,
|
||||
buf,
|
||||
hdr->hdr_buf_size,
|
||||
sizeof(*hdr),
|
||||
hdr->hdr_version,
|
||||
hdr->hdr_cur_offs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
default_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, struct pt_regs *regs, unsigned long stamp)
|
||||
{
|
||||
pfm_default_smpl_hdr_t *hdr;
|
||||
pfm_default_smpl_entry_t *ent;
|
||||
void *cur, *last;
|
||||
unsigned long *e, entry_size;
|
||||
unsigned int npmds, i;
|
||||
unsigned char ovfl_pmd;
|
||||
unsigned char ovfl_notify;
|
||||
|
||||
if (unlikely(buf == NULL || arg == NULL|| regs == NULL || task == NULL)) {
|
||||
DPRINT(("[%d] invalid arguments buf=%p arg=%p\n", task->pid, buf, arg));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdr = (pfm_default_smpl_hdr_t *)buf;
|
||||
cur = buf+hdr->hdr_cur_offs;
|
||||
last = buf+hdr->hdr_buf_size;
|
||||
ovfl_pmd = arg->ovfl_pmd;
|
||||
ovfl_notify = arg->ovfl_notify;
|
||||
|
||||
/*
|
||||
* precheck for sanity
|
||||
*/
|
||||
if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
|
||||
|
||||
npmds = hweight64(arg->smpl_pmds[0]);
|
||||
|
||||
ent = (pfm_default_smpl_entry_t *)cur;
|
||||
|
||||
prefetch(arg->smpl_pmds_values);
|
||||
|
||||
entry_size = sizeof(*ent) + (npmds << 3);
|
||||
|
||||
/* position for first pmd */
|
||||
e = (unsigned long *)(ent+1);
|
||||
|
||||
hdr->hdr_count++;
|
||||
|
||||
DPRINT_ovfl(("[%d] count=%lu cur=%p last=%p free_bytes=%lu ovfl_pmd=%d ovfl_notify=%d npmds=%u\n",
|
||||
task->pid,
|
||||
hdr->hdr_count,
|
||||
cur, last,
|
||||
last-cur,
|
||||
ovfl_pmd,
|
||||
ovfl_notify, npmds));
|
||||
|
||||
/*
|
||||
* current = task running at the time of the overflow.
|
||||
*
|
||||
* per-task mode:
|
||||
* - this is ususally the task being monitored.
|
||||
* Under certain conditions, it might be a different task
|
||||
*
|
||||
* system-wide:
|
||||
* - this is not necessarily the task controlling the session
|
||||
*/
|
||||
#ifndef XEN
|
||||
ent->pid = current->pid;
|
||||
#endif
|
||||
ent->ovfl_pmd = ovfl_pmd;
|
||||
ent->last_reset_val = arg->pmd_last_reset; //pmd[0].reg_last_reset_val;
|
||||
|
||||
/*
|
||||
* where did the fault happen (includes slot number)
|
||||
*/
|
||||
ent->ip = regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3);
|
||||
|
||||
ent->tstamp = stamp;
|
||||
ent->cpu = smp_processor_id();
|
||||
ent->set = arg->active_set;
|
||||
#ifndef XEN
|
||||
ent->tgid = current->tgid;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* selectively store PMDs in increasing index number
|
||||
*/
|
||||
if (npmds) {
|
||||
unsigned long *val = arg->smpl_pmds_values;
|
||||
for(i=0; i < npmds; i++) {
|
||||
*e++ = *val++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* update position for next entry
|
||||
*/
|
||||
hdr->hdr_cur_offs += entry_size;
|
||||
cur += entry_size;
|
||||
|
||||
/*
|
||||
* post check to avoid losing the last sample
|
||||
*/
|
||||
if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full;
|
||||
|
||||
/*
|
||||
* keep same ovfl_pmds, ovfl_notify
|
||||
*/
|
||||
arg->ovfl_ctrl.bits.notify_user = 0;
|
||||
arg->ovfl_ctrl.bits.block_task = 0;
|
||||
arg->ovfl_ctrl.bits.mask_monitoring = 0;
|
||||
arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; /* reset before returning from interrupt handler */
|
||||
|
||||
return 0;
|
||||
full:
|
||||
DPRINT_ovfl(("sampling buffer full free=%lu, count=%lu, ovfl_notify=%d\n", last-cur, hdr->hdr_count, ovfl_notify));
|
||||
|
||||
/*
|
||||
* increment number of buffer overflow.
|
||||
* important to detect duplicate set of samples.
|
||||
*/
|
||||
hdr->hdr_overflows++;
|
||||
|
||||
/*
|
||||
* if no notification requested, then we saturate the buffer
|
||||
*/
|
||||
if (ovfl_notify == 0) {
|
||||
arg->ovfl_ctrl.bits.notify_user = 0;
|
||||
arg->ovfl_ctrl.bits.block_task = 0;
|
||||
arg->ovfl_ctrl.bits.mask_monitoring = 1;
|
||||
arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0;
|
||||
} else {
|
||||
arg->ovfl_ctrl.bits.notify_user = 1;
|
||||
arg->ovfl_ctrl.bits.block_task = 1; /* ignored for non-blocking context */
|
||||
arg->ovfl_ctrl.bits.mask_monitoring = 1;
|
||||
arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0; /* no reset now */
|
||||
}
|
||||
return -1; /* we are full, sorry */
|
||||
}
|
||||
|
||||
static int
|
||||
default_restart(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs)
|
||||
{
|
||||
pfm_default_smpl_hdr_t *hdr;
|
||||
|
||||
hdr = (pfm_default_smpl_hdr_t *)buf;
|
||||
|
||||
hdr->hdr_count = 0UL;
|
||||
hdr->hdr_cur_offs = sizeof(*hdr);
|
||||
|
||||
ctrl->bits.mask_monitoring = 0;
|
||||
ctrl->bits.reset_ovfl_pmds = 1; /* uses long-reset values */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
default_exit(struct task_struct *task, void *buf, struct pt_regs *regs)
|
||||
{
|
||||
DPRINT(("[%d] exit(%p)\n", task->pid, buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pfm_buffer_fmt_t default_fmt={
|
||||
.fmt_name = "default_format",
|
||||
.fmt_uuid = PFM_DEFAULT_SMPL_UUID,
|
||||
.fmt_arg_size = sizeof(pfm_default_smpl_arg_t),
|
||||
.fmt_validate = default_validate,
|
||||
.fmt_getsize = default_get_size,
|
||||
.fmt_init = default_init,
|
||||
.fmt_handler = default_handler,
|
||||
.fmt_restart = default_restart,
|
||||
.fmt_restart_active = default_restart,
|
||||
.fmt_exit = default_exit,
|
||||
};
|
||||
|
||||
#ifndef XEN
|
||||
static int __init
|
||||
pfm_default_smpl_init_module(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pfm_register_buffer_fmt(&default_fmt);
|
||||
if (ret == 0) {
|
||||
printk("perfmon_default_smpl: %s v%u.%u registered\n",
|
||||
default_fmt.fmt_name,
|
||||
PFM_DEFAULT_SMPL_VERSION_MAJ,
|
||||
PFM_DEFAULT_SMPL_VERSION_MIN);
|
||||
} else {
|
||||
printk("perfmon_default_smpl: %s cannot register ret=%d\n",
|
||||
default_fmt.fmt_name,
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __exit
|
||||
pfm_default_smpl_cleanup_module(void)
|
||||
{
|
||||
int ret;
|
||||
ret = pfm_unregister_buffer_fmt(default_fmt.fmt_uuid);
|
||||
|
||||
printk("perfmon_default_smpl: unregister %s=%d\n", default_fmt.fmt_name, ret);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
module_init(pfm_default_smpl_init_module);
|
||||
module_exit(pfm_default_smpl_cleanup_module);
|
||||
#endif
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* This file contains the generic PMU register description tables
|
||||
* and pmc checker used by perfmon.c.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Hewlett Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*/
|
||||
|
||||
static pfm_reg_desc_t pfm_gen_pmc_desc[PMU_MAX_PMCS]={
|
||||
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
static pfm_reg_desc_t pfm_gen_pmd_desc[PMU_MAX_PMDS]={
|
||||
/* pmd0 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
|
||||
/* pmd1 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
|
||||
/* pmd2 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
|
||||
/* pmd3 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
|
||||
/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}},
|
||||
/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}},
|
||||
/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}},
|
||||
/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
/*
|
||||
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
|
||||
*/
|
||||
static pmu_config_t pmu_conf_gen={
|
||||
.pmu_name = "Generic",
|
||||
.pmu_family = 0xff, /* any */
|
||||
.ovfl_val = (1UL << 32) - 1,
|
||||
.num_ibrs = 0, /* does not use */
|
||||
.num_dbrs = 0, /* does not use */
|
||||
.pmd_desc = pfm_gen_pmd_desc,
|
||||
.pmc_desc = pfm_gen_pmc_desc
|
||||
};
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file contains the Itanium PMU register description tables
|
||||
* and pmc checker used by perfmon.c.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Hewlett Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*/
|
||||
static int pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
|
||||
|
||||
static pfm_reg_desc_t pfm_ita_pmc_desc[PMU_MAX_PMCS]={
|
||||
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc4 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc5 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc6 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc7 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc8 */ { PFM_REG_CONFIG , 0, 0xf00000003ffffff8UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc9 */ { PFM_REG_CONFIG , 0, 0xf00000003ffffff8UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc10 */ { PFM_REG_MONITOR , 6, 0x0UL, -1UL, NULL, NULL, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc11 */ { PFM_REG_MONITOR , 6, 0x0000000010000000UL, -1UL, NULL, pfm_ita_pmc_check, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc12 */ { PFM_REG_MONITOR , 6, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc13 */ { PFM_REG_CONFIG , 0, 0x0003ffff00000001UL, -1UL, NULL, pfm_ita_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
static pfm_reg_desc_t pfm_ita_pmd_desc[PMU_MAX_PMDS]={
|
||||
/* pmd0 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
|
||||
/* pmd1 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
|
||||
/* pmd2 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
/* pmd3 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
/* pmd4 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}},
|
||||
/* pmd5 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}},
|
||||
/* pmd6 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}},
|
||||
/* pmd7 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}},
|
||||
/* pmd8 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd9 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd10 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd11 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd12 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd13 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd14 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd15 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd16 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd17 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
static int
|
||||
pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs)
|
||||
{
|
||||
int ret;
|
||||
int is_loaded;
|
||||
|
||||
/* sanitfy check */
|
||||
if (ctx == NULL) return -EINVAL;
|
||||
|
||||
is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED;
|
||||
|
||||
/*
|
||||
* we must clear the (instruction) debug registers if pmc13.ta bit is cleared
|
||||
* before they are written (fl_using_dbreg==0) to avoid picking up stale information.
|
||||
*/
|
||||
if (cnum == 13 && is_loaded && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx has active pmc13.ta cleared, clearing ibr\n", cnum, *val));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers as in use and also
|
||||
* ensure that they are properly cleared.
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(1, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* we must clear the (data) debug registers if pmc11.pt bit is cleared
|
||||
* before they are written (fl_using_dbreg==0) to avoid picking up stale information.
|
||||
*/
|
||||
if (cnum == 11 && is_loaded && ((*val >> 28)& 0x1) == 0 && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx has active pmc11.pt cleared, clearing dbr\n", cnum, *val));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers as in use and also
|
||||
* ensure that they are properly cleared.
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(0, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
|
||||
*/
|
||||
static pmu_config_t pmu_conf_ita={
|
||||
.pmu_name = "Itanium",
|
||||
.pmu_family = 0x7,
|
||||
.ovfl_val = (1UL << 32) - 1,
|
||||
.pmd_desc = pfm_ita_pmd_desc,
|
||||
.pmc_desc = pfm_ita_pmc_desc,
|
||||
.num_ibrs = 8,
|
||||
.num_dbrs = 8,
|
||||
.use_rr_dbregs = 1, /* debug register are use for range retrictions */
|
||||
};
|
||||
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* This file contains the McKinley PMU register description tables
|
||||
* and pmc checker used by perfmon.c.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Hewlett Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*/
|
||||
static int pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
|
||||
|
||||
static pfm_reg_desc_t pfm_mck_pmc_desc[PMU_MAX_PMCS]={
|
||||
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc4 */ { PFM_REG_COUNTING, 6, 0x0000000000800000UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc5 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc6 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc7 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc8 */ { PFM_REG_CONFIG , 0, 0xffffffff3fffffffUL, 0xffffffff3ffffffbUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc9 */ { PFM_REG_CONFIG , 0, 0xffffffff3ffffffcUL, 0xffffffff3ffffffbUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc10 */ { PFM_REG_MONITOR , 4, 0x0UL, 0xffffUL, NULL, pfm_mck_pmc_check, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc11 */ { PFM_REG_MONITOR , 6, 0x0UL, 0x30f01cf, NULL, pfm_mck_pmc_check, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc12 */ { PFM_REG_MONITOR , 6, 0x0UL, 0xffffUL, NULL, pfm_mck_pmc_check, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc13 */ { PFM_REG_CONFIG , 0, 0x00002078fefefefeUL, 0x1e00018181818UL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc14 */ { PFM_REG_CONFIG , 0, 0x0db60db60db60db6UL, 0x2492UL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
/* pmc15 */ { PFM_REG_CONFIG , 0, 0x00000000fffffff0UL, 0xfUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
static pfm_reg_desc_t pfm_mck_pmd_desc[PMU_MAX_PMDS]={
|
||||
/* pmd0 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
|
||||
/* pmd1 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
|
||||
/* pmd2 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
/* pmd3 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}},
|
||||
/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}},
|
||||
/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}},
|
||||
/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}},
|
||||
/* pmd8 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd9 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd10 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd11 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd12 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd13 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd14 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd15 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd16 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}},
|
||||
/* pmd17 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
|
||||
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
/*
|
||||
* PMC reserved fields must have their power-up values preserved
|
||||
*/
|
||||
static int
|
||||
pfm_mck_reserved(unsigned int cnum, unsigned long *val, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long tmp1, tmp2, ival = *val;
|
||||
|
||||
/* remove reserved areas from user value */
|
||||
tmp1 = ival & PMC_RSVD_MASK(cnum);
|
||||
|
||||
/* get reserved fields values */
|
||||
tmp2 = PMC_DFL_VAL(cnum) & ~PMC_RSVD_MASK(cnum);
|
||||
|
||||
*val = tmp1 | tmp2;
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx, mask=0x%lx, reset=0x%lx, val=0x%lx\n",
|
||||
cnum, ival, PMC_RSVD_MASK(cnum), PMC_DFL_VAL(cnum), *val));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* task can be NULL if the context is unloaded
|
||||
*/
|
||||
static int
|
||||
pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs)
|
||||
{
|
||||
int ret = 0, check_case1 = 0;
|
||||
unsigned long val8 = 0, val14 = 0, val13 = 0;
|
||||
int is_loaded;
|
||||
|
||||
/* first preserve the reserved fields */
|
||||
pfm_mck_reserved(cnum, val, regs);
|
||||
|
||||
/* sanitfy check */
|
||||
if (ctx == NULL) return -EINVAL;
|
||||
|
||||
is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED;
|
||||
|
||||
/*
|
||||
* we must clear the debug registers if pmc13 has a value which enable
|
||||
* memory pipeline event constraints. In this case we need to clear the
|
||||
* the debug registers if they have not yet been accessed. This is required
|
||||
* to avoid picking stale state.
|
||||
* PMC13 is "active" if:
|
||||
* one of the pmc13.cfg_dbrpXX field is different from 0x3
|
||||
* AND
|
||||
* at the corresponding pmc13.ena_dbrpXX is set.
|
||||
*/
|
||||
DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, *val, ctx->ctx_fl_using_dbreg, is_loaded));
|
||||
|
||||
if (cnum == 13 && is_loaded
|
||||
&& (*val & 0x1e00000000000UL) && (*val & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx has active pmc13 settings, clearing dbr\n", cnum, *val));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers as in use and also
|
||||
* ensure that they are properly cleared.
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(PFM_DATA_RR, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
}
|
||||
/*
|
||||
* we must clear the (instruction) debug registers if any pmc14.ibrpX bit is enabled
|
||||
* before they are (fl_using_dbreg==0) to avoid picking up stale information.
|
||||
*/
|
||||
if (cnum == 14 && is_loaded && ((*val & 0x2222UL) != 0x2222UL) && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx has active pmc14 settings, clearing ibr\n", cnum, *val));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers as in use and also
|
||||
* ensure that they are properly cleared.
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(PFM_CODE_RR, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
|
||||
}
|
||||
|
||||
switch(cnum) {
|
||||
case 4: *val |= 1UL << 23; /* force power enable bit */
|
||||
break;
|
||||
case 8: val8 = *val;
|
||||
val13 = ctx->ctx_pmcs[13];
|
||||
val14 = ctx->ctx_pmcs[14];
|
||||
check_case1 = 1;
|
||||
break;
|
||||
case 13: val8 = ctx->ctx_pmcs[8];
|
||||
val13 = *val;
|
||||
val14 = ctx->ctx_pmcs[14];
|
||||
check_case1 = 1;
|
||||
break;
|
||||
case 14: val8 = ctx->ctx_pmcs[8];
|
||||
val13 = ctx->ctx_pmcs[13];
|
||||
val14 = *val;
|
||||
check_case1 = 1;
|
||||
break;
|
||||
}
|
||||
/* check illegal configuration which can produce inconsistencies in tagging
|
||||
* i-side events in L1D and L2 caches
|
||||
*/
|
||||
if (check_case1) {
|
||||
ret = ((val13 >> 45) & 0xf) == 0
|
||||
&& ((val8 & 0x1) == 0)
|
||||
&& ((((val14>>1) & 0x3) == 0x2 || ((val14>>1) & 0x3) == 0x0)
|
||||
||(((val14>>4) & 0x3) == 0x2 || ((val14>>4) & 0x3) == 0x0));
|
||||
|
||||
if (ret) DPRINT((KERN_DEBUG "perfmon: failure check_case1\n"));
|
||||
}
|
||||
|
||||
return ret ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
|
||||
*/
|
||||
static pmu_config_t pmu_conf_mck={
|
||||
.pmu_name = "Itanium 2",
|
||||
.pmu_family = 0x1f,
|
||||
.flags = PFM_PMU_IRQ_RESEND,
|
||||
.ovfl_val = (1UL << 47) - 1,
|
||||
.pmd_desc = pfm_mck_pmd_desc,
|
||||
.pmc_desc = pfm_mck_pmc_desc,
|
||||
.num_ibrs = 8,
|
||||
.num_dbrs = 8,
|
||||
.use_rr_dbregs = 1 /* debug register are use for range retrictions */
|
||||
};
|
||||
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* This file contains the Montecito PMU register description tables
|
||||
* and pmc checker used by perfmon.c.
|
||||
*
|
||||
* Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P.
|
||||
* Contributed by Stephane Eranian <eranian@hpl.hp.com>
|
||||
*/
|
||||
static int pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
|
||||
|
||||
#define RDEP_MONT_ETB (RDEP(38)|RDEP(39)|RDEP(48)|RDEP(49)|RDEP(50)|RDEP(51)|RDEP(52)|RDEP(53)|RDEP(54)|\
|
||||
RDEP(55)|RDEP(56)|RDEP(57)|RDEP(58)|RDEP(59)|RDEP(60)|RDEP(61)|RDEP(62)|RDEP(63))
|
||||
#define RDEP_MONT_DEAR (RDEP(32)|RDEP(33)|RDEP(36))
|
||||
#define RDEP_MONT_IEAR (RDEP(34)|RDEP(35))
|
||||
|
||||
static pfm_reg_desc_t pfm_mont_pmc_desc[PMU_MAX_PMCS]={
|
||||
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc4 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(4),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc5 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(5),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc6 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(6),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc7 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(7),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc8 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(8),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc9 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(9),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc10 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(10),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc11 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(11),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc12 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(12),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc13 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(13),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc14 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(14),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc15 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(15),0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc16 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc17 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc18 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc19 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc20 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc21 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc22 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc23 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc24 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc25 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc26 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc27 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc28 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc29 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc30 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc31 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmc32 */ { PFM_REG_CONFIG, 0, 0x30f01ffffffffff, 0x30f01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc33 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc34 */ { PFM_REG_CONFIG, 0, 0xf01ffffffffff, 0xf01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc35 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc36 */ { PFM_REG_CONFIG, 0, 0xfffffff0, 0xf, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc37 */ { PFM_REG_MONITOR, 4, 0x0, 0x3fff, NULL, pfm_mont_pmc_check, {RDEP_MONT_IEAR, 0, 0, 0}, {0, 0, 0, 0}},
|
||||
/* pmc38 */ { PFM_REG_CONFIG, 0, 0xdb6, 0x2492, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc39 */ { PFM_REG_MONITOR, 6, 0x0, 0xffcf, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc40 */ { PFM_REG_MONITOR, 6, 0x2000000, 0xf01cf, NULL, pfm_mont_pmc_check, {RDEP_MONT_DEAR,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc41 */ { PFM_REG_CONFIG, 0, 0x00002078fefefefe, 0x1e00018181818, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}},
|
||||
/* pmc42 */ { PFM_REG_MONITOR, 6, 0x0, 0x7ff4f, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}},
|
||||
{ PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
static pfm_reg_desc_t pfm_mont_pmd_desc[PMU_MAX_PMDS]={
|
||||
/* pmd0 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd1 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd2 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd3 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(4),0, 0, 0}},
|
||||
/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(5),0, 0, 0}},
|
||||
/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(6),0, 0, 0}},
|
||||
/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(7),0, 0, 0}},
|
||||
/* pmd8 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(8),0, 0, 0}},
|
||||
/* pmd9 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(9),0, 0, 0}},
|
||||
/* pmd10 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(10),0, 0, 0}},
|
||||
/* pmd11 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(11),0, 0, 0}},
|
||||
/* pmd12 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(12),0, 0, 0}},
|
||||
/* pmd13 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(13),0, 0, 0}},
|
||||
/* pmd14 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(14),0, 0, 0}},
|
||||
/* pmd15 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(15),0, 0, 0}},
|
||||
/* pmd16 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd17 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd18 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd19 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd20 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd21 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd22 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd23 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd24 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd25 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd26 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd27 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd28 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd29 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd30 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd31 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd32 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(33)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}},
|
||||
/* pmd33 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}},
|
||||
/* pmd34 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(35),0, 0, 0}, {RDEP(37),0, 0, 0}},
|
||||
/* pmd35 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(34),0, 0, 0}, {RDEP(37),0, 0, 0}},
|
||||
/* pmd36 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(33),0, 0, 0}, {RDEP(40),0, 0, 0}},
|
||||
/* pmd37 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd38 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd39 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd40 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd41 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd42 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd43 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd44 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd45 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd46 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd47 */ { PFM_REG_NOTIMPL, },
|
||||
/* pmd48 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd49 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd50 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd51 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd52 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd53 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd54 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd55 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd56 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd57 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd58 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd59 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd60 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd61 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd62 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
/* pmd63 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}},
|
||||
{ PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */
|
||||
};
|
||||
|
||||
/*
|
||||
* PMC reserved fields must have their power-up values preserved
|
||||
*/
|
||||
static int
|
||||
pfm_mont_reserved(unsigned int cnum, unsigned long *val, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long tmp1, tmp2, ival = *val;
|
||||
|
||||
/* remove reserved areas from user value */
|
||||
tmp1 = ival & PMC_RSVD_MASK(cnum);
|
||||
|
||||
/* get reserved fields values */
|
||||
tmp2 = PMC_DFL_VAL(cnum) & ~PMC_RSVD_MASK(cnum);
|
||||
|
||||
*val = tmp1 | tmp2;
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx, mask=0x%lx, reset=0x%lx, val=0x%lx\n",
|
||||
cnum, ival, PMC_RSVD_MASK(cnum), PMC_DFL_VAL(cnum), *val));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* task can be NULL if the context is unloaded
|
||||
*/
|
||||
static int
|
||||
pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long val32 = 0, val38 = 0, val41 = 0;
|
||||
unsigned long tmpval;
|
||||
int check_case1 = 0;
|
||||
int is_loaded;
|
||||
|
||||
/* first preserve the reserved fields */
|
||||
pfm_mont_reserved(cnum, val, regs);
|
||||
|
||||
tmpval = *val;
|
||||
|
||||
/* sanity check */
|
||||
if (ctx == NULL) return -EINVAL;
|
||||
|
||||
is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED;
|
||||
|
||||
/*
|
||||
* we must clear the debug registers if pmc41 has a value which enable
|
||||
* memory pipeline event constraints. In this case we need to clear the
|
||||
* the debug registers if they have not yet been accessed. This is required
|
||||
* to avoid picking stale state.
|
||||
* PMC41 is "active" if:
|
||||
* one of the pmc41.cfg_dtagXX field is different from 0x3
|
||||
* AND
|
||||
* at the corresponding pmc41.en_dbrpXX is set.
|
||||
* AND
|
||||
* ctx_fl_using_dbreg == 0 (i.e., dbr not yet used)
|
||||
*/
|
||||
DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, tmpval, ctx->ctx_fl_using_dbreg, is_loaded));
|
||||
|
||||
if (cnum == 41 && is_loaded
|
||||
&& (tmpval & 0x1e00000000000) && (tmpval & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc[%d]=0x%lx has active pmc41 settings, clearing dbr\n", cnum, tmpval));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers if:
|
||||
* AND
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(PFM_DATA_RR, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
}
|
||||
/*
|
||||
* we must clear the (instruction) debug registers if:
|
||||
* pmc38.ig_ibrpX is 0 (enabled)
|
||||
* AND
|
||||
* ctx_fl_using_dbreg == 0 (i.e., dbr not yet used)
|
||||
*/
|
||||
if (cnum == 38 && is_loaded && ((tmpval & 0x492UL) != 0x492UL) && ctx->ctx_fl_using_dbreg == 0) {
|
||||
|
||||
DPRINT(("pmc38=0x%lx has active pmc38 settings, clearing ibr\n", tmpval));
|
||||
|
||||
/* don't mix debug with perfmon */
|
||||
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
|
||||
|
||||
/*
|
||||
* a count of 0 will mark the debug registers as in use and also
|
||||
* ensure that they are properly cleared.
|
||||
*/
|
||||
ret = pfm_write_ibr_dbr(PFM_CODE_RR, ctx, NULL, 0, regs);
|
||||
if (ret) return ret;
|
||||
|
||||
}
|
||||
switch(cnum) {
|
||||
case 32: val32 = *val;
|
||||
val38 = ctx->ctx_pmcs[38];
|
||||
val41 = ctx->ctx_pmcs[41];
|
||||
check_case1 = 1;
|
||||
break;
|
||||
case 38: val38 = *val;
|
||||
val32 = ctx->ctx_pmcs[32];
|
||||
val41 = ctx->ctx_pmcs[41];
|
||||
check_case1 = 1;
|
||||
break;
|
||||
case 41: val41 = *val;
|
||||
val32 = ctx->ctx_pmcs[32];
|
||||
val38 = ctx->ctx_pmcs[38];
|
||||
check_case1 = 1;
|
||||
break;
|
||||
}
|
||||
/* check illegal configuration which can produce inconsistencies in tagging
|
||||
* i-side events in L1D and L2 caches
|
||||
*/
|
||||
if (check_case1) {
|
||||
ret = (((val41 >> 45) & 0xf) == 0 && ((val32>>57) & 0x1) == 0)
|
||||
&& ((((val38>>1) & 0x3) == 0x2 || ((val38>>1) & 0x3) == 0)
|
||||
|| (((val38>>4) & 0x3) == 0x2 || ((val38>>4) & 0x3) == 0));
|
||||
if (ret) {
|
||||
DPRINT(("invalid config pmc38=0x%lx pmc41=0x%lx pmc32=0x%lx\n", val38, val41, val32));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
*val = tmpval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
|
||||
*/
|
||||
static pmu_config_t pmu_conf_mont={
|
||||
.pmu_name = "Montecito",
|
||||
.pmu_family = 0x20,
|
||||
.flags = PFM_PMU_IRQ_RESEND,
|
||||
.ovfl_val = (1UL << 47) - 1,
|
||||
.pmd_desc = pfm_mont_pmd_desc,
|
||||
.pmc_desc = pfm_mont_pmc_desc,
|
||||
.num_ibrs = 8,
|
||||
.num_dbrs = 8,
|
||||
.use_rr_dbregs = 1 /* debug register are use for range retrictions */
|
||||
};
|
||||
@@ -1,891 +0,0 @@
|
||||
/*
|
||||
* Architecture-specific setup.
|
||||
*
|
||||
* Copyright (C) 1998-2003 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* 04/11/17 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
|
||||
*/
|
||||
#ifdef XEN
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <xen/types.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/symbols.h>
|
||||
#include <xen/smp.h>
|
||||
#include <xen/sched.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/unwind.h>
|
||||
#include <asm/sal.h>
|
||||
#else
|
||||
#define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/ia32.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unwind.h>
|
||||
#include <asm/user.h>
|
||||
|
||||
#include "entry.h"
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
# include <asm/perfmon.h>
|
||||
#endif
|
||||
|
||||
#include "sigframe.h"
|
||||
|
||||
void (*ia64_mark_idle)(int);
|
||||
static DEFINE_PER_CPU(unsigned int, cpu_idle_state);
|
||||
|
||||
unsigned long boot_option_idle_override = 0;
|
||||
EXPORT_SYMBOL(boot_option_idle_override);
|
||||
#endif
|
||||
|
||||
void
|
||||
ia64_do_show_stack (struct unw_frame_info *info, void *arg)
|
||||
{
|
||||
unsigned long ip, sp, bsp;
|
||||
char buf[128]; /* don't make it so big that it overflows the stack! */
|
||||
|
||||
printk("\nCall Trace:\n");
|
||||
do {
|
||||
unw_get_ip(info, &ip);
|
||||
if (ip == 0)
|
||||
break;
|
||||
|
||||
unw_get_sp(info, &sp);
|
||||
unw_get_bsp(info, &bsp);
|
||||
snprintf(buf, sizeof(buf),
|
||||
" [<%016lx>] %%s\n"
|
||||
" sp=%016lx bsp=%016lx\n",
|
||||
ip, sp, bsp);
|
||||
print_symbol(buf, ip);
|
||||
} while (unw_unwind(info) >= 0);
|
||||
}
|
||||
|
||||
void
|
||||
show_stack (struct task_struct *task, unsigned long *sp)
|
||||
{
|
||||
if (!task)
|
||||
unw_init_running(ia64_do_show_stack, NULL);
|
||||
else {
|
||||
struct unw_frame_info info;
|
||||
|
||||
unw_init_from_blocked_task(&info, task);
|
||||
ia64_do_show_stack(&info, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_stack (void)
|
||||
{
|
||||
show_stack(NULL, NULL);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
#ifdef XEN
|
||||
void
|
||||
show_registers(struct pt_regs *regs)
|
||||
#else
|
||||
void
|
||||
show_regs (struct pt_regs *regs)
|
||||
#endif
|
||||
{
|
||||
unsigned long ip = regs->cr_iip + ia64_psr(regs)->ri;
|
||||
|
||||
#ifndef XEN
|
||||
print_modules();
|
||||
printk("\nPid: %d, CPU %d, comm: %20s\n", current->pid, smp_processor_id(), current->comm);
|
||||
printk("psr : %016lx ifs : %016lx ip : [<%016lx>] %s\n",
|
||||
regs->cr_ipsr, regs->cr_ifs, ip, print_tainted());
|
||||
#else
|
||||
struct vcpu* vcpu = current;
|
||||
if (vcpu != NULL) {
|
||||
struct domain* d = vcpu->domain;
|
||||
printk("d 0x%p domid %d\n", d, d->domain_id);
|
||||
printk("vcpu 0x%p vcpu %d\n",
|
||||
vcpu, vcpu->vcpu_id);
|
||||
}
|
||||
printk("\nCPU %d\n", smp_processor_id());
|
||||
printk("psr : %016lx ifs : %016lx ip : [<%016lx>]\n",
|
||||
regs->cr_ipsr, regs->cr_ifs, ip);
|
||||
#endif
|
||||
print_symbol("ip is at %s\n", ip);
|
||||
printk("unat: %016lx pfs : %016lx rsc : %016lx\n",
|
||||
regs->ar_unat, regs->ar_pfs, regs->ar_rsc);
|
||||
printk("rnat: %016lx bsps: %016lx pr : %016lx\n",
|
||||
regs->ar_rnat, regs->ar_bspstore, regs->pr);
|
||||
printk("ldrs: %016lx ccv : %016lx fpsr: %016lx\n",
|
||||
regs->loadrs, regs->ar_ccv, regs->ar_fpsr);
|
||||
printk("csd : %016lx ssd : %016lx\n", regs->ar_csd, regs->ar_ssd);
|
||||
printk("b0 : %016lx b6 : %016lx b7 : %016lx\n", regs->b0, regs->b6, regs->b7);
|
||||
printk("f6 : %05lx%016lx f7 : %05lx%016lx\n",
|
||||
regs->f6.u.bits[1], regs->f6.u.bits[0],
|
||||
regs->f7.u.bits[1], regs->f7.u.bits[0]);
|
||||
printk("f8 : %05lx%016lx f9 : %05lx%016lx\n",
|
||||
regs->f8.u.bits[1], regs->f8.u.bits[0],
|
||||
regs->f9.u.bits[1], regs->f9.u.bits[0]);
|
||||
printk("f10 : %05lx%016lx f11 : %05lx%016lx\n",
|
||||
regs->f10.u.bits[1], regs->f10.u.bits[0],
|
||||
regs->f11.u.bits[1], regs->f11.u.bits[0]);
|
||||
|
||||
printk("r1 : %016lx r2 : %016lx r3 : %016lx\n", regs->r1, regs->r2, regs->r3);
|
||||
printk("r8 : %016lx r9 : %016lx r10 : %016lx\n", regs->r8, regs->r9, regs->r10);
|
||||
printk("r11 : %016lx r12 : %016lx r13 : %016lx\n", regs->r11, regs->r12, regs->r13);
|
||||
printk("r14 : %016lx r15 : %016lx r16 : %016lx\n", regs->r14, regs->r15, regs->r16);
|
||||
printk("r17 : %016lx r18 : %016lx r19 : %016lx\n", regs->r17, regs->r18, regs->r19);
|
||||
printk("r20 : %016lx r21 : %016lx r22 : %016lx\n", regs->r20, regs->r21, regs->r22);
|
||||
printk("r23 : %016lx r24 : %016lx r25 : %016lx\n", regs->r23, regs->r24, regs->r25);
|
||||
printk("r26 : %016lx r27 : %016lx r28 : %016lx\n", regs->r26, regs->r27, regs->r28);
|
||||
printk("r29 : %016lx r30 : %016lx r31 : %016lx\n", regs->r29, regs->r30, regs->r31);
|
||||
|
||||
#ifndef XEN
|
||||
if (user_mode(regs)) {
|
||||
/* print the stacked registers */
|
||||
unsigned long val, *bsp, ndirty;
|
||||
int i, sof, is_nat = 0;
|
||||
|
||||
sof = regs->cr_ifs & 0x7f; /* size of frame */
|
||||
ndirty = (regs->loadrs >> 19);
|
||||
bsp = ia64_rse_skip_regs((unsigned long *) regs->ar_bspstore, ndirty);
|
||||
for (i = 0; i < sof; ++i) {
|
||||
get_user(val, (unsigned long __user *) ia64_rse_skip_regs(bsp, i));
|
||||
printk("r%-3u:%c%016lx%s", 32 + i, is_nat ? '*' : ' ', val,
|
||||
((i == sof - 1) || (i % 3) == 2) ? "\n" : " ");
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
show_stack(NULL, NULL);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
void
|
||||
do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall)
|
||||
{
|
||||
if (fsys_mode(current, &scr->pt)) {
|
||||
/* defer signal-handling etc. until we return to privilege-level 0. */
|
||||
if (!ia64_psr(&scr->pt)->lp)
|
||||
ia64_psr(&scr->pt)->lp = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
if (current->thread.pfm_needs_checking)
|
||||
pfm_handle_work();
|
||||
#endif
|
||||
|
||||
/* deal with pending signal delivery */
|
||||
if (test_thread_flag(TIF_SIGPENDING))
|
||||
ia64_do_signal(oldset, scr, in_syscall);
|
||||
}
|
||||
|
||||
static int pal_halt = 1;
|
||||
static int can_do_pal_halt = 1;
|
||||
|
||||
static int __init nohalt_setup(char * str)
|
||||
{
|
||||
pal_halt = can_do_pal_halt = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("nohalt", nohalt_setup);
|
||||
|
||||
void
|
||||
update_pal_halt_status(int status)
|
||||
{
|
||||
can_do_pal_halt = pal_halt && status;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use this if we don't have any better idle routine..
|
||||
*/
|
||||
void
|
||||
default_idle (void)
|
||||
{
|
||||
local_irq_enable();
|
||||
while (!need_resched())
|
||||
if (can_do_pal_halt)
|
||||
safe_halt();
|
||||
else
|
||||
cpu_relax();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
/* We don't actually take CPU down, just spin without interrupts. */
|
||||
#ifndef XEN
|
||||
static inline void play_dead(void)
|
||||
#else
|
||||
void play_dead(void)
|
||||
#endif
|
||||
{
|
||||
extern void ia64_cpu_local_tick (void);
|
||||
unsigned int this_cpu = smp_processor_id();
|
||||
|
||||
/* Ack it */
|
||||
__get_cpu_var(cpu_state) = CPU_DEAD;
|
||||
|
||||
max_xtp();
|
||||
local_irq_disable();
|
||||
ia64_jump_to_sal(&sal_boot_rendez_state[this_cpu]);
|
||||
/*
|
||||
* The above is a point of no-return, the processor is
|
||||
* expected to be in SAL loop now.
|
||||
*/
|
||||
BUG();
|
||||
}
|
||||
#else
|
||||
#ifndef XEN
|
||||
static inline void play_dead(void)
|
||||
#else
|
||||
void play_dead(void)
|
||||
#endif
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
#ifndef XEN
|
||||
void cpu_idle_wait(void)
|
||||
{
|
||||
unsigned int cpu, this_cpu = get_cpu();
|
||||
cpumask_t map;
|
||||
|
||||
set_cpus_allowed(current, cpumask_of_cpu(this_cpu));
|
||||
put_cpu();
|
||||
|
||||
cpus_clear(map);
|
||||
for_each_online_cpu(cpu) {
|
||||
per_cpu(cpu_idle_state, cpu) = 1;
|
||||
cpu_set(cpu, map);
|
||||
}
|
||||
|
||||
__get_cpu_var(cpu_idle_state) = 0;
|
||||
|
||||
wmb();
|
||||
do {
|
||||
ssleep(1);
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu_isset(cpu, map) && !per_cpu(cpu_idle_state, cpu))
|
||||
cpu_clear(cpu, map);
|
||||
}
|
||||
cpus_and(map, map, cpu_online_map);
|
||||
} while (!cpus_empty(map));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_idle_wait);
|
||||
|
||||
void __attribute__((noreturn))
|
||||
cpu_idle (void)
|
||||
{
|
||||
void (*mark_idle)(int) = ia64_mark_idle;
|
||||
|
||||
/* endless idle loop with no priority at all */
|
||||
while (1) {
|
||||
#ifdef CONFIG_SMP
|
||||
if (!need_resched())
|
||||
min_xtp();
|
||||
#endif
|
||||
while (!need_resched()) {
|
||||
void (*idle)(void);
|
||||
|
||||
if (__get_cpu_var(cpu_idle_state))
|
||||
__get_cpu_var(cpu_idle_state) = 0;
|
||||
|
||||
rmb();
|
||||
if (mark_idle)
|
||||
(*mark_idle)(1);
|
||||
|
||||
idle = pm_idle;
|
||||
if (!idle)
|
||||
idle = default_idle;
|
||||
(*idle)();
|
||||
}
|
||||
|
||||
if (mark_idle)
|
||||
(*mark_idle)(0);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
normal_xtp();
|
||||
#endif
|
||||
schedule();
|
||||
check_pgt_cache();
|
||||
if (cpu_is_offline(smp_processor_id()))
|
||||
play_dead();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ia64_save_extra (struct task_struct *task)
|
||||
{
|
||||
#ifdef CONFIG_PERFMON
|
||||
unsigned long info;
|
||||
#endif
|
||||
|
||||
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
|
||||
ia64_save_debug_regs(&task->thread.dbr[0]);
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
|
||||
pfm_save_regs(task);
|
||||
|
||||
info = __get_cpu_var(pfm_syst_info);
|
||||
if (info & PFM_CPUINFO_SYST_WIDE)
|
||||
pfm_syst_wide_update_task(task, info, 0);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IA32_SUPPORT
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(task)))
|
||||
ia32_save_state(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ia64_load_extra (struct task_struct *task)
|
||||
{
|
||||
#ifdef CONFIG_PERFMON
|
||||
unsigned long info;
|
||||
#endif
|
||||
|
||||
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
|
||||
ia64_load_debug_regs(&task->thread.dbr[0]);
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
|
||||
pfm_load_regs(task);
|
||||
|
||||
info = __get_cpu_var(pfm_syst_info);
|
||||
if (info & PFM_CPUINFO_SYST_WIDE)
|
||||
pfm_syst_wide_update_task(task, info, 1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IA32_SUPPORT
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(task)))
|
||||
ia32_load_state(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the state of an ia-64 thread.
|
||||
*
|
||||
* We get here through the following call chain:
|
||||
*
|
||||
* from user-level: from kernel:
|
||||
*
|
||||
* <clone syscall> <some kernel call frames>
|
||||
* sys_clone :
|
||||
* do_fork do_fork
|
||||
* copy_thread copy_thread
|
||||
*
|
||||
* This means that the stack layout is as follows:
|
||||
*
|
||||
* +---------------------+ (highest addr)
|
||||
* | struct pt_regs |
|
||||
* +---------------------+
|
||||
* | struct switch_stack |
|
||||
* +---------------------+
|
||||
* | |
|
||||
* | memory stack |
|
||||
* | | <-- sp (lowest addr)
|
||||
* +---------------------+
|
||||
*
|
||||
* Observe that we copy the unat values that are in pt_regs and switch_stack. Spilling an
|
||||
* integer to address X causes bit N in ar.unat to be set to the NaT bit of the register,
|
||||
* with N=(X & 0x1ff)/8. Thus, copying the unat value preserves the NaT bits ONLY if the
|
||||
* pt_regs structure in the parent is congruent to that of the child, modulo 512. Since
|
||||
* the stack is page aligned and the page size is at least 4KB, this is always the case,
|
||||
* so there is nothing to worry about.
|
||||
*/
|
||||
int
|
||||
copy_thread (int nr, unsigned long clone_flags,
|
||||
unsigned long user_stack_base, unsigned long user_stack_size,
|
||||
struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
extern char ia64_ret_from_clone, ia32_ret_from_clone;
|
||||
struct switch_stack *child_stack, *stack;
|
||||
unsigned long rbs, child_rbs, rbs_size;
|
||||
struct pt_regs *child_ptregs;
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* For SMP idle threads, fork_by_hand() calls do_fork with
|
||||
* NULL regs.
|
||||
*/
|
||||
if (!regs)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
stack = ((struct switch_stack *) regs) - 1;
|
||||
|
||||
child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
|
||||
child_stack = (struct switch_stack *) child_ptregs - 1;
|
||||
|
||||
/* copy parent's switch_stack & pt_regs to child: */
|
||||
memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
|
||||
|
||||
rbs = (unsigned long) current + IA64_RBS_OFFSET;
|
||||
child_rbs = (unsigned long) p + IA64_RBS_OFFSET;
|
||||
rbs_size = stack->ar_bspstore - rbs;
|
||||
|
||||
/* copy the parent's register backing store to the child: */
|
||||
memcpy((void *) child_rbs, (void *) rbs, rbs_size);
|
||||
|
||||
if (likely(user_mode(child_ptregs))) {
|
||||
if ((clone_flags & CLONE_SETTLS) && !IS_IA32_PROCESS(regs))
|
||||
child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
|
||||
if (user_stack_base) {
|
||||
child_ptregs->r12 = user_stack_base + user_stack_size - 16;
|
||||
child_ptregs->ar_bspstore = user_stack_base;
|
||||
child_ptregs->ar_rnat = 0;
|
||||
child_ptregs->loadrs = 0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Note: we simply preserve the relative position of
|
||||
* the stack pointer here. There is no need to
|
||||
* allocate a scratch area here, since that will have
|
||||
* been taken care of by the caller of sys_clone()
|
||||
* already.
|
||||
*/
|
||||
child_ptregs->r12 = (unsigned long) child_ptregs - 16; /* kernel sp */
|
||||
child_ptregs->r13 = (unsigned long) p; /* set `current' pointer */
|
||||
}
|
||||
child_stack->ar_bspstore = child_rbs + rbs_size;
|
||||
if (IS_IA32_PROCESS(regs))
|
||||
child_stack->b0 = (unsigned long) &ia32_ret_from_clone;
|
||||
else
|
||||
child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
|
||||
|
||||
/* copy parts of thread_struct: */
|
||||
p->thread.ksp = (unsigned long) child_stack - 16;
|
||||
|
||||
/* stop some PSR bits from being inherited.
|
||||
* the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
|
||||
* therefore we must specify them explicitly here and not include them in
|
||||
* IA64_PSR_BITS_TO_CLEAR.
|
||||
*/
|
||||
child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
|
||||
& ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
|
||||
|
||||
/*
|
||||
* NOTE: The calling convention considers all floating point
|
||||
* registers in the high partition (fph) to be scratch. Since
|
||||
* the only way to get to this point is through a system call,
|
||||
* we know that the values in fph are all dead. Hence, there
|
||||
* is no need to inherit the fph state from the parent to the
|
||||
* child and all we have to do is to make sure that
|
||||
* IA64_THREAD_FPH_VALID is cleared in the child.
|
||||
*
|
||||
* XXX We could push this optimization a bit further by
|
||||
* clearing IA64_THREAD_FPH_VALID on ANY system call.
|
||||
* However, it's not clear this is worth doing. Also, it
|
||||
* would be a slight deviation from the normal Linux system
|
||||
* call behavior where scratch registers are preserved across
|
||||
* system calls (unless used by the system call itself).
|
||||
*/
|
||||
# define THREAD_FLAGS_TO_CLEAR (IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID \
|
||||
| IA64_THREAD_PM_VALID)
|
||||
# define THREAD_FLAGS_TO_SET 0
|
||||
p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
|
||||
| THREAD_FLAGS_TO_SET);
|
||||
ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
|
||||
#ifdef CONFIG_IA32_SUPPORT
|
||||
/*
|
||||
* If we're cloning an IA32 task then save the IA32 extra
|
||||
* state from the current task to the new task
|
||||
*/
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(current))) {
|
||||
ia32_save_state(p);
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
retval = ia32_clone_tls(p, child_ptregs);
|
||||
|
||||
/* Copy partially mapped page list */
|
||||
if (!retval)
|
||||
retval = ia32_copy_partial_page_list(p, clone_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
if (current->thread.pfm_context)
|
||||
pfm_inherit(p, child_ptregs);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif /* !XEN */
|
||||
|
||||
static void
|
||||
do_copy_task_regs (struct task_struct *task, struct unw_frame_info *info, void *arg)
|
||||
{
|
||||
unsigned long mask, sp, nat_bits = 0, ip, ar_rnat, urbs_end, cfm;
|
||||
elf_greg_t *dst = arg;
|
||||
struct pt_regs *pt;
|
||||
char nat;
|
||||
int i;
|
||||
|
||||
memset(dst, 0, sizeof(elf_gregset_t)); /* don't leak any kernel bits to user-level */
|
||||
|
||||
if (unw_unwind_to_user(info) < 0)
|
||||
return;
|
||||
|
||||
unw_get_sp(info, &sp);
|
||||
pt = (struct pt_regs *) (sp + 16);
|
||||
|
||||
#ifndef XEN
|
||||
/* FIXME: Is this needed by XEN when it makes its crash notes
|
||||
* during kdump? */
|
||||
urbs_end = ia64_get_user_rbs_end(task, pt, &cfm);
|
||||
|
||||
if (ia64_sync_user_rbs(task, info->sw, pt->ar_bspstore, urbs_end) < 0)
|
||||
return;
|
||||
|
||||
ia64_peek(task, info->sw, urbs_end, (long) ia64_rse_rnat_addr((long *) urbs_end),
|
||||
&ar_rnat);
|
||||
#else
|
||||
|
||||
ia64_peek(task, info->sw, urbs_end, (long) ia64_rse_rnat_addr((u64 *) urbs_end),
|
||||
(long *)&ar_rnat);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* coredump format:
|
||||
* r0-r31
|
||||
* NaT bits (for r0-r31; bit N == 1 iff rN is a NaT)
|
||||
* predicate registers (p0-p63)
|
||||
* b0-b7
|
||||
* ip cfm user-mask
|
||||
* ar.rsc ar.bsp ar.bspstore ar.rnat
|
||||
* ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
|
||||
*/
|
||||
|
||||
/* r0 is zero */
|
||||
for (i = 1, mask = (1UL << i); i < 32; ++i) {
|
||||
unw_get_gr(info, i, &dst[i], &nat);
|
||||
if (nat)
|
||||
nat_bits |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
dst[32] = nat_bits;
|
||||
unw_get_pr(info, &dst[33]);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
unw_get_br(info, i, &dst[34 + i]);
|
||||
|
||||
unw_get_rp(info, &ip);
|
||||
dst[42] = ip + ia64_psr(pt)->ri;
|
||||
dst[43] = cfm;
|
||||
dst[44] = pt->cr_ipsr & IA64_PSR_UM;
|
||||
|
||||
unw_get_ar(info, UNW_AR_RSC, &dst[45]);
|
||||
/*
|
||||
* For bsp and bspstore, unw_get_ar() would return the kernel
|
||||
* addresses, but we need the user-level addresses instead:
|
||||
*/
|
||||
dst[46] = urbs_end; /* note: by convention PT_AR_BSP points to the end of the urbs! */
|
||||
dst[47] = pt->ar_bspstore;
|
||||
dst[48] = ar_rnat;
|
||||
unw_get_ar(info, UNW_AR_CCV, &dst[49]);
|
||||
unw_get_ar(info, UNW_AR_UNAT, &dst[50]);
|
||||
unw_get_ar(info, UNW_AR_FPSR, &dst[51]);
|
||||
dst[52] = pt->ar_pfs; /* UNW_AR_PFS is == to pt->cr_ifs for interrupt frames */
|
||||
unw_get_ar(info, UNW_AR_LC, &dst[53]);
|
||||
unw_get_ar(info, UNW_AR_EC, &dst[54]);
|
||||
unw_get_ar(info, UNW_AR_CSD, &dst[55]);
|
||||
unw_get_ar(info, UNW_AR_SSD, &dst[56]);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
|
||||
void
|
||||
do_dump_task_fpu (struct task_struct *task, struct unw_frame_info *info, void *arg)
|
||||
{
|
||||
elf_fpreg_t *dst = arg;
|
||||
int i;
|
||||
|
||||
memset(dst, 0, sizeof(elf_fpregset_t)); /* don't leak any "random" bits */
|
||||
|
||||
if (unw_unwind_to_user(info) < 0)
|
||||
return;
|
||||
|
||||
/* f0 is 0.0, f1 is 1.0 */
|
||||
|
||||
for (i = 2; i < 32; ++i)
|
||||
unw_get_fr(info, i, dst + i);
|
||||
|
||||
ia64_flush_fph(task);
|
||||
if ((task->thread.flags & IA64_THREAD_FPH_VALID) != 0)
|
||||
memcpy(dst + 32, task->thread.fph, 96*16);
|
||||
}
|
||||
|
||||
#endif /* !XEN */
|
||||
|
||||
void
|
||||
do_copy_regs (struct unw_frame_info *info, void *arg)
|
||||
{
|
||||
do_copy_task_regs(current, info, arg);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
|
||||
void
|
||||
do_dump_fpu (struct unw_frame_info *info, void *arg)
|
||||
{
|
||||
do_dump_task_fpu(current, info, arg);
|
||||
}
|
||||
|
||||
int
|
||||
dump_task_regs(struct task_struct *task, elf_gregset_t *regs)
|
||||
{
|
||||
struct unw_frame_info tcore_info;
|
||||
|
||||
if (current == task) {
|
||||
unw_init_running(do_copy_regs, regs);
|
||||
} else {
|
||||
memset(&tcore_info, 0, sizeof(tcore_info));
|
||||
unw_init_from_blocked_task(&tcore_info, task);
|
||||
do_copy_task_regs(task, &tcore_info, regs);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* !XEN */
|
||||
|
||||
void
|
||||
ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
|
||||
{
|
||||
unw_init_running(do_copy_regs, dst);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
|
||||
int
|
||||
dump_task_fpu (struct task_struct *task, elf_fpregset_t *dst)
|
||||
{
|
||||
struct unw_frame_info tcore_info;
|
||||
|
||||
if (current == task) {
|
||||
unw_init_running(do_dump_fpu, dst);
|
||||
} else {
|
||||
memset(&tcore_info, 0, sizeof(tcore_info));
|
||||
unw_init_from_blocked_task(&tcore_info, task);
|
||||
do_dump_task_fpu(task, &tcore_info, dst);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
|
||||
{
|
||||
unw_init_running(do_dump_fpu, dst);
|
||||
return 1; /* f0-f31 are always valid so we always return 1 */
|
||||
}
|
||||
|
||||
long
|
||||
sys_execve (char __user *filename, char __user * __user *argv, char __user * __user *envp,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
char *fname;
|
||||
int error;
|
||||
|
||||
fname = getname(filename);
|
||||
error = PTR_ERR(fname);
|
||||
if (IS_ERR(fname))
|
||||
goto out;
|
||||
error = do_execve(fname, argv, envp, regs);
|
||||
putname(fname);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
pid_t
|
||||
kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
extern void start_kernel_thread (void);
|
||||
unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
|
||||
struct {
|
||||
struct switch_stack sw;
|
||||
struct pt_regs pt;
|
||||
} regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
|
||||
regs.pt.r1 = helper_fptr[1]; /* set GP */
|
||||
regs.pt.r9 = (unsigned long) fn; /* 1st argument */
|
||||
regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
|
||||
/* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
|
||||
regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
|
||||
regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
|
||||
regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
|
||||
regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
|
||||
regs.sw.pr = (1 << PRED_KERNEL_STACK);
|
||||
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s.pt, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
/* This gets called from kernel_thread() via ia64_invoke_thread_helper(). */
|
||||
int
|
||||
kernel_thread_helper (int (*fn)(void *), void *arg)
|
||||
{
|
||||
#ifdef CONFIG_IA32_SUPPORT
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(current))) {
|
||||
/* A kernel thread is always a 64-bit process. */
|
||||
current->thread.map_base = DEFAULT_MAP_BASE;
|
||||
current->thread.task_size = DEFAULT_TASK_SIZE;
|
||||
ia64_set_kr(IA64_KR_IO_BASE, current->thread.old_iob);
|
||||
ia64_set_kr(IA64_KR_TSSD, current->thread.old_k1);
|
||||
}
|
||||
#endif
|
||||
return (*fn)(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush thread state. This is called when a thread does an execve().
|
||||
*/
|
||||
void
|
||||
flush_thread (void)
|
||||
{
|
||||
/*
|
||||
* Remove function-return probe instances associated with this task
|
||||
* and put them back on the free list. Do not insert an exit probe for
|
||||
* this function, it will be disabled by kprobe_flush_task if you do.
|
||||
*/
|
||||
kprobe_flush_task(current);
|
||||
|
||||
/* drop floating-point and debug-register state if it exists: */
|
||||
current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID);
|
||||
ia64_drop_fpu(current);
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(current)))
|
||||
ia32_drop_partial_page_list(current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up state associated with current thread. This is called when
|
||||
* the thread calls exit().
|
||||
*/
|
||||
void
|
||||
exit_thread (void)
|
||||
{
|
||||
|
||||
/*
|
||||
* Remove function-return probe instances associated with this task
|
||||
* and put them back on the free list. Do not insert an exit probe for
|
||||
* this function, it will be disabled by kprobe_flush_task if you do.
|
||||
*/
|
||||
kprobe_flush_task(current);
|
||||
|
||||
ia64_drop_fpu(current);
|
||||
#ifdef CONFIG_PERFMON
|
||||
/* if needed, stop monitoring and flush state to perfmon context */
|
||||
if (current->thread.pfm_context)
|
||||
pfm_exit_thread(current);
|
||||
|
||||
/* free debug register resources */
|
||||
if (current->thread.flags & IA64_THREAD_DBG_VALID)
|
||||
pfm_release_debug_registers(current);
|
||||
#endif
|
||||
if (IS_IA32_PROCESS(ia64_task_regs(current)))
|
||||
ia32_drop_partial_page_list(current);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_wchan (struct task_struct *p)
|
||||
{
|
||||
struct unw_frame_info info;
|
||||
unsigned long ip;
|
||||
int count = 0;
|
||||
|
||||
/*
|
||||
* Note: p may not be a blocked task (it could be current or
|
||||
* another process running on some other CPU. Rather than
|
||||
* trying to determine if p is really blocked, we just assume
|
||||
* it's blocked and rely on the unwind routines to fail
|
||||
* gracefully if the process wasn't really blocked after all.
|
||||
* --davidm 99/12/15
|
||||
*/
|
||||
unw_init_from_blocked_task(&info, p);
|
||||
do {
|
||||
if (unw_unwind(&info) < 0)
|
||||
return 0;
|
||||
unw_get_ip(&info, &ip);
|
||||
if (!in_sched_functions(ip))
|
||||
return ip;
|
||||
} while (count++ < 16);
|
||||
return 0;
|
||||
}
|
||||
#endif // !XEN
|
||||
|
||||
void
|
||||
cpu_halt (void)
|
||||
{
|
||||
pal_power_mgmt_info_u_t power_info[8];
|
||||
unsigned long min_power;
|
||||
int i, min_power_state;
|
||||
|
||||
if (ia64_pal_halt_info(power_info) != 0)
|
||||
return;
|
||||
|
||||
min_power_state = 0;
|
||||
min_power = power_info[0].pal_power_mgmt_info_s.power_consumption;
|
||||
for (i = 1; i < 8; ++i)
|
||||
if (power_info[i].pal_power_mgmt_info_s.im
|
||||
&& power_info[i].pal_power_mgmt_info_s.power_consumption < min_power) {
|
||||
min_power = power_info[i].pal_power_mgmt_info_s.power_consumption;
|
||||
min_power_state = i;
|
||||
}
|
||||
|
||||
while (1)
|
||||
ia64_pal_halt(min_power_state);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
void
|
||||
machine_restart (char *restart_cmd)
|
||||
{
|
||||
(*efi.reset_system)(EFI_RESET_WARM, 0, 0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
machine_halt (void)
|
||||
{
|
||||
cpu_halt();
|
||||
}
|
||||
|
||||
void
|
||||
machine_power_off (void)
|
||||
{
|
||||
if (pm_power_off)
|
||||
pm_power_off();
|
||||
machine_halt();
|
||||
}
|
||||
#endif // !XEN
|
||||
@@ -1,386 +0,0 @@
|
||||
/*
|
||||
* System Abstraction Layer (SAL) interface routines.
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2001, 2003 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* Copyright (C) 1999 VA Linux Systems
|
||||
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#ifdef XEN
|
||||
#include <linux/smp.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <xen/lib.h>
|
||||
#endif
|
||||
#include <asm/delay.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/pal.h>
|
||||
|
||||
__cacheline_aligned DEFINE_SPINLOCK(sal_lock);
|
||||
unsigned long sal_platform_features;
|
||||
|
||||
unsigned short sal_revision;
|
||||
unsigned short sal_version;
|
||||
|
||||
#define SAL_MAJOR(x) ((x) >> 8)
|
||||
#define SAL_MINOR(x) ((x) & 0xff)
|
||||
|
||||
static struct {
|
||||
void *addr; /* function entry point */
|
||||
void *gpval; /* gp value to use */
|
||||
} pdesc;
|
||||
|
||||
static long
|
||||
default_handler (void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ia64_sal_handler ia64_sal = (ia64_sal_handler) default_handler;
|
||||
ia64_sal_desc_ptc_t *ia64_ptc_domain_info;
|
||||
|
||||
const char *
|
||||
ia64_sal_strerror (long status)
|
||||
{
|
||||
const char *str;
|
||||
switch (status) {
|
||||
case 0: str = "Call completed without error"; break;
|
||||
case 1: str = "Effect a warm boot of the system to complete "
|
||||
"the update"; break;
|
||||
case -1: str = "Not implemented"; break;
|
||||
case -2: str = "Invalid argument"; break;
|
||||
case -3: str = "Call completed with error"; break;
|
||||
case -4: str = "Virtual address not registered"; break;
|
||||
case -5: str = "No information available"; break;
|
||||
case -6: str = "Insufficient space to add the entry"; break;
|
||||
case -7: str = "Invalid entry_addr value"; break;
|
||||
case -8: str = "Invalid interrupt vector"; break;
|
||||
case -9: str = "Requested memory not available"; break;
|
||||
case -10: str = "Unable to write to the NVM device"; break;
|
||||
case -11: str = "Invalid partition type specified"; break;
|
||||
case -12: str = "Invalid NVM_Object id specified"; break;
|
||||
case -13: str = "NVM_Object already has the maximum number "
|
||||
"of partitions"; break;
|
||||
case -14: str = "Insufficient space in partition for the "
|
||||
"requested write sub-function"; break;
|
||||
case -15: str = "Insufficient data buffer space for the "
|
||||
"requested read record sub-function"; break;
|
||||
case -16: str = "Scratch buffer required for the write/delete "
|
||||
"sub-function"; break;
|
||||
case -17: str = "Insufficient space in the NVM_Object for the "
|
||||
"requested create sub-function"; break;
|
||||
case -18: str = "Invalid value specified in the partition_rec "
|
||||
"argument"; break;
|
||||
case -19: str = "Record oriented I/O not supported for this "
|
||||
"partition"; break;
|
||||
case -20: str = "Bad format of record to be written or "
|
||||
"required keyword variable not "
|
||||
"specified"; break;
|
||||
default: str = "Unknown SAL status code"; break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void __init
|
||||
ia64_sal_handler_init (void *entry_point, void *gpval)
|
||||
{
|
||||
/* fill in the SAL procedure descriptor and point ia64_sal to it: */
|
||||
pdesc.addr = entry_point;
|
||||
pdesc.gpval = gpval;
|
||||
ia64_sal = (ia64_sal_handler) &pdesc;
|
||||
}
|
||||
|
||||
static void __init
|
||||
check_versions (struct ia64_sal_systab *systab)
|
||||
{
|
||||
sal_revision = (systab->sal_rev_major << 8) | systab->sal_rev_minor;
|
||||
sal_version = (systab->sal_b_rev_major << 8) | systab->sal_b_rev_minor;
|
||||
|
||||
/* Check for broken firmware */
|
||||
if ((sal_revision == SAL_VERSION_CODE(49, 29))
|
||||
&& (sal_version == SAL_VERSION_CODE(49, 29)))
|
||||
{
|
||||
/*
|
||||
* Old firmware for zx2000 prototypes have this weird version number,
|
||||
* reset it to something sane.
|
||||
*/
|
||||
sal_revision = SAL_VERSION_CODE(2, 8);
|
||||
sal_version = SAL_VERSION_CODE(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init
|
||||
sal_desc_entry_point (void *p)
|
||||
{
|
||||
struct ia64_sal_desc_entry_point *ep = p;
|
||||
ia64_pal_handler_init(__va_efi(ep->pal_proc));
|
||||
ia64_sal_handler_init(__va_efi(ep->sal_proc), __va_efi(ep->gp));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void __init
|
||||
set_smp_redirect (int flag)
|
||||
{
|
||||
#if defined(CONFIG_HOTPLUG_CPU) && !defined(XEN)
|
||||
if (no_int_routing)
|
||||
smp_int_redirect &= ~flag;
|
||||
else
|
||||
smp_int_redirect |= flag;
|
||||
#else
|
||||
/*
|
||||
* For CPU Hotplug we dont want to do any chipset supported
|
||||
* interrupt redirection. The reason is this would require that
|
||||
* All interrupts be stopped and hard bind the irq to a cpu.
|
||||
* Later when the interrupt is fired we need to set the redir hint
|
||||
* on again in the vector. This is combersome for something that the
|
||||
* user mode irq balancer will solve anyways.
|
||||
*/
|
||||
no_int_routing=1;
|
||||
smp_int_redirect &= ~flag;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#define set_smp_redirect(flag) do { } while (0)
|
||||
#endif
|
||||
|
||||
static void __init
|
||||
sal_desc_platform_feature (void *p)
|
||||
{
|
||||
struct ia64_sal_desc_platform_feature *pf = p;
|
||||
sal_platform_features = pf->feature_mask;
|
||||
|
||||
printk(KERN_INFO "SAL Platform features:");
|
||||
if (!sal_platform_features) {
|
||||
printk(" None\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_BUS_LOCK)
|
||||
printk(" BusLock");
|
||||
if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT) {
|
||||
printk(" IRQ_Redirection");
|
||||
set_smp_redirect(SMP_IRQ_REDIRECTION);
|
||||
}
|
||||
if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT) {
|
||||
printk(" IPI_Redirection");
|
||||
set_smp_redirect(SMP_IPI_REDIRECTION);
|
||||
}
|
||||
if (sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)
|
||||
printk(" ITC_Drift");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void __init
|
||||
sal_desc_ap_wakeup (void *p)
|
||||
{
|
||||
struct ia64_sal_desc_ap_wakeup *ap = p;
|
||||
|
||||
switch (ap->mechanism) {
|
||||
case IA64_SAL_AP_EXTERNAL_INT:
|
||||
ap_wakeup_vector = ap->vector;
|
||||
printk(KERN_INFO "SAL: AP wakeup using external interrupt "
|
||||
"vector 0x%lx\n", ap_wakeup_vector);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "SAL: AP wakeup mechanism unsupported!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init
|
||||
chk_nointroute_opt(void)
|
||||
{
|
||||
char *cp;
|
||||
extern char saved_command_line[];
|
||||
|
||||
for (cp = saved_command_line; *cp; ) {
|
||||
if (memcmp(cp, "nointroute", 10) == 0) {
|
||||
no_int_routing = 1;
|
||||
printk ("no_int_routing on\n");
|
||||
break;
|
||||
} else {
|
||||
while (*cp != ' ' && *cp)
|
||||
++cp;
|
||||
while (*cp == ' ')
|
||||
++cp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
static void __init sal_desc_ap_wakeup(void *p) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HP rx5670 firmware polls for interrupts during SAL_CACHE_FLUSH by reading
|
||||
* cr.ivr, but it never writes cr.eoi. This leaves any interrupt marked as
|
||||
* "in-service" and masks other interrupts of equal or lower priority.
|
||||
*
|
||||
* HP internal defect reports: F1859, F2775, F3031.
|
||||
*/
|
||||
static int sal_cache_flush_drops_interrupts;
|
||||
|
||||
static void __init
|
||||
check_sal_cache_flush (void)
|
||||
{
|
||||
unsigned long flags, itv;
|
||||
int cpu;
|
||||
u64 vector;
|
||||
|
||||
cpu = get_cpu();
|
||||
local_irq_save(flags);
|
||||
|
||||
/*
|
||||
* Schedule a timer interrupt, wait until it's reported, and see if
|
||||
* SAL_CACHE_FLUSH drops it.
|
||||
*/
|
||||
itv = ia64_get_itv();
|
||||
BUG_ON((itv & (1 << 16)) == 0);
|
||||
|
||||
ia64_set_itv(IA64_TIMER_VECTOR);
|
||||
ia64_set_itm(ia64_get_itc() + 1000);
|
||||
|
||||
while (!ia64_get_irr(IA64_TIMER_VECTOR))
|
||||
cpu_relax();
|
||||
|
||||
ia64_sal_cache_flush(3);
|
||||
|
||||
if (ia64_get_irr(IA64_TIMER_VECTOR)) {
|
||||
vector = ia64_get_ivr();
|
||||
ia64_eoi();
|
||||
} else {
|
||||
sal_cache_flush_drops_interrupts = 1;
|
||||
printk(KERN_ERR "SAL: SAL_CACHE_FLUSH drops interrupts; "
|
||||
"PAL_CACHE_FLUSH will be used instead\n");
|
||||
ia64_eoi();
|
||||
}
|
||||
|
||||
ia64_set_itv(itv);
|
||||
local_irq_restore(flags);
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
s64
|
||||
ia64_sal_cache_flush (u64 cache_type)
|
||||
{
|
||||
struct ia64_sal_retval isrv;
|
||||
|
||||
if (sal_cache_flush_drops_interrupts) {
|
||||
unsigned long flags;
|
||||
u64 progress;
|
||||
s64 rc;
|
||||
|
||||
progress = 0;
|
||||
local_irq_save(flags);
|
||||
rc = ia64_pal_cache_flush(cache_type,
|
||||
PAL_CACHE_FLUSH_INVALIDATE, &progress, NULL);
|
||||
local_irq_restore(flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
SAL_CALL(isrv, SAL_CACHE_FLUSH, cache_type, 0, 0, 0, 0, 0, 0);
|
||||
return isrv.status;
|
||||
}
|
||||
|
||||
void __init
|
||||
ia64_sal_init (struct ia64_sal_systab *systab)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (!systab) {
|
||||
printk(KERN_WARNING "Hmm, no SAL System Table.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef XEN /* warning cleanup */
|
||||
if (strncmp((char *)systab->signature, "SST_", 4) != 0)
|
||||
#else
|
||||
if (strncmp(systab->signature, "SST_", 4) != 0)
|
||||
#endif
|
||||
|
||||
printk(KERN_ERR "bad signature in system table!");
|
||||
|
||||
check_versions(systab);
|
||||
#ifdef CONFIG_SMP
|
||||
chk_nointroute_opt();
|
||||
#endif
|
||||
|
||||
/* revisions are coded in BCD, so %x does the job for us */
|
||||
printk(KERN_INFO "SAL %x.%x: %.32s %.32s%sversion %x.%x\n",
|
||||
SAL_MAJOR(sal_revision), SAL_MINOR(sal_revision),
|
||||
systab->oem_id, systab->product_id,
|
||||
systab->product_id[0] ? " " : "",
|
||||
SAL_MAJOR(sal_version), SAL_MINOR(sal_version));
|
||||
|
||||
p = (char *) (systab + 1);
|
||||
for (i = 0; i < systab->entry_count; i++) {
|
||||
/*
|
||||
* The first byte of each entry type contains the type
|
||||
* descriptor.
|
||||
*/
|
||||
switch (*p) {
|
||||
case SAL_DESC_ENTRY_POINT:
|
||||
sal_desc_entry_point(p);
|
||||
break;
|
||||
case SAL_DESC_PLATFORM_FEATURE:
|
||||
sal_desc_platform_feature(p);
|
||||
break;
|
||||
case SAL_DESC_PTC:
|
||||
ia64_ptc_domain_info = (ia64_sal_desc_ptc_t *)p;
|
||||
break;
|
||||
case SAL_DESC_AP_WAKEUP:
|
||||
sal_desc_ap_wakeup(p);
|
||||
break;
|
||||
}
|
||||
p += SAL_DESC_SIZE(*p);
|
||||
}
|
||||
|
||||
check_sal_cache_flush();
|
||||
}
|
||||
|
||||
int
|
||||
ia64_sal_oemcall(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1,
|
||||
u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7)
|
||||
{
|
||||
if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX)
|
||||
return -1;
|
||||
SAL_CALL(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ia64_sal_oemcall);
|
||||
|
||||
int
|
||||
ia64_sal_oemcall_nolock(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1,
|
||||
u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6,
|
||||
u64 arg7)
|
||||
{
|
||||
if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX)
|
||||
return -1;
|
||||
SAL_CALL_NOLOCK(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6,
|
||||
arg7);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ia64_sal_oemcall_nolock);
|
||||
|
||||
int
|
||||
ia64_sal_oemcall_reentrant(struct ia64_sal_retval *isrvp, u64 oemfunc,
|
||||
u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5,
|
||||
u64 arg6, u64 arg7)
|
||||
{
|
||||
if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX)
|
||||
return -1;
|
||||
SAL_CALL_REENTRANT(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6,
|
||||
arg7);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ia64_sal_oemcall_reentrant);
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,495 +0,0 @@
|
||||
/*
|
||||
* SMP Support
|
||||
*
|
||||
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
|
||||
* Copyright (C) 1999, 2001, 2003 David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* Lots of stuff stolen from arch/alpha/kernel/smp.c
|
||||
*
|
||||
* 01/05/16 Rohit Seth <rohit.seth@intel.com> IA64-SMP functions. Reorganized
|
||||
* the existing code (on the lines of x86 port).
|
||||
* 00/09/11 David Mosberger <davidm@hpl.hp.com> Do loops_per_jiffy
|
||||
* calibration on each CPU.
|
||||
* 00/08/23 Asit Mallick <asit.k.mallick@intel.com> fixed logical processor id
|
||||
* 00/03/31 Rohit Seth <rohit.seth@intel.com> Fixes for Bootstrap Processor
|
||||
* & cpu_online_map now gets done here (instead of setup.c)
|
||||
* 99/10/05 davidm Update to bring it in sync with new command-line processing
|
||||
* scheme.
|
||||
* 10/13/00 Goutham Rao <goutham.rao@intel.com> Updated smp_call_function and
|
||||
* smp_call_function_single to resend IPI on timeouts
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/mca.h>
|
||||
#ifdef XEN
|
||||
#include <xen/errno.h>
|
||||
#include <asm/vhpt.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
//#if CONFIG_SMP || IA64
|
||||
#if CONFIG_SMP
|
||||
//Huh? This seems to be used on ia64 even if !CONFIG_SMP
|
||||
void smp_send_event_check_mask(const cpumask_t *mask)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
/* Not for me. */
|
||||
if (cpumask_subset(mask, cpumask_of(smp_processor_id())))
|
||||
return;
|
||||
|
||||
//printf("smp_send_event_check_mask called\n");
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; ++cpu)
|
||||
if (cpumask_test_cpu(cpu, mask) && cpu != smp_processor_id())
|
||||
platform_send_ipi(cpu, IA64_IPI_RESCHEDULE, IA64_IPI_DM_INT, 0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP /* ifdef XEN */
|
||||
|
||||
/*
|
||||
* Structure and data for smp_call_function(). This is designed to minimise static memory
|
||||
* requirements. It also looks cleaner.
|
||||
*/
|
||||
static __cacheline_aligned DEFINE_SPINLOCK(call_lock);
|
||||
|
||||
struct call_data_struct {
|
||||
void (*func) (void *info);
|
||||
void *info;
|
||||
long wait;
|
||||
atomic_t started;
|
||||
atomic_t finished;
|
||||
};
|
||||
|
||||
static volatile struct call_data_struct *call_data;
|
||||
|
||||
#define IPI_CALL_FUNC 0
|
||||
#define IPI_CPU_STOP 1
|
||||
#define IPI_STATE_DUMP 2
|
||||
|
||||
/* This needs to be cacheline aligned because it is written to by *other* CPUs. */
|
||||
static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned;
|
||||
|
||||
extern void cpu_halt (void);
|
||||
|
||||
#ifdef XEN
|
||||
/* work around for spinlock irq check. */
|
||||
void
|
||||
lock_ipi_calllock(unsigned long *flags)
|
||||
{
|
||||
spin_lock_irqsave(&call_lock, *flags);
|
||||
}
|
||||
|
||||
void
|
||||
unlock_ipi_calllock(unsigned long flags)
|
||||
{
|
||||
spin_unlock_irqrestore(&call_lock, flags);
|
||||
}
|
||||
#else
|
||||
void
|
||||
lock_ipi_calllock(void)
|
||||
{
|
||||
spin_lock_irq(&call_lock);
|
||||
}
|
||||
|
||||
void
|
||||
unlock_ipi_calllock(void)
|
||||
{
|
||||
spin_unlock_irq(&call_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
stop_this_cpu (void)
|
||||
{
|
||||
/*
|
||||
* Remove this CPU:
|
||||
*/
|
||||
cpumask_clear_cpu(smp_processor_id(), &cpu_online_map);
|
||||
max_xtp();
|
||||
local_irq_disable();
|
||||
cpu_halt();
|
||||
}
|
||||
|
||||
void
|
||||
cpu_die(void)
|
||||
{
|
||||
max_xtp();
|
||||
local_irq_disable();
|
||||
cpu_halt();
|
||||
/* Should never be here */
|
||||
BUG();
|
||||
for (;;);
|
||||
}
|
||||
|
||||
irqreturn_t
|
||||
handle_IPI (int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
int this_cpu = get_cpu();
|
||||
unsigned long *pending_ipis = &__ia64_per_cpu_var(ipi_operation);
|
||||
unsigned long ops;
|
||||
|
||||
#ifdef XEN
|
||||
perfc_incr(ipis);
|
||||
#endif
|
||||
mb(); /* Order interrupt and bit testing. */
|
||||
while ((ops = xchg(pending_ipis, 0)) != 0) {
|
||||
mb(); /* Order bit clearing and data access. */
|
||||
do {
|
||||
unsigned long which;
|
||||
|
||||
which = ffz(~ops);
|
||||
ops &= ~(1 << which);
|
||||
|
||||
switch (which) {
|
||||
case IPI_CALL_FUNC:
|
||||
{
|
||||
struct call_data_struct *data;
|
||||
void (*func)(void *info);
|
||||
void *info;
|
||||
int wait;
|
||||
|
||||
/* release the 'pointer lock' */
|
||||
data = (struct call_data_struct *) call_data;
|
||||
func = data->func;
|
||||
info = data->info;
|
||||
wait = data->wait;
|
||||
|
||||
mb();
|
||||
atomic_inc(&data->started);
|
||||
/*
|
||||
* At this point the structure may be gone unless
|
||||
* wait is true.
|
||||
*/
|
||||
(*func)(info);
|
||||
|
||||
/* Notify the sending CPU that the task is done. */
|
||||
mb();
|
||||
if (wait)
|
||||
atomic_inc(&data->finished);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPI_CPU_STOP:
|
||||
stop_this_cpu();
|
||||
break;
|
||||
|
||||
case IPI_STATE_DUMP:
|
||||
dump_execstate(regs);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
|
||||
break;
|
||||
}
|
||||
} while (ops);
|
||||
mb(); /* Order data access and bit testing. */
|
||||
}
|
||||
put_cpu();
|
||||
#ifndef XEN
|
||||
return IRQ_HANDLED;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with preeemption disabled.
|
||||
*/
|
||||
static inline void
|
||||
send_IPI_single (int dest_cpu, int op)
|
||||
{
|
||||
set_bit(op, &per_cpu(ipi_operation, dest_cpu));
|
||||
platform_send_ipi(dest_cpu, IA64_IPI_VECTOR, IA64_IPI_DM_INT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with preeemption disabled.
|
||||
*/
|
||||
static inline void
|
||||
send_IPI_allbutself (int op)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NR_CPUS; i++) {
|
||||
if (cpu_online(i) && i != smp_processor_id())
|
||||
send_IPI_single(i, op);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with preeemption disabled.
|
||||
*/
|
||||
static inline void
|
||||
send_IPI_all (int op)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_CPUS; i++)
|
||||
if (cpu_online(i))
|
||||
send_IPI_single(i, op);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with preeemption disabled.
|
||||
*/
|
||||
static inline void
|
||||
send_IPI_self (int op)
|
||||
{
|
||||
send_IPI_single(smp_processor_id(), op);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* Called with preeemption disabled.
|
||||
*/
|
||||
void
|
||||
smp_send_reschedule (int cpu)
|
||||
{
|
||||
platform_send_ipi(cpu, IA64_IPI_RESCHEDULE, IA64_IPI_DM_INT, 0);
|
||||
}
|
||||
|
||||
void
|
||||
smp_flush_tlb_all (void)
|
||||
{
|
||||
on_each_cpu((void (*)(void *))local_flush_tlb_all, NULL, 1);
|
||||
}
|
||||
|
||||
void
|
||||
smp_flush_tlb_mm (struct mm_struct *mm)
|
||||
{
|
||||
preempt_disable();
|
||||
/* this happens for the common case of a single-threaded fork(): */
|
||||
if (likely(mm == current->active_mm && atomic_read(&mm->mm_users) == 1))
|
||||
{
|
||||
local_finish_flush_tlb_mm(mm);
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
/*
|
||||
* We could optimize this further by using mm->cpu_vm_mask to track which CPUs
|
||||
* have been running in the address space. It's not clear that this is worth the
|
||||
* trouble though: to avoid races, we have to raise the IPI on the target CPU
|
||||
* anyhow, and once a CPU is interrupted, the cost of local_flush_tlb_all() is
|
||||
* rather trivial.
|
||||
*/
|
||||
on_each_cpu((void (*)(void *))local_finish_flush_tlb_mm, mm, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Run a function on another CPU
|
||||
* <func> The function to run. This must be fast and non-blocking.
|
||||
* <info> An arbitrary pointer to pass to the function.
|
||||
* <nonatomic> Currently unused.
|
||||
* <wait> If true, wait until function has completed on other CPUs.
|
||||
* [RETURNS] 0 on success, else a negative status code.
|
||||
*
|
||||
* Does not return until the remote CPU is nearly ready to execute <func>
|
||||
* or is or has executed.
|
||||
*/
|
||||
|
||||
int
|
||||
smp_call_function_single (int cpuid, void (*func) (void *info), void *info,
|
||||
int wait)
|
||||
{
|
||||
struct call_data_struct data;
|
||||
int cpus = 1;
|
||||
int me = get_cpu(); /* prevent preemption and reschedule on another processor */
|
||||
|
||||
if (cpuid == me) {
|
||||
printk(KERN_INFO "%s: trying to call self\n", __FUNCTION__);
|
||||
put_cpu();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
atomic_set(&data.started, 0);
|
||||
data.wait = wait;
|
||||
if (wait)
|
||||
atomic_set(&data.finished, 0);
|
||||
|
||||
#ifdef XEN
|
||||
spin_lock(&call_lock);
|
||||
#else
|
||||
spin_lock_bh(&call_lock);
|
||||
#endif
|
||||
|
||||
call_data = &data;
|
||||
mb(); /* ensure store to call_data precedes setting of IPI_CALL_FUNC */
|
||||
send_IPI_single(cpuid, IPI_CALL_FUNC);
|
||||
|
||||
/* Wait for response */
|
||||
while (atomic_read(&data.started) != cpus)
|
||||
cpu_relax();
|
||||
|
||||
if (wait)
|
||||
while (atomic_read(&data.finished) != cpus)
|
||||
cpu_relax();
|
||||
call_data = NULL;
|
||||
|
||||
#ifdef XEN
|
||||
spin_unlock(&call_lock);
|
||||
#else
|
||||
spin_unlock_bh(&call_lock);
|
||||
#endif
|
||||
put_cpu();
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function_single);
|
||||
|
||||
/*
|
||||
* this function sends a 'generic call function' IPI to all other CPUs
|
||||
* in the system.
|
||||
*/
|
||||
|
||||
/*
|
||||
* [SUMMARY] Run a function on all other CPUs.
|
||||
* <func> The function to run. This must be fast and non-blocking.
|
||||
* <info> An arbitrary pointer to pass to the function.
|
||||
* <wait> If true, wait (atomically) until function has completed on other CPUs.
|
||||
* [RETURNS] 0 on success, else a negative status code.
|
||||
*
|
||||
* Does not return until remote CPUs are nearly ready to execute <func> or are or have
|
||||
* executed.
|
||||
*
|
||||
* You must not call this function with disabled interrupts or from a
|
||||
* hardware interrupt handler or from a bottom half handler.
|
||||
*/
|
||||
void
|
||||
smp_call_function (void (*func) (void *info), void *info, int wait)
|
||||
{
|
||||
struct call_data_struct data;
|
||||
int cpus = num_online_cpus()-1;
|
||||
|
||||
if (!cpus)
|
||||
return;
|
||||
|
||||
/* Can deadlock when called with interrupts disabled */
|
||||
#ifdef XEN
|
||||
if (irqs_disabled()) {
|
||||
printk("smp_call_function called with interrupts disabled...");
|
||||
printk("enabling interrupts\n");
|
||||
local_irq_enable();
|
||||
}
|
||||
#else
|
||||
WARN_ON(irqs_disabled());
|
||||
#endif
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
atomic_set(&data.started, 0);
|
||||
data.wait = wait;
|
||||
if (wait)
|
||||
atomic_set(&data.finished, 0);
|
||||
|
||||
spin_lock(&call_lock);
|
||||
#if 0 //def XEN
|
||||
printk("smp_call_function: %d lock\n", smp_processor_id ());
|
||||
#endif
|
||||
|
||||
call_data = &data;
|
||||
mb(); /* ensure store to call_data precedes setting of IPI_CALL_FUNC */
|
||||
send_IPI_allbutself(IPI_CALL_FUNC);
|
||||
|
||||
/* Wait for response */
|
||||
while (atomic_read(&data.started) != cpus)
|
||||
cpu_relax();
|
||||
|
||||
if (wait)
|
||||
while (atomic_read(&data.finished) != cpus)
|
||||
cpu_relax();
|
||||
call_data = NULL;
|
||||
|
||||
spin_unlock(&call_lock);
|
||||
#if 0 //def XEN
|
||||
printk("smp_call_function: DONE WITH spin_unlock, returning \n");
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function);
|
||||
|
||||
#ifdef XEN
|
||||
void
|
||||
on_selected_cpus(const cpumask_t *selected, void (*func) (void *info),
|
||||
void *info, int wait)
|
||||
{
|
||||
struct call_data_struct data;
|
||||
unsigned int cpu, nr_cpus = cpumask_weight(selected);
|
||||
|
||||
ASSERT(local_irq_is_enabled());
|
||||
|
||||
if (!nr_cpus)
|
||||
return;
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
data.wait = wait;
|
||||
atomic_set(&data.started, 0);
|
||||
atomic_set(&data.finished, 0);
|
||||
|
||||
spin_lock(&call_lock);
|
||||
|
||||
call_data = &data;
|
||||
wmb();
|
||||
|
||||
for_each_cpu(cpu, selected)
|
||||
send_IPI_single(cpu, IPI_CALL_FUNC);
|
||||
|
||||
while (atomic_read(wait ? &data.finished : &data.started) != nr_cpus)
|
||||
cpu_relax();
|
||||
|
||||
spin_unlock(&call_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* this function calls the 'stop' function on all other CPUs in the system.
|
||||
*/
|
||||
void
|
||||
smp_send_stop (void)
|
||||
{
|
||||
send_IPI_allbutself(IPI_CPU_STOP);
|
||||
}
|
||||
|
||||
void
|
||||
smp_send_state_dump (unsigned int cpu)
|
||||
{
|
||||
send_IPI_single(cpu, IPI_STATE_DUMP);
|
||||
}
|
||||
|
||||
int __init
|
||||
setup_profiling_timer (unsigned int multiplier)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_SMP ifdef XEN */
|
||||
@@ -1,977 +0,0 @@
|
||||
/*
|
||||
* SMP boot-related support
|
||||
*
|
||||
* Copyright (C) 1998-2003, 2005 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* Copyright (C) 2001, 2004-2005 Intel Corp
|
||||
* Rohit Seth <rohit.seth@intel.com>
|
||||
* Suresh Siddha <suresh.b.siddha@intel.com>
|
||||
* Gordon Jin <gordon.jin@intel.com>
|
||||
* Ashok Raj <ashok.raj@intel.com>
|
||||
*
|
||||
* 01/05/16 Rohit Seth <rohit.seth@intel.com> Moved SMP booting functions from smp.c to here.
|
||||
* 01/04/27 David Mosberger <davidm@hpl.hp.com> Added ITC synching code.
|
||||
* 02/07/31 David Mosberger <davidm@hpl.hp.com> Switch over to hotplug-CPU boot-sequence.
|
||||
* smp_boot_cpus()/smp_commence() is replaced by
|
||||
* smp_prepare_cpus()/__cpu_up()/smp_cpus_done().
|
||||
* 04/06/21 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
|
||||
* 04/12/26 Jin Gordon <gordon.jin@intel.com>
|
||||
* 04/12/26 Rohit Seth <rohit.seth@intel.com>
|
||||
* Add multi-threading and multi-core detection
|
||||
* 05/01/30 Suresh Siddha <suresh.b.siddha@intel.com>
|
||||
* Setup cpu_sibling_map and cpu_core_map
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/notifier.h> /* hg add me */
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/ia32.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/mca.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifdef XEN
|
||||
#include <xen/domain.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/vmx.h>
|
||||
#ifndef CONFIG_SMP
|
||||
cpumask_t cpu_online_map = CPU_MASK_CPU0;
|
||||
EXPORT_SYMBOL(cpu_online_map);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP /* ifdef XEN */
|
||||
|
||||
#define SMP_DEBUG 0
|
||||
|
||||
#if SMP_DEBUG
|
||||
#define Dprintk(x...) printk(x)
|
||||
#else
|
||||
#define Dprintk(x...)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
/*
|
||||
* Store all idle threads, this can be reused instead of creating
|
||||
* a new thread. Also avoids complicated thread destroy functionality
|
||||
* for idle threads.
|
||||
*/
|
||||
struct task_struct *idle_thread_array[NR_CPUS];
|
||||
|
||||
/*
|
||||
* Global array allocated for NR_CPUS at boot time
|
||||
*/
|
||||
struct sal_to_os_boot sal_boot_rendez_state[NR_CPUS];
|
||||
|
||||
/*
|
||||
* start_ap in head.S uses this to store current booting cpu
|
||||
* info.
|
||||
*/
|
||||
struct sal_to_os_boot *sal_state_for_booting_cpu = &sal_boot_rendez_state[0];
|
||||
|
||||
#define set_brendez_area(x) (sal_state_for_booting_cpu = &sal_boot_rendez_state[(x)]);
|
||||
|
||||
#define get_idle_for_cpu(x) (idle_thread_array[(x)])
|
||||
#define set_idle_for_cpu(x,p) (idle_thread_array[(x)] = (p))
|
||||
|
||||
#else
|
||||
|
||||
#define get_idle_for_cpu(x) (NULL)
|
||||
#define set_idle_for_cpu(x,p)
|
||||
#define set_brendez_area(x)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* ITC synchronization related stuff:
|
||||
*/
|
||||
#define MASTER 0
|
||||
#define SLAVE (SMP_CACHE_BYTES/8)
|
||||
|
||||
#define NUM_ROUNDS 64 /* magic value */
|
||||
#define NUM_ITERS 5 /* likewise */
|
||||
|
||||
static DEFINE_SPINLOCK(itc_sync_lock);
|
||||
static volatile unsigned long go[SLAVE + 1];
|
||||
|
||||
#define DEBUG_ITC_SYNC 0
|
||||
|
||||
extern void __devinit calibrate_delay (void);
|
||||
extern void start_ap (void);
|
||||
extern unsigned long ia64_iobase;
|
||||
|
||||
task_t *task_for_booting_cpu;
|
||||
|
||||
/*
|
||||
* State for each CPU
|
||||
*/
|
||||
DEFINE_PER_CPU(int, cpu_state);
|
||||
|
||||
/* Bitmasks of currently online, and possible CPUs */
|
||||
cpumask_t cpu_online_map;
|
||||
EXPORT_SYMBOL(cpu_online_map);
|
||||
cpumask_t cpu_possible_map;
|
||||
EXPORT_SYMBOL(cpu_possible_map);
|
||||
|
||||
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_mask);
|
||||
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_mask);
|
||||
int smp_num_siblings = 1;
|
||||
int smp_num_cpucores = 1;
|
||||
|
||||
/* which logical CPU number maps to which CPU (physical APIC ID) */
|
||||
volatile int ia64_cpu_to_sapicid[NR_CPUS];
|
||||
EXPORT_SYMBOL(ia64_cpu_to_sapicid);
|
||||
|
||||
static volatile cpumask_t cpu_callin_map;
|
||||
|
||||
struct smp_boot_data smp_boot_data __initdata;
|
||||
|
||||
unsigned long ap_wakeup_vector = -1; /* External Int use to wakeup APs */
|
||||
|
||||
char __initdata no_int_routing;
|
||||
|
||||
unsigned char smp_int_redirect; /* are INT and IPI redirectable by the chipset? */
|
||||
|
||||
static int __init
|
||||
nointroute (char *str)
|
||||
{
|
||||
no_int_routing = 1;
|
||||
printk ("no_int_routing on\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("nointroute", nointroute);
|
||||
|
||||
static void fix_b0_for_bsp(void)
|
||||
{
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
int cpuid;
|
||||
static int fix_bsp_b0 = 1;
|
||||
|
||||
cpuid = smp_processor_id();
|
||||
|
||||
/*
|
||||
* Cache the b0 value on the first AP that comes up
|
||||
*/
|
||||
if (!(fix_bsp_b0 && cpuid))
|
||||
return;
|
||||
|
||||
sal_boot_rendez_state[0].br[0] = sal_boot_rendez_state[cpuid].br[0];
|
||||
printk ("Fixed BSP b0 value from CPU %d\n", cpuid);
|
||||
|
||||
fix_bsp_b0 = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sync_master (void *arg)
|
||||
{
|
||||
unsigned long flags, i;
|
||||
|
||||
go[MASTER] = 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
{
|
||||
for (i = 0; i < NUM_ROUNDS*NUM_ITERS; ++i) {
|
||||
while (!go[MASTER])
|
||||
cpu_relax();
|
||||
go[MASTER] = 0;
|
||||
go[SLAVE] = ia64_get_itc();
|
||||
}
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of cycles by which our itc differs from the itc on the master
|
||||
* (time-keeper) CPU. A positive number indicates our itc is ahead of the master,
|
||||
* negative that it is behind.
|
||||
*/
|
||||
static inline long
|
||||
#ifdef XEN /* warning cleanup */
|
||||
get_delta (unsigned long *rt, unsigned long *master)
|
||||
#else
|
||||
get_delta (long *rt, long *master)
|
||||
#endif
|
||||
{
|
||||
unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
|
||||
unsigned long tcenter, t0, t1, tm;
|
||||
long i;
|
||||
|
||||
for (i = 0; i < NUM_ITERS; ++i) {
|
||||
t0 = ia64_get_itc();
|
||||
go[MASTER] = 1;
|
||||
while (!(tm = go[SLAVE]))
|
||||
cpu_relax();
|
||||
go[SLAVE] = 0;
|
||||
t1 = ia64_get_itc();
|
||||
|
||||
if (t1 - t0 < best_t1 - best_t0)
|
||||
best_t0 = t0, best_t1 = t1, best_tm = tm;
|
||||
}
|
||||
|
||||
*rt = best_t1 - best_t0;
|
||||
*master = best_tm - best_t0;
|
||||
|
||||
/* average best_t0 and best_t1 without overflow: */
|
||||
tcenter = (best_t0/2 + best_t1/2);
|
||||
if (best_t0 % 2 + best_t1 % 2 == 2)
|
||||
++tcenter;
|
||||
return tcenter - best_tm;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronize ar.itc of the current (slave) CPU with the ar.itc of the MASTER CPU
|
||||
* (normally the time-keeper CPU). We use a closed loop to eliminate the possibility of
|
||||
* unaccounted-for errors (such as getting a machine check in the middle of a calibration
|
||||
* step). The basic idea is for the slave to ask the master what itc value it has and to
|
||||
* read its own itc before and after the master responds. Each iteration gives us three
|
||||
* timestamps:
|
||||
*
|
||||
* slave master
|
||||
*
|
||||
* t0 ---\
|
||||
* ---\
|
||||
* --->
|
||||
* tm
|
||||
* /---
|
||||
* /---
|
||||
* t1 <---
|
||||
*
|
||||
*
|
||||
* The goal is to adjust the slave's ar.itc such that tm falls exactly half-way between t0
|
||||
* and t1. If we achieve this, the clocks are synchronized provided the interconnect
|
||||
* between the slave and the master is symmetric. Even if the interconnect were
|
||||
* asymmetric, we would still know that the synchronization error is smaller than the
|
||||
* roundtrip latency (t0 - t1).
|
||||
*
|
||||
* When the interconnect is quiet and symmetric, this lets us synchronize the itc to
|
||||
* within one or two cycles. However, we can only *guarantee* that the synchronization is
|
||||
* accurate to within a round-trip time, which is typically in the range of several
|
||||
* hundred cycles (e.g., ~500 cycles). In practice, this means that the itc's are usually
|
||||
* almost perfectly synchronized, but we shouldn't assume that the accuracy is much better
|
||||
* than half a micro second or so.
|
||||
*/
|
||||
void
|
||||
ia64_sync_itc (unsigned int master)
|
||||
{
|
||||
long i, delta, adj, adjust_latency = 0, done = 0;
|
||||
unsigned long flags, rt, master_time_stamp, bound;
|
||||
#if DEBUG_ITC_SYNC
|
||||
struct {
|
||||
long rt; /* roundtrip time */
|
||||
long master; /* master's timestamp */
|
||||
long diff; /* difference between midpoint and master's timestamp */
|
||||
long lat; /* estimate of itc adjustment latency */
|
||||
} t[NUM_ROUNDS];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure local timer ticks are disabled while we sync. If
|
||||
* they were enabled, we'd have to worry about nasty issues
|
||||
* like setting the ITC ahead of (or a long time before) the
|
||||
* next scheduled tick.
|
||||
*/
|
||||
BUG_ON((ia64_get_itv() & (1 << 16)) == 0);
|
||||
|
||||
go[MASTER] = 1;
|
||||
|
||||
if (smp_call_function_single(master, sync_master, NULL, 0) < 0) {
|
||||
printk(KERN_ERR "sync_itc: failed to get attention of CPU %u!\n", master);
|
||||
return;
|
||||
}
|
||||
|
||||
while (go[MASTER])
|
||||
cpu_relax(); /* wait for master to be ready */
|
||||
|
||||
spin_lock_irqsave(&itc_sync_lock, flags);
|
||||
{
|
||||
for (i = 0; i < NUM_ROUNDS; ++i) {
|
||||
delta = get_delta(&rt, &master_time_stamp);
|
||||
if (delta == 0) {
|
||||
done = 1; /* let's lock on to this... */
|
||||
bound = rt;
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
if (i > 0) {
|
||||
adjust_latency += -delta;
|
||||
adj = -delta + adjust_latency/4;
|
||||
} else
|
||||
adj = -delta;
|
||||
|
||||
ia64_set_itc(ia64_get_itc() + adj);
|
||||
}
|
||||
#if DEBUG_ITC_SYNC
|
||||
t[i].rt = rt;
|
||||
t[i].master = master_time_stamp;
|
||||
t[i].diff = delta;
|
||||
t[i].lat = adjust_latency/4;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&itc_sync_lock, flags);
|
||||
|
||||
#if DEBUG_ITC_SYNC
|
||||
for (i = 0; i < NUM_ROUNDS; ++i)
|
||||
printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
|
||||
t[i].rt, t[i].master, t[i].diff, t[i].lat);
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "CPU %d: synchronized ITC with CPU %u (last diff %ld cycles, "
|
||||
"maxerr %lu cycles)\n", smp_processor_id(), master, delta, rt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ideally sets up per-cpu profiling hooks. Doesn't do much now...
|
||||
*/
|
||||
static inline void __devinit
|
||||
smp_setup_percpu_timer (void)
|
||||
{
|
||||
}
|
||||
|
||||
static void __devinit
|
||||
smp_callin (void)
|
||||
{
|
||||
#ifdef XEN
|
||||
/* work around for spinlock irq assert. */
|
||||
unsigned long flags;
|
||||
#endif
|
||||
int cpuid, phys_id;
|
||||
extern void ia64_init_itm(void);
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
extern void pfm_init_percpu(void);
|
||||
#endif
|
||||
|
||||
cpuid = smp_processor_id();
|
||||
phys_id = hard_smp_processor_id();
|
||||
|
||||
if (cpu_online(cpuid)) {
|
||||
printk(KERN_ERR "huh, phys CPU#0x%x, CPU#0x%x already present??\n",
|
||||
phys_id, cpuid);
|
||||
BUG();
|
||||
}
|
||||
|
||||
fix_b0_for_bsp();
|
||||
|
||||
#ifdef XEN
|
||||
notify_cpu_starting(cpuid);
|
||||
lock_ipi_calllock(&flags);
|
||||
#else
|
||||
lock_ipi_calllock();
|
||||
#endif
|
||||
cpumask_set_cpu(cpuid, &cpu_online_map);
|
||||
#ifdef XEN
|
||||
unlock_ipi_calllock(flags);
|
||||
#else
|
||||
unlock_ipi_calllock();
|
||||
#endif
|
||||
per_cpu(cpu_state, cpuid) = CPU_ONLINE;
|
||||
|
||||
smp_setup_percpu_timer();
|
||||
|
||||
ia64_mca_cmc_vector_setup(); /* Setup vector on AP */
|
||||
|
||||
#ifdef CONFIG_PERFMON
|
||||
pfm_init_percpu();
|
||||
#endif
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
|
||||
/*
|
||||
* Synchronize the ITC with the BP. Need to do this after irqs are
|
||||
* enabled because ia64_sync_itc() calls smp_call_function_single(), which
|
||||
* calls spin_unlock_bh(), which calls spin_unlock_bh(), which calls
|
||||
* local_bh_enable(), which bugs out if irqs are not enabled...
|
||||
*/
|
||||
Dprintk("Going to syncup ITC with BP.\n");
|
||||
ia64_sync_itc(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get our bogomips.
|
||||
*/
|
||||
ia64_init_itm();
|
||||
#ifndef XEN
|
||||
calibrate_delay();
|
||||
#endif
|
||||
local_cpu_data->loops_per_jiffy = loops_per_jiffy;
|
||||
|
||||
#ifdef CONFIG_IA32_SUPPORT
|
||||
ia32_gdt_init();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allow the master to continue.
|
||||
*/
|
||||
cpumask_set_cpu(cpuid, &cpu_callin_map);
|
||||
Dprintk("Stack on CPU %d at about %p\n",cpuid, &cpuid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Activate a secondary processor. head.S calls this.
|
||||
*/
|
||||
int __devinit
|
||||
start_secondary (void *unused)
|
||||
{
|
||||
/* Early console may use I/O ports */
|
||||
ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase));
|
||||
#ifndef XEN
|
||||
Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id());
|
||||
efi_map_pal_code();
|
||||
#endif
|
||||
cpu_init();
|
||||
smp_callin();
|
||||
|
||||
#ifdef XEN
|
||||
if (vmx_enabled)
|
||||
vmx_init_env(0, 0);
|
||||
|
||||
startup_cpu_idle_loop();
|
||||
#else
|
||||
cpu_idle();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
struct create_idle {
|
||||
struct task_struct *idle;
|
||||
struct completion done;
|
||||
int cpu;
|
||||
};
|
||||
|
||||
void
|
||||
do_fork_idle(void *_c_idle)
|
||||
{
|
||||
struct create_idle *c_idle = _c_idle;
|
||||
|
||||
c_idle->idle = fork_idle(c_idle->cpu);
|
||||
complete(&c_idle->done);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit
|
||||
do_boot_cpu (int sapicid, int cpu)
|
||||
{
|
||||
int timeout;
|
||||
#ifndef XEN
|
||||
struct create_idle c_idle = {
|
||||
.cpu = cpu,
|
||||
.done = COMPLETION_INITIALIZER(c_idle.done),
|
||||
};
|
||||
DECLARE_WORK(work, do_fork_idle, &c_idle);
|
||||
|
||||
c_idle.idle = get_idle_for_cpu(cpu);
|
||||
if (c_idle.idle) {
|
||||
init_idle(c_idle.idle, cpu);
|
||||
goto do_rest;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use kernel_thread since we must avoid to reschedule the child.
|
||||
*/
|
||||
if (!keventd_up() || current_is_keventd())
|
||||
work.func(work.data);
|
||||
else {
|
||||
schedule_work(&work);
|
||||
wait_for_completion(&c_idle.done);
|
||||
}
|
||||
|
||||
if (IS_ERR(c_idle.idle))
|
||||
panic("failed fork for CPU %d", cpu);
|
||||
|
||||
set_idle_for_cpu(cpu, c_idle.idle);
|
||||
|
||||
do_rest:
|
||||
task_for_booting_cpu = c_idle.idle;
|
||||
#else
|
||||
struct vcpu *v;
|
||||
|
||||
v = idle_vcpu[cpu];
|
||||
BUG_ON(v == NULL);
|
||||
|
||||
//printf ("do_boot_cpu: cpu=%d, domain=%p, vcpu=%p\n", cpu, idle, v);
|
||||
|
||||
task_for_booting_cpu = (task_t *)v;
|
||||
|
||||
/* Set cpu number. */
|
||||
get_thread_info(v)->cpu = cpu;
|
||||
#endif
|
||||
|
||||
Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid);
|
||||
|
||||
set_brendez_area(cpu);
|
||||
platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0);
|
||||
|
||||
/*
|
||||
* Wait 10s total for the AP to start
|
||||
*/
|
||||
Dprintk("Waiting on callin_map ...");
|
||||
for (timeout = 0; timeout < 100000; timeout++) {
|
||||
if (cpumask_test_cpu(cpu, &cpu_callin_map))
|
||||
break; /* It has booted */
|
||||
udelay(100);
|
||||
}
|
||||
Dprintk("\n");
|
||||
|
||||
if (!cpumask_test_cpu(cpu, &cpu_callin_map)) {
|
||||
printk(KERN_ERR "Processor 0x%x/0x%x is stuck.\n", cpu, sapicid);
|
||||
ia64_cpu_to_sapicid[cpu] = -1;
|
||||
cpumask_clear_cpu(cpu, &cpu_online_map); /* was set in smp_callin() */
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
static int __init
|
||||
decay (char *str)
|
||||
{
|
||||
int ticks;
|
||||
get_option (&str, &ticks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("decay=", decay);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize the logical CPU number to SAPICID mapping
|
||||
*/
|
||||
void __init
|
||||
smp_build_cpu_map (void)
|
||||
{
|
||||
int sapicid, cpu, i;
|
||||
int boot_cpu_id = hard_smp_processor_id();
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++) {
|
||||
ia64_cpu_to_sapicid[cpu] = -1;
|
||||
#ifndef XEN
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
cpu_set(cpu, cpu_possible_map);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
ia64_cpu_to_sapicid[0] = boot_cpu_id;
|
||||
cpumask_clear(&cpu_present_map);
|
||||
cpumask_set_cpu(0, &cpu_present_map);
|
||||
cpumask_set_cpu(0, &cpu_possible_map);
|
||||
for (cpu = 1, i = 0; i < smp_boot_data.cpu_count; i++) {
|
||||
sapicid = smp_boot_data.cpu_phys_id[i];
|
||||
if (sapicid == boot_cpu_id)
|
||||
continue;
|
||||
cpumask_set_cpu(cpu, &cpu_present_map);
|
||||
cpumask_set_cpu(cpu, &cpu_possible_map);
|
||||
ia64_cpu_to_sapicid[cpu] = sapicid;
|
||||
cpu++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cycle through the APs sending Wakeup IPIs to boot each.
|
||||
*/
|
||||
void __init
|
||||
smp_prepare_cpus (unsigned int max_cpus)
|
||||
{
|
||||
int boot_cpu_id = hard_smp_processor_id();
|
||||
|
||||
/*
|
||||
* Initialize the per-CPU profiling counter/multiplier
|
||||
*/
|
||||
|
||||
smp_setup_percpu_timer();
|
||||
|
||||
/*
|
||||
* We have the boot CPU online for sure.
|
||||
*/
|
||||
cpumask_set_cpu(0, &cpu_online_map);
|
||||
cpumask_set_cpu(0, &cpu_callin_map);
|
||||
|
||||
local_cpu_data->loops_per_jiffy = loops_per_jiffy;
|
||||
ia64_cpu_to_sapicid[0] = boot_cpu_id;
|
||||
|
||||
printk(KERN_INFO "Boot processor id 0x%x/0x%x\n", 0, boot_cpu_id);
|
||||
|
||||
current_thread_info()->cpu = 0;
|
||||
|
||||
/*
|
||||
* If SMP should be disabled, then really disable it!
|
||||
*/
|
||||
if (!max_cpus) {
|
||||
printk(KERN_INFO "SMP mode deactivated.\n");
|
||||
cpumask_clear(&cpu_online_map);
|
||||
cpumask_clear(&cpu_present_map);
|
||||
cpumask_clear(&cpu_possible_map);
|
||||
cpumask_set_cpu(0, &cpu_online_map);
|
||||
cpumask_set_cpu(0, &cpu_present_map);
|
||||
cpumask_set_cpu(0, &cpu_possible_map);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __devinit smp_prepare_boot_cpu(void)
|
||||
{
|
||||
cpumask_set_cpu(smp_processor_id(), &cpu_online_map);
|
||||
cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
|
||||
per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
|
||||
}
|
||||
|
||||
/*
|
||||
* mt_info[] is a temporary store for all info returned by
|
||||
* PAL_LOGICAL_TO_PHYSICAL, to be copied into cpuinfo_ia64 when the
|
||||
* specific cpu comes.
|
||||
*/
|
||||
static struct {
|
||||
__u32 socket_id;
|
||||
__u16 core_id;
|
||||
__u16 thread_id;
|
||||
__u16 proc_fixed_addr;
|
||||
__u8 valid;
|
||||
} mt_info[NR_CPUS] __devinitdata;
|
||||
|
||||
#if defined(XEN) && !defined(CONFIG_HOTPLUG_CPU)
|
||||
static inline void
|
||||
remove_from_mtinfo(int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
if (mt_info[i].valid && mt_info[i].socket_id ==
|
||||
cpu_data(cpu)->socket_id)
|
||||
mt_info[i].valid = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
clear_cpu_sibling_map(int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_cpu(i, per_cpu(cpu_sibling_mask, cpu))
|
||||
cpumask_clear_cpu(cpu, per_cpu(cpu_sibling_mask, i));
|
||||
for_each_cpu(i, per_cpu(cpu_core_mask, cpu))
|
||||
cpumask_clear_cpu(cpu, per_cpu(cpu_core_mask, i));
|
||||
|
||||
cpumask_clear(per_cpu(cpu_sibling_mask, cpu));
|
||||
cpumask_clear(per_cpu(cpu_core_mask, cpu));
|
||||
}
|
||||
|
||||
static void
|
||||
remove_siblinginfo(int cpu)
|
||||
{
|
||||
int last = 0;
|
||||
|
||||
if (cpu_data(cpu)->threads_per_core == 1 &&
|
||||
cpu_data(cpu)->cores_per_socket == 1) {
|
||||
cpumask_clear_cpu(cpu, per_cpu(cpu_core_mask, cpu));
|
||||
cpumask_clear_cpu(cpu, per_cpu(cpu_sibling_mask, cpu));
|
||||
return;
|
||||
}
|
||||
|
||||
last = (cpumask_weight(per_cpu(cpu_core_mask, cpu)) == 1);
|
||||
|
||||
/* remove it from all sibling map's */
|
||||
clear_cpu_sibling_map(cpu);
|
||||
|
||||
/* if this cpu is the last in the core group, remove all its info
|
||||
* from mt_info structure
|
||||
*/
|
||||
if (last)
|
||||
remove_from_mtinfo(cpu);
|
||||
}
|
||||
|
||||
extern void fixup_irqs(void);
|
||||
/* must be called with cpucontrol mutex held */
|
||||
void __cpu_disable(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
remove_siblinginfo(cpu);
|
||||
cpumask_clear_cpu(cpu, &cpu_online_map);
|
||||
#ifndef XEN
|
||||
fixup_irqs();
|
||||
#endif
|
||||
local_flush_tlb_all();
|
||||
cpumask_clear_cpu(cpu, &cpu_callin_map);
|
||||
}
|
||||
#else /* !CONFIG_HOTPLUG_CPU */
|
||||
void __cpu_disable(void)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
void __cpu_die(unsigned int cpu)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
/* They ack this in play_dead by setting CPU_DEAD */
|
||||
if (per_cpu(cpu_state, cpu) == CPU_DEAD)
|
||||
{
|
||||
printk ("CPU %d is now offline\n", cpu);
|
||||
return;
|
||||
}
|
||||
#ifdef XEN
|
||||
udelay(100 * 1000);
|
||||
#else
|
||||
msleep(100);
|
||||
#endif
|
||||
}
|
||||
printk(KERN_ERR "CPU %u didn't die...\n", cpu);
|
||||
}
|
||||
#else /* !CONFIG_HOTPLUG_CPU */
|
||||
void __cpu_die(unsigned int cpu)
|
||||
{
|
||||
/* We said "no" in __cpu_disable */
|
||||
BUG();
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
void
|
||||
smp_cpus_done(void)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long bogosum = 0;
|
||||
|
||||
/*
|
||||
* Allow the user to impress friends.
|
||||
*/
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++)
|
||||
if (cpu_online(cpu))
|
||||
bogosum += cpu_data(cpu)->loops_per_jiffy;
|
||||
|
||||
printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
|
||||
(int)num_online_cpus(), bogosum/(500000/HZ), (bogosum/(5000/HZ))%100);
|
||||
}
|
||||
|
||||
static inline void __devinit
|
||||
set_cpu_sibling_map(int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_online_cpu(i) {
|
||||
if ((cpu_data(cpu)->socket_id == cpu_data(i)->socket_id)) {
|
||||
cpumask_set_cpu(i, per_cpu(cpu_core_mask, cpu));
|
||||
cpumask_set_cpu(cpu, per_cpu(cpu_core_mask, i));
|
||||
if (cpu_data(cpu)->core_id == cpu_data(i)->core_id) {
|
||||
cpumask_set_cpu(i, per_cpu(cpu_sibling_mask, cpu));
|
||||
cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __devinit
|
||||
__cpu_up (unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
int sapicid;
|
||||
|
||||
sapicid = ia64_cpu_to_sapicid[cpu];
|
||||
if (sapicid == -1)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Already booted cpu? not valid anymore since we dont
|
||||
* do idle loop tightspin anymore.
|
||||
*/
|
||||
if (cpumask_test_cpu(cpu, &cpu_callin_map))
|
||||
return -EINVAL;
|
||||
|
||||
if (!per_cpu(cpu_sibling_mask, cpu) &&
|
||||
!zalloc_cpumask_var(&per_cpu(cpu_sibling_mask, cpu)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!per_cpu(cpu_core_mask, cpu) &&
|
||||
!zalloc_cpumask_var(&per_cpu(cpu_core_mask, cpu)))
|
||||
return -ENOMEM;
|
||||
|
||||
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
|
||||
/* Processor goes to start_secondary(), sets online flag */
|
||||
ret = do_boot_cpu(sapicid, cpu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (cpu_data(cpu)->threads_per_core == 1 &&
|
||||
cpu_data(cpu)->cores_per_socket == 1) {
|
||||
cpumask_set_cpu(cpu, per_cpu(cpu_sibling_mask, cpu));
|
||||
cpumask_set_cpu(cpu, per_cpu(cpu_core_mask, cpu));
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_cpu_sibling_map(cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assume that CPU's have been discovered by some platform-dependent interface. For
|
||||
* SoftSDV/Lion, that would be ACPI.
|
||||
*
|
||||
* Setup of the IPI irq handler is done in irq.c:init_IRQ_SMP().
|
||||
*/
|
||||
void __init
|
||||
init_smp_config(void)
|
||||
{
|
||||
struct fptr {
|
||||
unsigned long fp;
|
||||
unsigned long gp;
|
||||
} *ap_startup;
|
||||
long sal_ret;
|
||||
|
||||
/* Tell SAL where to drop the AP's. */
|
||||
ap_startup = (struct fptr *) start_ap;
|
||||
sal_ret = ia64_sal_set_vectors(SAL_VECTOR_OS_BOOT_RENDEZ,
|
||||
ia64_tpa(ap_startup->fp), ia64_tpa(ap_startup->gp), 0, 0, 0, 0);
|
||||
if (sal_ret < 0)
|
||||
printk(KERN_ERR "SMP: Can't set SAL AP Boot Rendezvous: %s\n",
|
||||
ia64_sal_strerror(sal_ret));
|
||||
}
|
||||
|
||||
static inline int __devinit
|
||||
check_for_mtinfo_index(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
if (!mt_info[i].valid)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the mt_info to find out if this socket's cid/tid information is
|
||||
* cached or not. If the socket exists, fill in the core_id and thread_id
|
||||
* in cpuinfo
|
||||
*/
|
||||
static int __devinit
|
||||
check_for_new_socket(__u16 logical_address, struct cpuinfo_ia64 *c)
|
||||
{
|
||||
int i;
|
||||
__u32 sid = c->socket_id;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
if (mt_info[i].valid && mt_info[i].proc_fixed_addr == logical_address
|
||||
&& mt_info[i].socket_id == sid) {
|
||||
c->core_id = mt_info[i].core_id;
|
||||
c->thread_id = mt_info[i].thread_id;
|
||||
return 1; /* not a new socket */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* identify_siblings(cpu) gets called from identify_cpu. This populates the
|
||||
* information related to logical execution units in per_cpu_data structure.
|
||||
*/
|
||||
void __devinit
|
||||
identify_siblings(struct cpuinfo_ia64 *c)
|
||||
{
|
||||
s64 status;
|
||||
u16 pltid;
|
||||
u64 proc_fixed_addr;
|
||||
int count, i;
|
||||
pal_logical_to_physical_t info;
|
||||
|
||||
if (smp_num_cpucores == 1 && smp_num_siblings == 1)
|
||||
return;
|
||||
|
||||
if ((status = ia64_pal_logical_to_phys(0, &info)) != PAL_STATUS_SUCCESS) {
|
||||
printk(KERN_ERR "ia64_pal_logical_to_phys failed with %ld\n",
|
||||
status);
|
||||
return;
|
||||
}
|
||||
if ((status = ia64_sal_physical_id_info(&pltid)) != PAL_STATUS_SUCCESS) {
|
||||
printk(KERN_ERR "ia64_sal_pltid failed with %ld\n", status);
|
||||
return;
|
||||
}
|
||||
if ((status = ia64_pal_fixed_addr(&proc_fixed_addr)) != PAL_STATUS_SUCCESS) {
|
||||
printk(KERN_ERR "ia64_pal_fixed_addr failed with %ld\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
c->socket_id = (pltid << 8) | info.overview_ppid;
|
||||
c->cores_per_socket = info.overview_cpp;
|
||||
c->threads_per_core = info.overview_tpc;
|
||||
count = c->num_log = info.overview_num_log;
|
||||
|
||||
/* If the thread and core id information is already cached, then
|
||||
* we will simply update cpu_info and return. Otherwise, we will
|
||||
* do the PAL calls and cache core and thread id's of all the siblings.
|
||||
*/
|
||||
if (check_for_new_socket(proc_fixed_addr, c))
|
||||
return;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int index;
|
||||
|
||||
if (i && (status = ia64_pal_logical_to_phys(i, &info))
|
||||
!= PAL_STATUS_SUCCESS) {
|
||||
printk(KERN_ERR "ia64_pal_logical_to_phys failed"
|
||||
" with %ld\n", status);
|
||||
return;
|
||||
}
|
||||
if (info.log2_la == proc_fixed_addr) {
|
||||
c->core_id = info.log1_cid;
|
||||
c->thread_id = info.log1_tid;
|
||||
}
|
||||
|
||||
index = check_for_mtinfo_index();
|
||||
/* We will not do the mt_info caching optimization in this case.
|
||||
*/
|
||||
if (index < 0)
|
||||
continue;
|
||||
|
||||
mt_info[index].valid = 1;
|
||||
mt_info[index].socket_id = c->socket_id;
|
||||
mt_info[index].core_id = info.log1_cid;
|
||||
mt_info[index].thread_id = info.log1_tid;
|
||||
mt_info[index].proc_fixed_addr = info.log2_la;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SMP ifdef XEN */
|
||||
@@ -1 +0,0 @@
|
||||
subdir-y += kernel
|
||||
@@ -1,5 +0,0 @@
|
||||
obj-y += sn2_smp.o
|
||||
obj-y += setup.o
|
||||
obj-y += iomv.o
|
||||
obj-y += irq.o
|
||||
obj-y += io_init.o
|
||||
@@ -1,12 +0,0 @@
|
||||
# Source files in this directory are near-identical copies of linux-2.6.19
|
||||
# files:
|
||||
|
||||
# NOTE: ALL changes to these files should be clearly marked
|
||||
# (e.g. with #ifdef XEN or XEN in a comment) so that they can be
|
||||
# easily updated to future versions of the corresponding Linux files.
|
||||
|
||||
io_init.c -> linux/arch/ia64/sn/kernel/io_init.c
|
||||
iomv.c -> linux/arch/ia64/sn/kernel/iomv.c
|
||||
irq.c -> linux/arch/ia64/sn/kernel/irq.c
|
||||
setup.c -> linux/arch/ia64/sn/kernel/setup.c
|
||||
sn2_smp.c -> linux/arch/ia64/sn/kernel/sn2/sn2_smp.c
|
||||
@@ -1,793 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/nodemask.h>
|
||||
#ifdef XEN
|
||||
#include <linux/init.h>
|
||||
#endif
|
||||
#include <asm/sn/types.h>
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/sn_feature_sets.h>
|
||||
#include <asm/sn/geo.h>
|
||||
#include <asm/sn/io.h>
|
||||
#include <asm/sn/l1.h>
|
||||
#include <asm/sn/module.h>
|
||||
#include <asm/sn/pcibr_provider.h>
|
||||
#include <asm/sn/pcibus_provider_defs.h>
|
||||
#include <asm/sn/pcidev.h>
|
||||
#include <asm/sn/simulator.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#ifndef XEN
|
||||
#include <asm/sn/tioca_provider.h>
|
||||
#include <asm/sn/tioce_provider.h>
|
||||
#endif
|
||||
#ifdef XEN
|
||||
#include "asm/sn/hubdev.h"
|
||||
#include "asm/sn/xwidgetdev.h"
|
||||
#else
|
||||
#include "xtalk/hubdev.h"
|
||||
#include "xtalk/xwidgetdev.h"
|
||||
#endif
|
||||
|
||||
|
||||
extern void sn_init_cpei_timer(void);
|
||||
extern void register_sn_procfs(void);
|
||||
#ifdef XEN
|
||||
#define pci_dev_get(dev) do{}while(0)
|
||||
extern void sn_irq_lh_init(void);
|
||||
#endif
|
||||
|
||||
static struct list_head sn_sysdata_list;
|
||||
|
||||
/* sysdata list struct */
|
||||
struct sysdata_el {
|
||||
struct list_head entry;
|
||||
void *sysdata;
|
||||
};
|
||||
|
||||
struct slab_info {
|
||||
struct hubdev_info hubdev;
|
||||
};
|
||||
|
||||
struct brick {
|
||||
moduleid_t id; /* Module ID of this module */
|
||||
struct slab_info slab_info[MAX_SLABS + 1];
|
||||
};
|
||||
|
||||
int sn_ioif_inited; /* SN I/O infrastructure initialized? */
|
||||
|
||||
struct sn_pcibus_provider *sn_pci_provider[PCIIO_ASIC_MAX_TYPES]; /* indexed by asic type */
|
||||
|
||||
static int max_segment_number; /* Default highest segment number */
|
||||
static int max_pcibus_number = 255; /* Default highest pci bus number */
|
||||
|
||||
/*
|
||||
* Hooks and struct for unsupported pci providers
|
||||
*/
|
||||
|
||||
static dma_addr_t
|
||||
sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size, int type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sn_default_pci_unmap(struct pci_dev *pdev, dma_addr_t addr, int direction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void *
|
||||
sn_default_pci_bus_fixup(struct pcibus_bussoft *soft, struct pci_controller *controller)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct sn_pcibus_provider sn_pci_default_provider = {
|
||||
.dma_map = sn_default_pci_map,
|
||||
.dma_map_consistent = sn_default_pci_map,
|
||||
.dma_unmap = sn_default_pci_unmap,
|
||||
.bus_fixup = sn_default_pci_bus_fixup,
|
||||
};
|
||||
|
||||
/*
|
||||
* Retrieve the DMA Flush List given nasid, widget, and device.
|
||||
* This list is needed to implement the WAR - Flush DMA data on PIO Reads.
|
||||
*/
|
||||
static inline u64
|
||||
sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num,
|
||||
u64 address)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff,
|
||||
(u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST,
|
||||
(u64) nasid, (u64) widget_num,
|
||||
(u64) device_num, (u64) address, 0, 0, 0);
|
||||
return ret_stuff.status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the hub device info structure for the given nasid.
|
||||
*/
|
||||
static inline u64 sal_get_hubdev_info(u64 handle, u64 address)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff,
|
||||
(u64) SN_SAL_IOIF_GET_HUBDEV_INFO,
|
||||
(u64) handle, (u64) address, 0, 0, 0, 0, 0);
|
||||
return ret_stuff.v0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the pci bus information given the bus number.
|
||||
*/
|
||||
static inline u64 sal_get_pcibus_info(u64 segment, u64 busnum, u64 address)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff,
|
||||
(u64) SN_SAL_IOIF_GET_PCIBUS_INFO,
|
||||
(u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0);
|
||||
return ret_stuff.v0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the pci device information given the bus and device|function number.
|
||||
*/
|
||||
static inline u64
|
||||
sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
|
||||
u64 sn_irq_info)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff,
|
||||
(u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
|
||||
(u64) segment, (u64) bus_number, (u64) devfn,
|
||||
(u64) pci_dev,
|
||||
sn_irq_info, 0, 0);
|
||||
return ret_stuff.v0;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* sn_pcidev_info_get() - Retrieve the pcidev_info struct for the specified
|
||||
* device.
|
||||
*/
|
||||
inline struct pcidev_info *
|
||||
sn_pcidev_info_get(struct pci_dev *dev)
|
||||
{
|
||||
struct pcidev_info *pcidev;
|
||||
|
||||
list_for_each_entry(pcidev,
|
||||
&(SN_PCI_CONTROLLER(dev)->pcidev_info), pdi_list) {
|
||||
if (pcidev->pdi_linux_pcidev == dev) {
|
||||
return pcidev;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Older PROM flush WAR
|
||||
*
|
||||
* 01/16/06 -- This war will be in place until a new official PROM is released.
|
||||
* Additionally note that the struct sn_flush_device_war also has to be
|
||||
* removed from arch/ia64/sn/include/xtalk/hubdev.h
|
||||
*/
|
||||
static u8 war_implemented = 0;
|
||||
|
||||
static s64 sn_device_fixup_war(u64 nasid, u64 widget, int device,
|
||||
struct sn_flush_device_common *common)
|
||||
{
|
||||
struct sn_flush_device_war *war_list;
|
||||
struct sn_flush_device_war *dev_entry;
|
||||
struct ia64_sal_retval isrv = {0,0,0,0};
|
||||
|
||||
if (!war_implemented) {
|
||||
printk(KERN_WARNING "PROM version < 4.50 -- implementing old "
|
||||
"PROM flush WAR\n");
|
||||
war_implemented = 1;
|
||||
}
|
||||
|
||||
war_list = kzalloc(DEV_PER_WIDGET * sizeof(*war_list), GFP_KERNEL);
|
||||
if (!war_list)
|
||||
BUG();
|
||||
|
||||
SAL_CALL_NOLOCK(isrv, SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST,
|
||||
nasid, widget, __pa(war_list), 0, 0, 0 ,0);
|
||||
if (isrv.status)
|
||||
panic("sn_device_fixup_war failed: %s\n",
|
||||
ia64_sal_strerror(isrv.status));
|
||||
|
||||
dev_entry = war_list + device;
|
||||
memcpy(common,dev_entry, sizeof(*common));
|
||||
kfree(war_list);
|
||||
|
||||
return isrv.status;
|
||||
}
|
||||
|
||||
/*
|
||||
* sn_fixup_ionodes() - This routine initializes the HUB data strcuture for
|
||||
* each node in the system.
|
||||
*/
|
||||
static void __init sn_fixup_ionodes(void)
|
||||
{
|
||||
struct sn_flush_device_kernel *sn_flush_device_kernel;
|
||||
struct sn_flush_device_kernel *dev_entry;
|
||||
struct hubdev_info *hubdev;
|
||||
u64 status;
|
||||
u64 nasid;
|
||||
int i, widget, device, size;
|
||||
|
||||
/*
|
||||
* Get SGI Specific HUB chipset information.
|
||||
* Inform Prom that this kernel can support domain bus numbering.
|
||||
*/
|
||||
for (i = 0; i < num_cnodes; i++) {
|
||||
hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo);
|
||||
nasid = cnodeid_to_nasid(i);
|
||||
hubdev->max_segment_number = 0xffffffff;
|
||||
hubdev->max_pcibus_number = 0xff;
|
||||
status = sal_get_hubdev_info(nasid, (u64) __pa(hubdev));
|
||||
if (status)
|
||||
continue;
|
||||
|
||||
/* Save the largest Domain and pcibus numbers found. */
|
||||
if (hubdev->max_segment_number) {
|
||||
/*
|
||||
* Dealing with a Prom that supports segments.
|
||||
*/
|
||||
max_segment_number = hubdev->max_segment_number;
|
||||
max_pcibus_number = hubdev->max_pcibus_number;
|
||||
}
|
||||
|
||||
/* Attach the error interrupt handlers */
|
||||
if (nasid & 1)
|
||||
ice_error_init(hubdev);
|
||||
else
|
||||
hub_error_init(hubdev);
|
||||
|
||||
for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++)
|
||||
hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev;
|
||||
|
||||
if (!hubdev->hdi_flush_nasid_list.widget_p)
|
||||
continue;
|
||||
|
||||
size = (HUB_WIDGET_ID_MAX + 1) *
|
||||
sizeof(struct sn_flush_device_kernel *);
|
||||
hubdev->hdi_flush_nasid_list.widget_p =
|
||||
kzalloc(size, GFP_KERNEL);
|
||||
if (!hubdev->hdi_flush_nasid_list.widget_p)
|
||||
BUG();
|
||||
|
||||
for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) {
|
||||
size = DEV_PER_WIDGET *
|
||||
sizeof(struct sn_flush_device_kernel);
|
||||
sn_flush_device_kernel = kzalloc(size, GFP_KERNEL);
|
||||
if (!sn_flush_device_kernel)
|
||||
BUG();
|
||||
|
||||
dev_entry = sn_flush_device_kernel;
|
||||
for (device = 0; device < DEV_PER_WIDGET;
|
||||
device++,dev_entry++) {
|
||||
size = sizeof(struct sn_flush_device_common);
|
||||
dev_entry->common = kzalloc(size, GFP_KERNEL);
|
||||
if (!dev_entry->common)
|
||||
BUG();
|
||||
|
||||
if (sn_prom_feature_available(
|
||||
PRF_DEVICE_FLUSH_LIST))
|
||||
status = sal_get_device_dmaflush_list(
|
||||
nasid, widget, device,
|
||||
(u64)(dev_entry->common));
|
||||
else
|
||||
#ifdef XEN
|
||||
BUG();
|
||||
#else
|
||||
status = sn_device_fixup_war(nasid,
|
||||
widget, device,
|
||||
dev_entry->common);
|
||||
#endif
|
||||
if (status != SALRET_OK)
|
||||
panic("SAL call failed: %s\n",
|
||||
ia64_sal_strerror(status));
|
||||
|
||||
spin_lock_init(&dev_entry->sfdl_flush_lock);
|
||||
}
|
||||
|
||||
if (sn_flush_device_kernel)
|
||||
hubdev->hdi_flush_nasid_list.widget_p[widget] =
|
||||
sn_flush_device_kernel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sn_pci_window_fixup() - Create a pci_window for each device resource.
|
||||
* Until ACPI support is added, we need this code
|
||||
* to setup pci_windows for use by
|
||||
* pcibios_bus_to_resource(),
|
||||
* pcibios_resource_to_bus(), etc.
|
||||
*/
|
||||
static void
|
||||
sn_pci_window_fixup(struct pci_dev *dev, unsigned int count,
|
||||
s64 * pci_addrs)
|
||||
{
|
||||
struct pci_controller *controller = PCI_CONTROLLER(dev->bus);
|
||||
unsigned int i;
|
||||
unsigned int idx;
|
||||
unsigned int new_count;
|
||||
struct pci_window *new_window;
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
idx = controller->windows;
|
||||
new_count = controller->windows + count;
|
||||
new_window = kcalloc(new_count, sizeof(struct pci_window), GFP_KERNEL);
|
||||
if (new_window == NULL)
|
||||
BUG();
|
||||
if (controller->window) {
|
||||
memcpy(new_window, controller->window,
|
||||
sizeof(struct pci_window) * controller->windows);
|
||||
kfree(controller->window);
|
||||
}
|
||||
|
||||
/* Setup a pci_window for each device resource. */
|
||||
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
|
||||
if (pci_addrs[i] == -1)
|
||||
continue;
|
||||
|
||||
new_window[idx].offset = dev->resource[i].start - pci_addrs[i];
|
||||
new_window[idx].resource = dev->resource[i];
|
||||
idx++;
|
||||
}
|
||||
|
||||
controller->windows = new_count;
|
||||
controller->window = new_window;
|
||||
}
|
||||
|
||||
void sn_pci_unfixup_slot(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *host_pci_dev = SN_PCIDEV_INFO(dev)->host_pci_dev;
|
||||
|
||||
sn_irq_unfixup(dev);
|
||||
pci_dev_put(host_pci_dev);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* sn_pci_fixup_slot() - This routine sets up a slot's resources
|
||||
* consistent with the Linux PCI abstraction layer. Resources acquired
|
||||
* from our PCI provider include PIO maps to BAR space and interrupt
|
||||
* objects.
|
||||
*/
|
||||
void sn_pci_fixup_slot(struct pci_dev *dev)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
int idx;
|
||||
int segment = pci_domain_nr(dev->bus);
|
||||
int status = 0;
|
||||
struct pcibus_bussoft *bs;
|
||||
struct pci_bus *host_pci_bus;
|
||||
struct pci_dev *host_pci_dev;
|
||||
struct pcidev_info *pcidev_info;
|
||||
s64 pci_addrs[PCI_ROM_RESOURCE + 1];
|
||||
struct sn_irq_info *sn_irq_info;
|
||||
unsigned long size;
|
||||
unsigned int bus_no, devfn;
|
||||
|
||||
pci_dev_get(dev); /* for the sysdata pointer */
|
||||
pcidev_info = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
|
||||
if (!pcidev_info)
|
||||
BUG(); /* Cannot afford to run out of memory */
|
||||
|
||||
sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
|
||||
if (!sn_irq_info)
|
||||
BUG(); /* Cannot afford to run out of memory */
|
||||
|
||||
/* Call to retrieve pci device information needed by kernel. */
|
||||
status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number,
|
||||
dev->devfn,
|
||||
(u64) __pa(pcidev_info),
|
||||
(u64) __pa(sn_irq_info));
|
||||
if (status)
|
||||
BUG(); /* Cannot get platform pci device information */
|
||||
|
||||
/* Add pcidev_info to list in sn_pci_controller struct */
|
||||
list_add_tail(&pcidev_info->pdi_list,
|
||||
&(SN_PCI_CONTROLLER(dev->bus)->pcidev_info));
|
||||
|
||||
/* Copy over PIO Mapped Addresses */
|
||||
for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
|
||||
unsigned long start, end, addr;
|
||||
|
||||
if (!pcidev_info->pdi_pio_mapped_addr[idx]) {
|
||||
pci_addrs[idx] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
start = dev->resource[idx].start;
|
||||
end = dev->resource[idx].end;
|
||||
size = end - start;
|
||||
if (size == 0) {
|
||||
pci_addrs[idx] = -1;
|
||||
continue;
|
||||
}
|
||||
pci_addrs[idx] = start;
|
||||
count++;
|
||||
addr = pcidev_info->pdi_pio_mapped_addr[idx];
|
||||
addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET;
|
||||
dev->resource[idx].start = addr;
|
||||
dev->resource[idx].end = addr + size;
|
||||
#ifndef XEN
|
||||
if (dev->resource[idx].flags & IORESOURCE_IO)
|
||||
dev->resource[idx].parent = &ioport_resource;
|
||||
else
|
||||
dev->resource[idx].parent = &iomem_resource;
|
||||
#endif
|
||||
}
|
||||
/* Create a pci_window in the pci_controller struct for
|
||||
* each device resource.
|
||||
*/
|
||||
if (count > 0)
|
||||
sn_pci_window_fixup(dev, count, pci_addrs);
|
||||
|
||||
/*
|
||||
* Using the PROMs values for the PCI host bus, get the Linux
|
||||
* PCI host_pci_dev struct and set up host bus linkages
|
||||
*/
|
||||
|
||||
bus_no = (pcidev_info->pdi_slot_host_handle >> 32) & 0xff;
|
||||
devfn = pcidev_info->pdi_slot_host_handle & 0xffffffff;
|
||||
host_pci_bus = pci_find_bus(segment, bus_no);
|
||||
host_pci_dev = pci_get_slot(host_pci_bus, devfn);
|
||||
|
||||
pcidev_info->host_pci_dev = host_pci_dev;
|
||||
pcidev_info->pdi_linux_pcidev = dev;
|
||||
pcidev_info->pdi_host_pcidev_info = SN_PCIDEV_INFO(host_pci_dev);
|
||||
bs = SN_PCIBUS_BUSSOFT(dev->bus);
|
||||
pcidev_info->pdi_pcibus_info = bs;
|
||||
|
||||
if (bs && bs->bs_asic_type < PCIIO_ASIC_MAX_TYPES) {
|
||||
SN_PCIDEV_BUSPROVIDER(dev) = sn_pci_provider[bs->bs_asic_type];
|
||||
} else {
|
||||
SN_PCIDEV_BUSPROVIDER(dev) = &sn_pci_default_provider;
|
||||
}
|
||||
|
||||
/* Only set up IRQ stuff if this device has a host bus context */
|
||||
if (bs && sn_irq_info->irq_irq) {
|
||||
pcidev_info->pdi_sn_irq_info = sn_irq_info;
|
||||
dev->irq = pcidev_info->pdi_sn_irq_info->irq_irq;
|
||||
sn_irq_fixup(dev, sn_irq_info);
|
||||
} else {
|
||||
pcidev_info->pdi_sn_irq_info = NULL;
|
||||
kfree(sn_irq_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sn_pci_controller_fixup() - This routine sets up a bus's resources
|
||||
* consistent with the Linux PCI abstraction layer.
|
||||
*/
|
||||
void sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus)
|
||||
{
|
||||
int status;
|
||||
int nasid, cnode;
|
||||
struct pci_controller *controller;
|
||||
struct sn_pci_controller *sn_controller;
|
||||
struct pcibus_bussoft *prom_bussoft_ptr;
|
||||
struct hubdev_info *hubdev_info;
|
||||
void *provider_soft;
|
||||
struct sn_pcibus_provider *provider;
|
||||
|
||||
status = sal_get_pcibus_info((u64) segment, (u64) busnum,
|
||||
(u64) ia64_tpa(&prom_bussoft_ptr));
|
||||
if (status > 0)
|
||||
return; /*bus # does not exist */
|
||||
prom_bussoft_ptr = __va(prom_bussoft_ptr);
|
||||
|
||||
/* Allocate a sn_pci_controller, which has a pci_controller struct
|
||||
* as the first member.
|
||||
*/
|
||||
sn_controller = kzalloc(sizeof(struct sn_pci_controller), GFP_KERNEL);
|
||||
if (!sn_controller)
|
||||
BUG();
|
||||
INIT_LIST_HEAD(&sn_controller->pcidev_info);
|
||||
controller = &sn_controller->pci_controller;
|
||||
controller->segment = segment;
|
||||
|
||||
#ifndef XEN
|
||||
if (bus == NULL) {
|
||||
bus = pci_scan_bus(busnum, &pci_root_ops, controller);
|
||||
if (bus == NULL)
|
||||
goto error_return; /* error, or bus already scanned */
|
||||
bus->sysdata = NULL;
|
||||
}
|
||||
|
||||
if (bus->sysdata)
|
||||
goto error_return; /* sysdata already alloc'd */
|
||||
|
||||
/*
|
||||
* Per-provider fixup. Copies the contents from prom to local
|
||||
* area and links SN_PCIBUS_BUSSOFT().
|
||||
*/
|
||||
|
||||
if (prom_bussoft_ptr->bs_asic_type >= PCIIO_ASIC_MAX_TYPES)
|
||||
goto error_return; /* unsupported asic type */
|
||||
|
||||
if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB)
|
||||
goto error_return; /* no further fixup necessary */
|
||||
|
||||
#endif
|
||||
provider = sn_pci_provider[prom_bussoft_ptr->bs_asic_type];
|
||||
if (provider == NULL)
|
||||
goto error_return; /* no provider registerd for this asic */
|
||||
|
||||
bus->sysdata = controller;
|
||||
if (provider->bus_fixup)
|
||||
provider_soft = (*provider->bus_fixup) (prom_bussoft_ptr, controller);
|
||||
else
|
||||
provider_soft = NULL;
|
||||
|
||||
if (provider_soft == NULL) {
|
||||
/* fixup failed or not applicable */
|
||||
bus->sysdata = NULL;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup pci_windows for legacy IO and MEM space.
|
||||
* (Temporary until ACPI support is in place.)
|
||||
*/
|
||||
controller->window = kcalloc(2, sizeof(struct pci_window), GFP_KERNEL);
|
||||
if (controller->window == NULL)
|
||||
BUG();
|
||||
controller->window[0].offset = prom_bussoft_ptr->bs_legacy_io;
|
||||
controller->window[0].resource.name = "legacy_io";
|
||||
controller->window[0].resource.flags = IORESOURCE_IO;
|
||||
controller->window[0].resource.start = prom_bussoft_ptr->bs_legacy_io;
|
||||
controller->window[0].resource.end =
|
||||
controller->window[0].resource.start + 0xffff;
|
||||
#ifndef XEN
|
||||
controller->window[0].resource.parent = &ioport_resource;
|
||||
#endif
|
||||
controller->window[1].offset = prom_bussoft_ptr->bs_legacy_mem;
|
||||
controller->window[1].resource.name = "legacy_mem";
|
||||
controller->window[1].resource.flags = IORESOURCE_MEM;
|
||||
controller->window[1].resource.start = prom_bussoft_ptr->bs_legacy_mem;
|
||||
controller->window[1].resource.end =
|
||||
controller->window[1].resource.start + (1024 * 1024) - 1;
|
||||
#ifndef XEN
|
||||
controller->window[1].resource.parent = &iomem_resource;
|
||||
#endif
|
||||
controller->windows = 2;
|
||||
|
||||
/*
|
||||
* Generic bus fixup goes here. Don't reference prom_bussoft_ptr
|
||||
* after this point.
|
||||
*/
|
||||
|
||||
PCI_CONTROLLER(bus)->platform_data = provider_soft;
|
||||
nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base);
|
||||
cnode = nasid_to_cnodeid(nasid);
|
||||
hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
|
||||
SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info =
|
||||
&(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]);
|
||||
|
||||
/*
|
||||
* If the node information we obtained during the fixup phase is invalid
|
||||
* then set controller->node to -1 (undetermined)
|
||||
*/
|
||||
if (controller->node >= num_online_nodes()) {
|
||||
struct pcibus_bussoft *b = SN_PCIBUS_BUSSOFT(bus);
|
||||
|
||||
printk(KERN_WARNING "Device ASIC=%u XID=%u PBUSNUM=%u"
|
||||
"L_IO=%lx L_MEM=%lx BASE=%lx\n",
|
||||
b->bs_asic_type, b->bs_xid, b->bs_persist_busnum,
|
||||
b->bs_legacy_io, b->bs_legacy_mem, b->bs_base);
|
||||
printk(KERN_WARNING "on node %d but only %d nodes online."
|
||||
"Association set to undetermined.\n",
|
||||
controller->node, num_online_nodes());
|
||||
controller->node = -1;
|
||||
}
|
||||
return;
|
||||
|
||||
error_return:
|
||||
|
||||
kfree(sn_controller);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
void sn_bus_store_sysdata(struct pci_dev *dev)
|
||||
{
|
||||
struct sysdata_el *element;
|
||||
|
||||
element = kzalloc(sizeof(struct sysdata_el), GFP_KERNEL);
|
||||
if (!element) {
|
||||
dev_dbg(dev, "%s: out of memory!\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
element->sysdata = SN_PCIDEV_INFO(dev);
|
||||
list_add(&element->entry, &sn_sysdata_list);
|
||||
}
|
||||
|
||||
void sn_bus_free_sysdata(void)
|
||||
{
|
||||
struct sysdata_el *element;
|
||||
struct list_head *list, *safe;
|
||||
|
||||
list_for_each_safe(list, safe, &sn_sysdata_list) {
|
||||
element = list_entry(list, struct sysdata_el, entry);
|
||||
list_del(&element->entry);
|
||||
list_del(&(((struct pcidev_info *)
|
||||
(element->sysdata))->pdi_list));
|
||||
kfree(element->sysdata);
|
||||
kfree(element);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ugly hack to get PCI setup until we have a proper ACPI namespace.
|
||||
*/
|
||||
|
||||
#define PCI_BUSES_TO_SCAN 256
|
||||
|
||||
static int __init sn_io_early_init(void)
|
||||
{
|
||||
int i, j;
|
||||
#ifndef XEN
|
||||
struct pci_dev *pci_dev = NULL;
|
||||
#endif
|
||||
|
||||
if (!ia64_platform_is("sn2") || IS_RUNNING_ON_FAKE_PROM())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* prime sn_pci_provider[]. Individial provider init routines will
|
||||
* override their respective default entries.
|
||||
*/
|
||||
|
||||
for (i = 0; i < PCIIO_ASIC_MAX_TYPES; i++)
|
||||
sn_pci_provider[i] = &sn_pci_default_provider;
|
||||
|
||||
#ifndef XEN
|
||||
pcibr_init_provider();
|
||||
tioca_init_provider();
|
||||
tioce_init_provider();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is needed to avoid bounce limit checks in the blk layer
|
||||
*/
|
||||
ia64_max_iommu_merge_mask = ~PAGE_MASK;
|
||||
#ifndef XEN
|
||||
sn_fixup_ionodes();
|
||||
#endif
|
||||
sn_irq_lh_init();
|
||||
INIT_LIST_HEAD(&sn_sysdata_list);
|
||||
#ifndef XEN
|
||||
sn_init_cpei_timer();
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
register_sn_procfs();
|
||||
#endif
|
||||
#endif
|
||||
/* busses are not known yet ... */
|
||||
for (i = 0; i <= max_segment_number; i++)
|
||||
for (j = 0; j <= max_pcibus_number; j++)
|
||||
sn_pci_controller_fixup(i, j, NULL);
|
||||
|
||||
/*
|
||||
* Generic Linux PCI Layer has created the pci_bus and pci_dev
|
||||
* structures - time for us to add our SN PLatform specific
|
||||
* information.
|
||||
*/
|
||||
|
||||
#ifndef XEN
|
||||
while ((pci_dev =
|
||||
pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL)
|
||||
sn_pci_fixup_slot(pci_dev);
|
||||
#endif
|
||||
|
||||
sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hubdev_init_node() - Creates the HUB data structure and link them to it's
|
||||
* own NODE specific data area.
|
||||
*/
|
||||
void hubdev_init_node(nodepda_t * npda, cnodeid_t node)
|
||||
{
|
||||
struct hubdev_info *hubdev_info;
|
||||
int size;
|
||||
#ifndef XEN
|
||||
pg_data_t *pg;
|
||||
#else
|
||||
struct pglist_data *pg;
|
||||
#endif
|
||||
|
||||
size = sizeof(struct hubdev_info);
|
||||
|
||||
if (node >= num_online_nodes()) /* Headless/memless IO nodes */
|
||||
pg = NODE_DATA(0);
|
||||
else
|
||||
pg = NODE_DATA(node);
|
||||
|
||||
hubdev_info = (struct hubdev_info *)alloc_bootmem_node(pg, size);
|
||||
|
||||
npda->pdinfo = (void *)hubdev_info;
|
||||
}
|
||||
|
||||
geoid_t
|
||||
cnodeid_get_geoid(cnodeid_t cnode)
|
||||
{
|
||||
struct hubdev_info *hubdev;
|
||||
|
||||
hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
|
||||
return hubdev->hdi_geoid;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
void sn_generate_path(struct pci_bus *pci_bus, char *address)
|
||||
{
|
||||
nasid_t nasid;
|
||||
cnodeid_t cnode;
|
||||
geoid_t geoid;
|
||||
moduleid_t moduleid;
|
||||
u16 bricktype;
|
||||
|
||||
nasid = NASID_GET(SN_PCIBUS_BUSSOFT(pci_bus)->bs_base);
|
||||
cnode = nasid_to_cnodeid(nasid);
|
||||
geoid = cnodeid_get_geoid(cnode);
|
||||
moduleid = geo_module(geoid);
|
||||
|
||||
snprintf(address, 15, "module_%c%c%c%c%.2d",
|
||||
'0'+RACK_GET_CLASS(MODULE_GET_RACK(moduleid)),
|
||||
'0'+RACK_GET_GROUP(MODULE_GET_RACK(moduleid)),
|
||||
'0'+RACK_GET_NUM(MODULE_GET_RACK(moduleid)),
|
||||
MODULE_GET_BTCHAR(moduleid), MODULE_GET_BPOS(moduleid));
|
||||
|
||||
/* Tollhouse requires slot id to be displayed */
|
||||
bricktype = MODULE_GET_BTYPE(moduleid);
|
||||
if ((bricktype == L1_BRICKTYPE_191010) ||
|
||||
(bricktype == L1_BRICKTYPE_1932))
|
||||
snprintf(address, 15+8, "%s^%d", address, geo_slot(geoid));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
__initcall(sn_io_early_init);
|
||||
#else
|
||||
subsys_initcall(sn_io_early_init);
|
||||
#endif
|
||||
#ifndef XEN
|
||||
EXPORT_SYMBOL(sn_pci_fixup_slot);
|
||||
EXPORT_SYMBOL(sn_pci_unfixup_slot);
|
||||
EXPORT_SYMBOL(sn_pci_controller_fixup);
|
||||
EXPORT_SYMBOL(sn_bus_store_sysdata);
|
||||
EXPORT_SYMBOL(sn_bus_free_sysdata);
|
||||
EXPORT_SYMBOL(sn_generate_path);
|
||||
#endif
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#ifndef XEN
|
||||
#include <asm/vga.h>
|
||||
#endif
|
||||
#include <asm/sn/nodepda.h>
|
||||
#include <asm/sn/simulator.h>
|
||||
#include <asm/sn/pda.h>
|
||||
#include <asm/sn/sn_cpuid.h>
|
||||
#include <asm/sn/shub_mmr.h>
|
||||
|
||||
#define IS_LEGACY_VGA_IOPORT(p) \
|
||||
(((p) >= 0x3b0 && (p) <= 0x3bb) || ((p) >= 0x3c0 && (p) <= 0x3df))
|
||||
|
||||
#ifdef XEN
|
||||
#define vga_console_iobase 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sn_io_addr - convert an in/out port to an i/o address
|
||||
* @port: port to convert
|
||||
*
|
||||
* Legacy in/out instructions are converted to ld/st instructions
|
||||
* on IA64. This routine will convert a port number into a valid
|
||||
* SN i/o address. Used by sn_in*() and sn_out*().
|
||||
*/
|
||||
void *sn_io_addr(unsigned long port)
|
||||
{
|
||||
if (!IS_RUNNING_ON_SIMULATOR()) {
|
||||
if (IS_LEGACY_VGA_IOPORT(port))
|
||||
port += vga_console_iobase;
|
||||
/* On sn2, legacy I/O ports don't point at anything */
|
||||
if (port < (64 * 1024))
|
||||
return NULL;
|
||||
return ((void *)(port | __IA64_UNCACHED_OFFSET));
|
||||
} else {
|
||||
/* but the simulator uses them... */
|
||||
unsigned long addr;
|
||||
|
||||
/*
|
||||
* word align port, but need more than 10 bits
|
||||
* for accessing registers in bedrock local block
|
||||
* (so we don't do port&0xfff)
|
||||
*/
|
||||
addr = (is_shub2() ? 0xc00000028c000000UL : 0xc0000087cc000000UL) | ((port >> 2) << 12);
|
||||
if ((port >= 0x1f0 && port <= 0x1f7) || port == 0x3f6 || port == 0x3f7)
|
||||
addr |= port;
|
||||
return (void *)addr;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sn_io_addr);
|
||||
|
||||
/**
|
||||
* __sn_mmiowb - I/O space memory barrier
|
||||
*
|
||||
* See include/asm-ia64/io.h and Documentation/DocBook/deviceiobook.tmpl
|
||||
* for details.
|
||||
*
|
||||
* On SN2, we wait for the PIO_WRITE_STATUS SHub register to clear.
|
||||
* See PV 871084 for details about the WAR about zero value.
|
||||
*
|
||||
*/
|
||||
void __sn_mmiowb(void)
|
||||
{
|
||||
volatile unsigned long *adr = pda->pio_write_status_addr;
|
||||
unsigned long val = pda->pio_write_status_val;
|
||||
|
||||
while ((*adr & SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK) != val)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__sn_mmiowb);
|
||||
@@ -1,576 +0,0 @@
|
||||
/*
|
||||
* Platform dependent support for SGI SN
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/init.h>
|
||||
#ifdef XEN
|
||||
#include <linux/linux-pci.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#endif
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/arch.h>
|
||||
#include <asm/sn/intr.h>
|
||||
#include <asm/sn/pcibr_provider.h>
|
||||
#include <asm/sn/pcibus_provider_defs.h>
|
||||
#ifndef XEN
|
||||
#include <asm/sn/pcidev.h>
|
||||
#endif
|
||||
#include <asm/sn/shub_mmr.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
|
||||
#ifdef XEN
|
||||
#define pci_dev_get(dev) do {} while(0)
|
||||
#define move_native_irq(foo) do {} while(0)
|
||||
#endif
|
||||
|
||||
static void force_interrupt(int irq);
|
||||
#ifndef XEN
|
||||
static void register_intr_pda(struct sn_irq_info *sn_irq_info);
|
||||
static void unregister_intr_pda(struct sn_irq_info *sn_irq_info);
|
||||
#endif
|
||||
|
||||
int sn_force_interrupt_flag = 1;
|
||||
extern int sn_ioif_inited;
|
||||
struct list_head **sn_irq_lh;
|
||||
static DEFINE_SPINLOCK(sn_irq_info_lock); /* non-IRQ lock */
|
||||
|
||||
u64 sn_intr_alloc(nasid_t local_nasid, int local_widget,
|
||||
struct sn_irq_info *sn_irq_info,
|
||||
int req_irq, nasid_t req_nasid,
|
||||
int req_slice)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
|
||||
(u64) SAL_INTR_ALLOC, (u64) local_nasid,
|
||||
(u64) local_widget, __pa(sn_irq_info), (u64) req_irq,
|
||||
(u64) req_nasid, (u64) req_slice);
|
||||
|
||||
return ret_stuff.status;
|
||||
}
|
||||
|
||||
void sn_intr_free(nasid_t local_nasid, int local_widget,
|
||||
struct sn_irq_info *sn_irq_info)
|
||||
{
|
||||
struct ia64_sal_retval ret_stuff;
|
||||
ret_stuff.status = 0;
|
||||
ret_stuff.v0 = 0;
|
||||
|
||||
SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
|
||||
(u64) SAL_INTR_FREE, (u64) local_nasid,
|
||||
(u64) local_widget, (u64) sn_irq_info->irq_irq,
|
||||
(u64) sn_irq_info->irq_cookie, 0, 0);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
static unsigned int sn_startup_irq(unsigned int irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sn_shutdown_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void sn_disable_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void sn_enable_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
static void sn_ack_irq(struct irq_desc *desc)
|
||||
{
|
||||
unsigned int irq = desc->irq;
|
||||
#else
|
||||
static void sn_ack_irq(unsigned int irq)
|
||||
{
|
||||
#endif
|
||||
u64 event_occurred, mask;
|
||||
|
||||
irq = irq & 0xff;
|
||||
event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED));
|
||||
mask = event_occurred & SH_ALL_INT_MASK;
|
||||
HUB_S((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), mask);
|
||||
__set_bit(irq, (volatile void *)pda->sn_in_service_ivecs);
|
||||
|
||||
move_native_irq(irq);
|
||||
}
|
||||
|
||||
#ifdef XEN
|
||||
static void sn_end_irq(struct irq_desc *desc)
|
||||
{
|
||||
unsigned int irq = desc->irq;
|
||||
#else
|
||||
static void sn_end_irq(unsigned int irq)
|
||||
{
|
||||
#endif
|
||||
int ivec;
|
||||
u64 event_occurred;
|
||||
|
||||
ivec = irq & 0xff;
|
||||
if (ivec == SGI_UART_VECTOR) {
|
||||
event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR (SH_EVENT_OCCURRED));
|
||||
/* If the UART bit is set here, we may have received an
|
||||
* interrupt from the UART that the driver missed. To
|
||||
* make sure, we IPI ourselves to force us to look again.
|
||||
*/
|
||||
if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) {
|
||||
platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR,
|
||||
IA64_IPI_DM_INT, 0);
|
||||
}
|
||||
}
|
||||
__clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs);
|
||||
if (sn_force_interrupt_flag)
|
||||
force_interrupt(irq);
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
static void sn_irq_info_free(struct rcu_head *head);
|
||||
|
||||
struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
|
||||
nasid_t nasid, int slice)
|
||||
{
|
||||
int vector;
|
||||
int cpuphys;
|
||||
int64_t bridge;
|
||||
int local_widget, status;
|
||||
nasid_t local_nasid;
|
||||
struct sn_irq_info *new_irq_info;
|
||||
struct sn_pcibus_provider *pci_provider;
|
||||
|
||||
new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
|
||||
if (new_irq_info == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));
|
||||
|
||||
bridge = (u64) new_irq_info->irq_bridge;
|
||||
if (!bridge) {
|
||||
kfree(new_irq_info);
|
||||
return NULL; /* irq is not a device interrupt */
|
||||
}
|
||||
|
||||
local_nasid = NASID_GET(bridge);
|
||||
|
||||
if (local_nasid & 1)
|
||||
local_widget = TIO_SWIN_WIDGETNUM(bridge);
|
||||
else
|
||||
local_widget = SWIN_WIDGETNUM(bridge);
|
||||
|
||||
vector = sn_irq_info->irq_irq;
|
||||
/* Free the old PROM new_irq_info structure */
|
||||
sn_intr_free(local_nasid, local_widget, new_irq_info);
|
||||
/* Update kernels new_irq_info with new target info */
|
||||
unregister_intr_pda(new_irq_info);
|
||||
|
||||
/* allocate a new PROM new_irq_info struct */
|
||||
status = sn_intr_alloc(local_nasid, local_widget,
|
||||
new_irq_info, vector,
|
||||
nasid, slice);
|
||||
|
||||
/* SAL call failed */
|
||||
if (status) {
|
||||
kfree(new_irq_info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpuphys = nasid_slice_to_cpuid(nasid, slice);
|
||||
new_irq_info->irq_cpuid = cpuphys;
|
||||
register_intr_pda(new_irq_info);
|
||||
|
||||
pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];
|
||||
|
||||
/*
|
||||
* If this represents a line interrupt, target it. If it's
|
||||
* an msi (irq_int_bit < 0), it's already targeted.
|
||||
*/
|
||||
if (new_irq_info->irq_int_bit >= 0 &&
|
||||
pci_provider && pci_provider->target_interrupt)
|
||||
(pci_provider->target_interrupt)(new_irq_info);
|
||||
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#ifdef XEN
|
||||
list_replace(&sn_irq_info->list, &new_irq_info->list);
|
||||
#else
|
||||
list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
|
||||
#endif
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
#ifndef XEN
|
||||
call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
set_irq_affinity_info((vector & 0xff), cpuphys, 0);
|
||||
#endif
|
||||
|
||||
return new_irq_info;
|
||||
}
|
||||
|
||||
static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
|
||||
{
|
||||
struct sn_irq_info *sn_irq_info, *sn_irq_info_safe;
|
||||
nasid_t nasid;
|
||||
int slice;
|
||||
|
||||
nasid = cpuid_to_nasid(first_cpu(mask));
|
||||
slice = cpuid_to_slice(first_cpu(mask));
|
||||
|
||||
list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe,
|
||||
sn_irq_lh[irq], list)
|
||||
(void)sn_retarget_vector(sn_irq_info, nasid, slice);
|
||||
}
|
||||
#endif
|
||||
|
||||
static hw_irq_controller irq_type_sn = {
|
||||
#ifndef XEN
|
||||
.name = "SN hub",
|
||||
.startup = sn_startup_irq,
|
||||
.shutdown = sn_shutdown_irq,
|
||||
.enable = sn_enable_irq,
|
||||
.disable = sn_disable_irq,
|
||||
#else
|
||||
.typename = "SN hub",
|
||||
.startup = irq_startup_none,
|
||||
.shutdown = irq_shutdown_none,
|
||||
.enable = irq_enable_none,
|
||||
.disable = irq_disable_none,
|
||||
#endif
|
||||
.ack = sn_ack_irq,
|
||||
.end = sn_end_irq,
|
||||
#ifndef XEN
|
||||
.set_affinity = sn_set_affinity_irq
|
||||
#endif
|
||||
};
|
||||
|
||||
unsigned int sn_local_vector_to_irq(u8 vector)
|
||||
{
|
||||
return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector));
|
||||
}
|
||||
|
||||
void sn_irq_init(void)
|
||||
{
|
||||
int i;
|
||||
irq_desc_t *base_desc = irq_desc;
|
||||
|
||||
#ifndef XEN
|
||||
ia64_first_device_vector = IA64_SN2_FIRST_DEVICE_VECTOR;
|
||||
ia64_last_device_vector = IA64_SN2_LAST_DEVICE_VECTOR;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
#ifdef XEN
|
||||
if (base_desc[i].handler == &no_irq_type) {
|
||||
base_desc[i].handler = &irq_type_sn;
|
||||
#else
|
||||
if (base_desc[i].chip == &no_irq_type) {
|
||||
base_desc[i].chip = &irq_type_sn;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void register_intr_pda(struct sn_irq_info *sn_irq_info)
|
||||
{
|
||||
int irq = sn_irq_info->irq_irq;
|
||||
int cpu = sn_irq_info->irq_cpuid;
|
||||
|
||||
if (pdacpu(cpu)->sn_last_irq < irq) {
|
||||
pdacpu(cpu)->sn_last_irq = irq;
|
||||
}
|
||||
|
||||
if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq)
|
||||
pdacpu(cpu)->sn_first_irq = irq;
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
static void unregister_intr_pda(struct sn_irq_info *sn_irq_info)
|
||||
{
|
||||
int irq = sn_irq_info->irq_irq;
|
||||
int cpu = sn_irq_info->irq_cpuid;
|
||||
struct sn_irq_info *tmp_irq_info;
|
||||
int i, foundmatch;
|
||||
|
||||
#ifndef XEN
|
||||
rcu_read_lock();
|
||||
#else
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#endif
|
||||
if (pdacpu(cpu)->sn_last_irq == irq) {
|
||||
foundmatch = 0;
|
||||
for (i = pdacpu(cpu)->sn_last_irq - 1;
|
||||
i && !foundmatch; i--) {
|
||||
#ifdef XEN
|
||||
list_for_each_entry(tmp_irq_info,
|
||||
sn_irq_lh[i],
|
||||
list) {
|
||||
#else
|
||||
list_for_each_entry_rcu(tmp_irq_info,
|
||||
sn_irq_lh[i],
|
||||
list) {
|
||||
#endif
|
||||
if (tmp_irq_info->irq_cpuid == cpu) {
|
||||
foundmatch = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pdacpu(cpu)->sn_last_irq = i;
|
||||
}
|
||||
|
||||
if (pdacpu(cpu)->sn_first_irq == irq) {
|
||||
foundmatch = 0;
|
||||
for (i = pdacpu(cpu)->sn_first_irq + 1;
|
||||
i < NR_IRQS && !foundmatch; i++) {
|
||||
#ifdef XEN
|
||||
list_for_each_entry(tmp_irq_info,
|
||||
sn_irq_lh[i],
|
||||
list) {
|
||||
#else
|
||||
list_for_each_entry_rcu(tmp_irq_info,
|
||||
sn_irq_lh[i],
|
||||
list) {
|
||||
#endif
|
||||
if (tmp_irq_info->irq_cpuid == cpu) {
|
||||
foundmatch = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i);
|
||||
}
|
||||
#ifndef XEN
|
||||
rcu_read_unlock();
|
||||
#else
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sn_irq_info_free(struct rcu_head *head)
|
||||
{
|
||||
struct sn_irq_info *sn_irq_info;
|
||||
|
||||
sn_irq_info = container_of(head, struct sn_irq_info, rcu);
|
||||
kfree(sn_irq_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
void sn_irq_fixup(struct sn_pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
|
||||
#else
|
||||
void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
|
||||
#endif
|
||||
{
|
||||
nasid_t nasid = sn_irq_info->irq_nasid;
|
||||
int slice = sn_irq_info->irq_slice;
|
||||
int cpu = nasid_slice_to_cpuid(nasid, slice);
|
||||
|
||||
pci_dev_get(pci_dev);
|
||||
sn_irq_info->irq_cpuid = cpu;
|
||||
#ifndef XEN
|
||||
sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);
|
||||
#endif
|
||||
|
||||
/* link it into the sn_irq[irq] list */
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#ifdef XEN
|
||||
list_add(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]);
|
||||
#else
|
||||
list_add_rcu(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]);
|
||||
#endif
|
||||
#ifndef XEN
|
||||
reserve_irq_vector(sn_irq_info->irq_irq);
|
||||
#endif
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
|
||||
register_intr_pda(sn_irq_info);
|
||||
}
|
||||
|
||||
#ifdef XEN
|
||||
void sn_irq_unfixup(struct sn_pci_dev *pci_dev)
|
||||
#else
|
||||
void sn_irq_unfixup(struct pci_dev *pci_dev)
|
||||
#endif
|
||||
{
|
||||
#ifndef XEN
|
||||
struct sn_irq_info *sn_irq_info;
|
||||
|
||||
/* Only cleanup IRQ stuff if this device has a host bus context */
|
||||
if (!SN_PCIDEV_BUSSOFT(pci_dev))
|
||||
return;
|
||||
|
||||
sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info;
|
||||
if (!sn_irq_info)
|
||||
return;
|
||||
if (!sn_irq_info->irq_irq) {
|
||||
kfree(sn_irq_info);
|
||||
return;
|
||||
}
|
||||
|
||||
unregister_intr_pda(sn_irq_info);
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#ifdef XEN
|
||||
list_del(&sn_irq_info->list);
|
||||
#else
|
||||
list_del_rcu(&sn_irq_info->list);
|
||||
#endif
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
if (list_empty(sn_irq_lh[sn_irq_info->irq_irq]))
|
||||
free_irq_vector(sn_irq_info->irq_irq);
|
||||
#ifndef XEN
|
||||
call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
|
||||
#endif
|
||||
pci_dev_put(pci_dev);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
sn_call_force_intr_provider(struct sn_irq_info *sn_irq_info)
|
||||
{
|
||||
struct sn_pcibus_provider *pci_provider;
|
||||
|
||||
pci_provider = sn_pci_provider[sn_irq_info->irq_bridge_type];
|
||||
if (pci_provider && pci_provider->force_interrupt)
|
||||
(*pci_provider->force_interrupt)(sn_irq_info);
|
||||
}
|
||||
|
||||
static void force_interrupt(int irq)
|
||||
{
|
||||
struct sn_irq_info *sn_irq_info;
|
||||
|
||||
#ifndef XEN
|
||||
if (!sn_ioif_inited)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#else
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
#ifdef XEN
|
||||
list_for_each_entry(sn_irq_info, sn_irq_lh[irq], list)
|
||||
#else
|
||||
list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list)
|
||||
#endif
|
||||
sn_call_force_intr_provider(sn_irq_info);
|
||||
|
||||
#ifdef XEN
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
#else
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* Check for lost interrupts. If the PIC int_status reg. says that
|
||||
* an interrupt has been sent, but not handled, and the interrupt
|
||||
* is not pending in either the cpu irr regs or in the soft irr regs,
|
||||
* and the interrupt is not in service, then the interrupt may have
|
||||
* been lost. Force an interrupt on that pin. It is possible that
|
||||
* the interrupt is in flight, so we may generate a spurious interrupt,
|
||||
* but we should never miss a real lost interrupt.
|
||||
*/
|
||||
static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
|
||||
{
|
||||
u64 regval;
|
||||
struct pcidev_info *pcidev_info;
|
||||
struct pcibus_info *pcibus_info;
|
||||
|
||||
/*
|
||||
* Bridge types attached to TIO (anything but PIC) do not need this WAR
|
||||
* since they do not target Shub II interrupt registers. If that
|
||||
* ever changes, this check needs to accomodate.
|
||||
*/
|
||||
if (sn_irq_info->irq_bridge_type != PCIIO_ASIC_TYPE_PIC)
|
||||
return;
|
||||
|
||||
pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
|
||||
if (!pcidev_info)
|
||||
return;
|
||||
|
||||
pcibus_info =
|
||||
(struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
|
||||
pdi_pcibus_info;
|
||||
regval = pcireg_intr_status_get(pcibus_info);
|
||||
|
||||
if (!ia64_get_irr(irq_to_vector(irq))) {
|
||||
if (!test_bit(irq, pda->sn_in_service_ivecs)) {
|
||||
regval &= 0xff;
|
||||
if (sn_irq_info->irq_int_bit & regval &
|
||||
sn_irq_info->irq_last_intr) {
|
||||
regval &= ~(sn_irq_info->irq_int_bit & regval);
|
||||
sn_call_force_intr_provider(sn_irq_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
sn_irq_info->irq_last_intr = regval;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sn_lb_int_war_check(void)
|
||||
{
|
||||
#ifndef XEN
|
||||
struct sn_irq_info *sn_irq_info;
|
||||
int i;
|
||||
|
||||
#ifdef XEN
|
||||
if (pda->sn_first_irq == 0)
|
||||
#else
|
||||
if (!sn_ioif_inited || pda->sn_first_irq == 0)
|
||||
#endif
|
||||
return;
|
||||
|
||||
#ifdef XEN
|
||||
spin_lock(&sn_irq_info_lock);
|
||||
#else
|
||||
rcu_read_lock();
|
||||
#endif
|
||||
for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
|
||||
#ifdef XEN
|
||||
list_for_each_entry(sn_irq_info, sn_irq_lh[i], list) {
|
||||
#else
|
||||
list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) {
|
||||
#endif
|
||||
sn_check_intr(i, sn_irq_info);
|
||||
}
|
||||
}
|
||||
#ifdef XEN
|
||||
spin_unlock(&sn_irq_info_lock);
|
||||
#else
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init sn_irq_lh_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
sn_irq_lh = kmalloc(sizeof(struct list_head *) * NR_IRQS, GFP_KERNEL);
|
||||
if (!sn_irq_lh)
|
||||
panic("SN PCI INIT: Failed to allocate memory for PCI init\n");
|
||||
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
sn_irq_lh[i] = kmalloc(sizeof(struct list_head), GFP_KERNEL);
|
||||
if (!sn_irq_lh[i])
|
||||
panic("SN PCI INIT: Failed IRQ memory allocation\n");
|
||||
|
||||
INIT_LIST_HEAD(sn_irq_lh[i]);
|
||||
}
|
||||
}
|
||||
@@ -1,803 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 1999,2001-2006 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#ifndef XEN
|
||||
#include <linux/kdev_t.h>
|
||||
#endif
|
||||
#include <linux/string.h>
|
||||
#ifndef XEN
|
||||
#include <linux/screen_info.h>
|
||||
#endif
|
||||
#include <linux/console.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/sched.h>
|
||||
#ifndef XEN
|
||||
#include <linux/root_dev.h>
|
||||
#endif
|
||||
#include <linux/nodemask.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/efi.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/processor.h>
|
||||
#ifndef XEN
|
||||
#include <asm/vga.h>
|
||||
#endif
|
||||
#include <asm/sn/arch.h>
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/pda.h>
|
||||
#include <asm/sn/nodepda.h>
|
||||
#include <asm/sn/sn_cpuid.h>
|
||||
#include <asm/sn/simulator.h>
|
||||
#include <asm/sn/leds.h>
|
||||
#ifndef XEN
|
||||
#include <asm/sn/bte.h>
|
||||
#endif
|
||||
#include <asm/sn/shub_mmr.h>
|
||||
#ifndef XEN
|
||||
#include <asm/sn/clksupport.h>
|
||||
#endif
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#include <asm/sn/geo.h>
|
||||
#include <asm/sn/sn_feature_sets.h>
|
||||
#ifndef XEN
|
||||
#include "xtalk/xwidgetdev.h"
|
||||
#include "xtalk/hubdev.h"
|
||||
#else
|
||||
#include "asm/sn/xwidgetdev.h"
|
||||
#include "asm/sn/hubdev.h"
|
||||
#endif
|
||||
#include <asm/sn/klconfig.h>
|
||||
#ifdef XEN
|
||||
#include <asm/sn/shubio.h>
|
||||
#endif
|
||||
|
||||
|
||||
DEFINE_PER_CPU(struct pda_s, pda_percpu);
|
||||
|
||||
#define MAX_PHYS_MEMORY (1UL << IA64_MAX_PHYS_BITS) /* Max physical address supported */
|
||||
|
||||
extern void bte_init_node(nodepda_t *, cnodeid_t);
|
||||
|
||||
extern void sn_timer_init(void);
|
||||
extern unsigned long last_time_offset;
|
||||
extern void (*ia64_mark_idle) (int);
|
||||
extern void snidle(int);
|
||||
extern unsigned long long (*ia64_printk_clock)(void);
|
||||
|
||||
unsigned long sn_rtc_cycles_per_second;
|
||||
EXPORT_SYMBOL(sn_rtc_cycles_per_second);
|
||||
|
||||
DEFINE_PER_CPU(struct sn_hub_info_s, __sn_hub_info);
|
||||
EXPORT_PER_CPU_SYMBOL(__sn_hub_info);
|
||||
|
||||
DEFINE_PER_CPU(short, __sn_cnodeid_to_nasid[MAX_COMPACT_NODES]);
|
||||
EXPORT_PER_CPU_SYMBOL(__sn_cnodeid_to_nasid);
|
||||
|
||||
DEFINE_PER_CPU(struct nodepda_s *, __sn_nodepda);
|
||||
EXPORT_PER_CPU_SYMBOL(__sn_nodepda);
|
||||
|
||||
char sn_system_serial_number_string[128];
|
||||
EXPORT_SYMBOL(sn_system_serial_number_string);
|
||||
u64 sn_partition_serial_number;
|
||||
EXPORT_SYMBOL(sn_partition_serial_number);
|
||||
u8 sn_partition_id;
|
||||
EXPORT_SYMBOL(sn_partition_id);
|
||||
u8 sn_system_size;
|
||||
EXPORT_SYMBOL(sn_system_size);
|
||||
u8 sn_sharing_domain_size;
|
||||
EXPORT_SYMBOL(sn_sharing_domain_size);
|
||||
u8 sn_coherency_id;
|
||||
EXPORT_SYMBOL(sn_coherency_id);
|
||||
u8 sn_region_size;
|
||||
EXPORT_SYMBOL(sn_region_size);
|
||||
int sn_prom_type; /* 0=hardware, 1=medusa/realprom, 2=medusa/fakeprom */
|
||||
|
||||
short physical_node_map[MAX_NUMALINK_NODES];
|
||||
static unsigned long sn_prom_features[MAX_PROM_FEATURE_SETS];
|
||||
|
||||
EXPORT_SYMBOL(physical_node_map);
|
||||
|
||||
int num_cnodes;
|
||||
|
||||
static void sn_init_pdas(char **);
|
||||
static void build_cnode_tables(void);
|
||||
|
||||
static nodepda_t *nodepdaindr[MAX_COMPACT_NODES];
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* The format of "screen_info" is strange, and due to early i386-setup
|
||||
* code. This is just enough to make the console code think we're on a
|
||||
* VGA color display.
|
||||
*/
|
||||
struct screen_info sn_screen_info = {
|
||||
.orig_x = 0,
|
||||
.orig_y = 0,
|
||||
.orig_video_mode = 3,
|
||||
.orig_video_cols = 80,
|
||||
.orig_video_ega_bx = 3,
|
||||
.orig_video_lines = 25,
|
||||
.orig_video_isVGA = 1,
|
||||
.orig_video_points = 16
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine can only be used during init, since
|
||||
* smp_boot_data is an init data structure.
|
||||
* We have to use smp_boot_data.cpu_phys_id to find
|
||||
* the physical id of the processor because the normal
|
||||
* cpu_physical_id() relies on data structures that
|
||||
* may not be initialized yet.
|
||||
*/
|
||||
|
||||
static int __init pxm_to_nasid(int pxm)
|
||||
{
|
||||
int i;
|
||||
int nid;
|
||||
|
||||
nid = pxm_to_node(pxm);
|
||||
for (i = 0; i < num_node_memblks; i++) {
|
||||
if (node_memblk[i].nid == nid) {
|
||||
return NASID_GET(node_memblk[i].start_paddr);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_sn_setup - early setup routine for SN platforms
|
||||
*
|
||||
* Sets up an initial console to aid debugging. Intended primarily
|
||||
* for bringup. See start_kernel() in init/main.c.
|
||||
*/
|
||||
|
||||
void __init early_sn_setup(void)
|
||||
{
|
||||
efi_system_table_t *efi_systab;
|
||||
efi_config_table_t *config_tables;
|
||||
struct ia64_sal_systab *sal_systab;
|
||||
struct ia64_sal_desc_entry_point *ep;
|
||||
char *p;
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
* Parse enough of the SAL tables to locate the SAL entry point. Since, console
|
||||
* IO on SN2 is done via SAL calls, early_printk won't work without this.
|
||||
*
|
||||
* This code duplicates some of the ACPI table parsing that is in efi.c & sal.c.
|
||||
* Any changes to those file may have to be made hereas well.
|
||||
*/
|
||||
efi_systab = (efi_system_table_t *) __va(ia64_boot_param->efi_systab);
|
||||
config_tables = __va(efi_systab->tables);
|
||||
for (i = 0; i < efi_systab->nr_tables; i++) {
|
||||
if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) ==
|
||||
0) {
|
||||
sal_systab = __va(config_tables[i].table);
|
||||
p = (char *)(sal_systab + 1);
|
||||
for (j = 0; j < sal_systab->entry_count; j++) {
|
||||
if (*p == SAL_DESC_ENTRY_POINT) {
|
||||
ep = (struct ia64_sal_desc_entry_point
|
||||
*)p;
|
||||
ia64_sal_handler_init(__va
|
||||
(ep->sal_proc),
|
||||
__va(ep->gp));
|
||||
return;
|
||||
}
|
||||
p += SAL_DESC_SIZE(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Uh-oh, SAL not available?? */
|
||||
printk(KERN_ERR "failed to find SAL entry point\n");
|
||||
}
|
||||
|
||||
extern int platform_intr_list[];
|
||||
static int __initdata shub_1_1_found;
|
||||
|
||||
/*
|
||||
* sn_check_for_wars
|
||||
*
|
||||
* Set flag for enabling shub specific wars
|
||||
*/
|
||||
|
||||
static inline int __init is_shub_1_1(int nasid)
|
||||
{
|
||||
unsigned long id;
|
||||
int rev;
|
||||
|
||||
if (is_shub2())
|
||||
return 0;
|
||||
id = REMOTE_HUB_L(nasid, SH1_SHUB_ID);
|
||||
rev = (id & SH1_SHUB_ID_REVISION_MASK) >> SH1_SHUB_ID_REVISION_SHFT;
|
||||
return rev <= 2;
|
||||
}
|
||||
|
||||
static void __init sn_check_for_wars(void)
|
||||
{
|
||||
int cnode;
|
||||
|
||||
if (is_shub2()) {
|
||||
/* none yet */
|
||||
} else {
|
||||
for_each_online_node(cnode) {
|
||||
if (is_shub_1_1(cnodeid_to_nasid(cnode)))
|
||||
shub_1_1_found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* Scan the EFI PCDP table (if it exists) for an acceptable VGA console
|
||||
* output device. If one exists, pick it and set sn_legacy_{io,mem} to
|
||||
* reflect the bus offsets needed to address it.
|
||||
*
|
||||
* Since pcdp support in SN is not supported in the 2.4 kernel (or at least
|
||||
* the one lbs is based on) just declare the needed structs here.
|
||||
*
|
||||
* Reference spec http://www.dig64.org/specifications/DIG64_PCDPv20.pdf
|
||||
*
|
||||
* Returns 0 if no acceptable vga is found, !0 otherwise.
|
||||
*
|
||||
* Note: This stuff is duped here because Altix requires the PCDP to
|
||||
* locate a usable VGA device due to lack of proper ACPI support. Structures
|
||||
* could be used from drivers/firmware/pcdp.h, but it was decided that moving
|
||||
* this file to a more public location just for Altix use was undesireable.
|
||||
*/
|
||||
|
||||
struct hcdp_uart_desc {
|
||||
u8 pad[45];
|
||||
};
|
||||
|
||||
struct pcdp {
|
||||
u8 signature[4]; /* should be 'HCDP' */
|
||||
u32 length;
|
||||
u8 rev; /* should be >=3 for pcdp, <3 for hcdp */
|
||||
u8 sum;
|
||||
u8 oem_id[6];
|
||||
u64 oem_tableid;
|
||||
u32 oem_rev;
|
||||
u32 creator_id;
|
||||
u32 creator_rev;
|
||||
u32 num_type0;
|
||||
struct hcdp_uart_desc uart[0]; /* num_type0 of these */
|
||||
/* pcdp descriptors follow */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcdp_device_desc {
|
||||
u8 type;
|
||||
u8 primary;
|
||||
u16 length;
|
||||
u16 index;
|
||||
/* interconnect specific structure follows */
|
||||
/* device specific structure follows that */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcdp_interface_pci {
|
||||
u8 type; /* 1 == pci */
|
||||
u8 reserved;
|
||||
u16 length;
|
||||
u8 segment;
|
||||
u8 bus;
|
||||
u8 dev;
|
||||
u8 fun;
|
||||
u16 devid;
|
||||
u16 vendid;
|
||||
u32 acpi_interrupt;
|
||||
u64 mmio_tra;
|
||||
u64 ioport_tra;
|
||||
u8 flags;
|
||||
u8 translation;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcdp_vga_device {
|
||||
u8 num_eas_desc;
|
||||
/* ACPI Extended Address Space Desc follows */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* from pcdp_device_desc.primary */
|
||||
#define PCDP_PRIMARY_CONSOLE 0x01
|
||||
|
||||
/* from pcdp_device_desc.type */
|
||||
#define PCDP_CONSOLE_INOUT 0x0
|
||||
#define PCDP_CONSOLE_DEBUG 0x1
|
||||
#define PCDP_CONSOLE_OUT 0x2
|
||||
#define PCDP_CONSOLE_IN 0x3
|
||||
#define PCDP_CONSOLE_TYPE_VGA 0x8
|
||||
|
||||
#define PCDP_CONSOLE_VGA (PCDP_CONSOLE_TYPE_VGA | PCDP_CONSOLE_OUT)
|
||||
|
||||
/* from pcdp_interface_pci.type */
|
||||
#define PCDP_IF_PCI 1
|
||||
|
||||
/* from pcdp_interface_pci.translation */
|
||||
#define PCDP_PCI_TRANS_IOPORT 0x02
|
||||
#define PCDP_PCI_TRANS_MMIO 0x01
|
||||
|
||||
#if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
|
||||
static void
|
||||
sn_scan_pcdp(void)
|
||||
{
|
||||
u8 *bp;
|
||||
struct pcdp *pcdp;
|
||||
struct pcdp_device_desc device;
|
||||
struct pcdp_interface_pci if_pci;
|
||||
extern struct efi efi;
|
||||
|
||||
if (efi.hcdp == EFI_INVALID_TABLE_ADDR)
|
||||
return; /* no hcdp/pcdp table */
|
||||
|
||||
pcdp = __va(efi.hcdp);
|
||||
|
||||
if (pcdp->rev < 3)
|
||||
return; /* only support PCDP (rev >= 3) */
|
||||
|
||||
for (bp = (u8 *)&pcdp->uart[pcdp->num_type0];
|
||||
bp < (u8 *)pcdp + pcdp->length;
|
||||
bp += device.length) {
|
||||
memcpy(&device, bp, sizeof(device));
|
||||
if (! (device.primary & PCDP_PRIMARY_CONSOLE))
|
||||
continue; /* not primary console */
|
||||
|
||||
if (device.type != PCDP_CONSOLE_VGA)
|
||||
continue; /* not VGA descriptor */
|
||||
|
||||
memcpy(&if_pci, bp+sizeof(device), sizeof(if_pci));
|
||||
if (if_pci.type != PCDP_IF_PCI)
|
||||
continue; /* not PCI interconnect */
|
||||
|
||||
if (if_pci.translation & PCDP_PCI_TRANS_IOPORT)
|
||||
vga_console_iobase =
|
||||
if_pci.ioport_tra | __IA64_UNCACHED_OFFSET;
|
||||
|
||||
if (if_pci.translation & PCDP_PCI_TRANS_MMIO)
|
||||
vga_console_membase =
|
||||
if_pci.mmio_tra | __IA64_UNCACHED_OFFSET;
|
||||
|
||||
break; /* once we find the primary, we're done */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long sn2_rtc_initial;
|
||||
|
||||
static unsigned long long ia64_sn2_printk_clock(void)
|
||||
{
|
||||
unsigned long rtc_now = rtc_time();
|
||||
|
||||
return (rtc_now - sn2_rtc_initial) *
|
||||
(1000000000 / sn_rtc_cycles_per_second);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sn_setup - SN platform setup routine
|
||||
* @cmdline_p: kernel command line
|
||||
*
|
||||
* Handles platform setup for SN machines. This includes determining
|
||||
* the RTC frequency (via a SAL call), initializing secondary CPUs, and
|
||||
* setting up per-node data areas. The console is also initialized here.
|
||||
*/
|
||||
#ifdef XEN
|
||||
void __cpuinit sn_cpu_init(void);
|
||||
#endif
|
||||
|
||||
void __init sn_setup(char **cmdline_p)
|
||||
{
|
||||
#ifndef XEN
|
||||
long status, ticks_per_sec, drift;
|
||||
#else
|
||||
unsigned long status, ticks_per_sec, drift;
|
||||
#endif
|
||||
u32 version = sn_sal_rev();
|
||||
#ifndef XEN
|
||||
extern void sn_cpu_init(void);
|
||||
|
||||
sn2_rtc_initial = rtc_time();
|
||||
ia64_sn_plat_set_error_handling_features(); // obsolete
|
||||
ia64_sn_set_os_feature(OSF_MCA_SLV_TO_OS_INIT_SLV);
|
||||
ia64_sn_set_os_feature(OSF_FEAT_LOG_SBES);
|
||||
|
||||
|
||||
#if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE)
|
||||
/*
|
||||
* Handle SN vga console.
|
||||
*
|
||||
* SN systems do not have enough ACPI table information
|
||||
* being passed from prom to identify VGA adapters and the legacy
|
||||
* addresses to access them. Until that is done, SN systems rely
|
||||
* on the PCDP table to identify the primary VGA console if one
|
||||
* exists.
|
||||
*
|
||||
* However, kernel PCDP support is optional, and even if it is built
|
||||
* into the kernel, it will not be used if the boot cmdline contains
|
||||
* console= directives.
|
||||
*
|
||||
* So, to work around this mess, we duplicate some of the PCDP code
|
||||
* here so that the primary VGA console (as defined by PCDP) will
|
||||
* work on SN systems even if a different console (e.g. serial) is
|
||||
* selected on the boot line (or CONFIG_EFI_PCDP is off).
|
||||
*/
|
||||
|
||||
if (! vga_console_membase)
|
||||
sn_scan_pcdp();
|
||||
|
||||
if (vga_console_membase) {
|
||||
/* usable vga ... make tty0 the preferred default console */
|
||||
if (!strstr(*cmdline_p, "console="))
|
||||
add_preferred_console("tty", 0, NULL);
|
||||
} else {
|
||||
printk(KERN_DEBUG "SGI: Disabling VGA console\n");
|
||||
if (!strstr(*cmdline_p, "console="))
|
||||
add_preferred_console("ttySG", 0, NULL);
|
||||
#ifdef CONFIG_DUMMY_CONSOLE
|
||||
conswitchp = &dummy_con;
|
||||
#else
|
||||
conswitchp = NULL;
|
||||
#endif /* CONFIG_DUMMY_CONSOLE */
|
||||
}
|
||||
#endif /* def(CONFIG_VT) && def(CONFIG_VGA_CONSOLE) */
|
||||
|
||||
MAX_DMA_ADDRESS = PAGE_OFFSET + MAX_PHYS_MEMORY;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Build the tables for managing cnodes.
|
||||
*/
|
||||
build_cnode_tables();
|
||||
|
||||
status =
|
||||
ia64_sal_freq_base(SAL_FREQ_BASE_REALTIME_CLOCK, &ticks_per_sec,
|
||||
&drift);
|
||||
if (status != 0 || ticks_per_sec < 100000) {
|
||||
printk(KERN_WARNING
|
||||
"unable to determine platform RTC clock frequency, guessing.\n");
|
||||
/* PROM gives wrong value for clock freq. so guess */
|
||||
sn_rtc_cycles_per_second = 1000000000000UL / 30000UL;
|
||||
} else
|
||||
sn_rtc_cycles_per_second = ticks_per_sec;
|
||||
#ifndef XEN
|
||||
|
||||
platform_intr_list[ACPI_INTERRUPT_CPEI] = IA64_CPE_VECTOR;
|
||||
|
||||
ia64_printk_clock = ia64_sn2_printk_clock;
|
||||
#endif
|
||||
|
||||
printk("SGI SAL version %x.%02x\n", version >> 8, version & 0x00FF);
|
||||
|
||||
/*
|
||||
* we set the default root device to /dev/hda
|
||||
* to make simulation easy
|
||||
*/
|
||||
#ifndef XEN
|
||||
ROOT_DEV = Root_HDA1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create the PDAs and NODEPDAs for all the cpus.
|
||||
*/
|
||||
sn_init_pdas(cmdline_p);
|
||||
|
||||
#ifndef XEN
|
||||
ia64_mark_idle = &snidle;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For the bootcpu, we do this here. All other cpus will make the
|
||||
* call as part of cpu_init in slave cpu initialization.
|
||||
*/
|
||||
sn_cpu_init();
|
||||
|
||||
#ifndef XEN
|
||||
#ifdef CONFIG_SMP
|
||||
init_smp_config();
|
||||
#endif
|
||||
screen_info = sn_screen_info;
|
||||
|
||||
sn_timer_init();
|
||||
|
||||
/*
|
||||
* set pm_power_off to a SAL call to allow
|
||||
* sn machines to power off. The SAL call can be replaced
|
||||
* by an ACPI interface call when ACPI is fully implemented
|
||||
* for sn.
|
||||
*/
|
||||
pm_power_off = ia64_sn_power_down;
|
||||
current->thread.flags |= IA64_THREAD_MIGRATION;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* sn_init_pdas - setup node data areas
|
||||
*
|
||||
* One time setup for Node Data Area. Called by sn_setup().
|
||||
*/
|
||||
static void __init sn_init_pdas(char **cmdline_p)
|
||||
{
|
||||
cnodeid_t cnode;
|
||||
|
||||
/*
|
||||
* Allocate & initalize the nodepda for each node.
|
||||
*/
|
||||
for_each_online_node(cnode) {
|
||||
nodepdaindr[cnode] =
|
||||
alloc_bootmem_node(NODE_DATA(cnode), sizeof(nodepda_t));
|
||||
memset(nodepdaindr[cnode], 0, sizeof(nodepda_t));
|
||||
memset(nodepdaindr[cnode]->phys_cpuid, -1,
|
||||
sizeof(nodepdaindr[cnode]->phys_cpuid));
|
||||
spin_lock_init(&nodepdaindr[cnode]->ptc_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate & initialize nodepda for TIOs. For now, put them on node 0.
|
||||
*/
|
||||
for (cnode = num_online_nodes(); cnode < num_cnodes; cnode++) {
|
||||
nodepdaindr[cnode] =
|
||||
alloc_bootmem_node(NODE_DATA(0), sizeof(nodepda_t));
|
||||
memset(nodepdaindr[cnode], 0, sizeof(nodepda_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Now copy the array of nodepda pointers to each nodepda.
|
||||
*/
|
||||
for (cnode = 0; cnode < num_cnodes; cnode++)
|
||||
memcpy(nodepdaindr[cnode]->pernode_pdaindr, nodepdaindr,
|
||||
sizeof(nodepdaindr));
|
||||
|
||||
#ifndef XEN
|
||||
/*
|
||||
* Set up IO related platform-dependent nodepda fields.
|
||||
* The following routine actually sets up the hubinfo struct
|
||||
* in nodepda.
|
||||
*/
|
||||
for_each_online_node(cnode) {
|
||||
bte_init_node(nodepdaindr[cnode], cnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the per node hubdev. This includes IO Nodes and
|
||||
* headless/memless nodes.
|
||||
*/
|
||||
for (cnode = 0; cnode < num_cnodes; cnode++) {
|
||||
hubdev_init_node(nodepdaindr[cnode], cnode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* sn_cpu_init - initialize per-cpu data areas
|
||||
* @cpuid: cpuid of the caller
|
||||
*
|
||||
* Called during cpu initialization on each cpu as it starts.
|
||||
* Currently, initializes the per-cpu data area for SNIA.
|
||||
* Also sets up a few fields in the nodepda. Also known as
|
||||
* platform_cpu_init() by the ia64 machvec code.
|
||||
*/
|
||||
void __cpuinit sn_cpu_init(void)
|
||||
{
|
||||
int cpuid;
|
||||
int cpuphyid;
|
||||
int nasid;
|
||||
int subnode;
|
||||
int slice;
|
||||
int cnode;
|
||||
int i;
|
||||
static int wars_have_been_checked;
|
||||
|
||||
cpuid = smp_processor_id();
|
||||
#ifndef XEN
|
||||
if (cpuid == 0 && IS_MEDUSA()) {
|
||||
if (ia64_sn_is_fake_prom())
|
||||
sn_prom_type = 2;
|
||||
else
|
||||
sn_prom_type = 1;
|
||||
printk(KERN_INFO "Running on medusa with %s PROM\n",
|
||||
(sn_prom_type == 1) ? "real" : "fake");
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(pda, 0, sizeof(pda));
|
||||
if (ia64_sn_get_sn_info(0, &sn_hub_info->shub2,
|
||||
&sn_hub_info->nasid_bitmask,
|
||||
&sn_hub_info->nasid_shift,
|
||||
&sn_system_size, &sn_sharing_domain_size,
|
||||
&sn_partition_id, &sn_coherency_id,
|
||||
&sn_region_size))
|
||||
BUG();
|
||||
sn_hub_info->as_shift = sn_hub_info->nasid_shift - 2;
|
||||
|
||||
/*
|
||||
* Don't check status. The SAL call is not supported on all PROMs
|
||||
* but a failure is harmless.
|
||||
*/
|
||||
(void) ia64_sn_set_cpu_number(cpuid);
|
||||
|
||||
/*
|
||||
* The boot cpu makes this call again after platform initialization is
|
||||
* complete.
|
||||
*/
|
||||
if (nodepdaindr[0] == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PROM_FEATURE_SETS; i++)
|
||||
if (ia64_sn_get_prom_feature_set(i, &sn_prom_features[i]) != 0)
|
||||
break;
|
||||
|
||||
cpuphyid = get_sapicid();
|
||||
|
||||
if (ia64_sn_get_sapic_info(cpuphyid, &nasid, &subnode, &slice))
|
||||
BUG();
|
||||
|
||||
for (i=0; i < MAX_NUMNODES; i++) {
|
||||
if (nodepdaindr[i]) {
|
||||
nodepdaindr[i]->phys_cpuid[cpuid].nasid = nasid;
|
||||
nodepdaindr[i]->phys_cpuid[cpuid].slice = slice;
|
||||
nodepdaindr[i]->phys_cpuid[cpuid].subnode = subnode;
|
||||
}
|
||||
}
|
||||
|
||||
cnode = nasid_to_cnodeid(nasid);
|
||||
|
||||
sn_nodepda = nodepdaindr[cnode];
|
||||
|
||||
pda->led_address =
|
||||
(typeof(pda->led_address)) (LED0 + (slice << LED_CPU_SHIFT));
|
||||
pda->led_state = LED_ALWAYS_SET;
|
||||
pda->hb_count = HZ / 2;
|
||||
pda->hb_state = 0;
|
||||
pda->idle_flag = 0;
|
||||
|
||||
if (cpuid != 0) {
|
||||
/* copy cpu 0's sn_cnodeid_to_nasid table to this cpu's */
|
||||
memcpy(sn_cnodeid_to_nasid,
|
||||
(&per_cpu(__sn_cnodeid_to_nasid, 0)),
|
||||
sizeof(__ia64_per_cpu_var(__sn_cnodeid_to_nasid)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for WARs.
|
||||
* Only needs to be done once, on BSP.
|
||||
* Has to be done after loop above, because it uses this cpu's
|
||||
* sn_cnodeid_to_nasid table which was just initialized if this
|
||||
* isn't cpu 0.
|
||||
* Has to be done before assignment below.
|
||||
*/
|
||||
if (!wars_have_been_checked) {
|
||||
sn_check_for_wars();
|
||||
wars_have_been_checked = 1;
|
||||
}
|
||||
sn_hub_info->shub_1_1_found = shub_1_1_found;
|
||||
|
||||
/*
|
||||
* Set up addresses of PIO/MEM write status registers.
|
||||
*/
|
||||
{
|
||||
u64 pio1[] = {SH1_PIO_WRITE_STATUS_0, 0, SH1_PIO_WRITE_STATUS_1, 0};
|
||||
u64 pio2[] = {SH2_PIO_WRITE_STATUS_0, SH2_PIO_WRITE_STATUS_2,
|
||||
SH2_PIO_WRITE_STATUS_1, SH2_PIO_WRITE_STATUS_3};
|
||||
u64 *pio;
|
||||
pio = is_shub1() ? pio1 : pio2;
|
||||
pda->pio_write_status_addr =
|
||||
(volatile unsigned long *)GLOBAL_MMR_ADDR(nasid, pio[slice]);
|
||||
pda->pio_write_status_val = is_shub1() ? SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK : 0;
|
||||
}
|
||||
|
||||
#ifndef XEN /* local_node_data is not allocated .... yet */
|
||||
/*
|
||||
* WAR addresses for SHUB 1.x.
|
||||
*/
|
||||
if (local_node_data->active_cpu_count++ == 0 && is_shub1()) {
|
||||
int buddy_nasid;
|
||||
buddy_nasid =
|
||||
cnodeid_to_nasid(numa_node_id() ==
|
||||
num_online_nodes() - 1 ? 0 : numa_node_id() + 1);
|
||||
pda->pio_shub_war_cam_addr =
|
||||
(volatile unsigned long *)GLOBAL_MMR_ADDR(nasid,
|
||||
SH1_PI_CAM_CONTROL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Build tables for converting between NASIDs and cnodes.
|
||||
*/
|
||||
static inline int __init board_needs_cnode(int type)
|
||||
{
|
||||
return (type == KLTYPE_SNIA || type == KLTYPE_TIO);
|
||||
}
|
||||
|
||||
void __init build_cnode_tables(void)
|
||||
{
|
||||
int nasid;
|
||||
int node;
|
||||
lboard_t *brd;
|
||||
|
||||
memset(physical_node_map, -1, sizeof(physical_node_map));
|
||||
memset(sn_cnodeid_to_nasid, -1,
|
||||
sizeof(__ia64_per_cpu_var(__sn_cnodeid_to_nasid)));
|
||||
|
||||
/*
|
||||
* First populate the tables with C/M bricks. This ensures that
|
||||
* cnode == node for all C & M bricks.
|
||||
*/
|
||||
for_each_online_node(node) {
|
||||
nasid = pxm_to_nasid(node_to_pxm(node));
|
||||
sn_cnodeid_to_nasid[node] = nasid;
|
||||
physical_node_map[nasid] = node;
|
||||
}
|
||||
|
||||
/*
|
||||
* num_cnodes is total number of C/M/TIO bricks. Because of the 256 node
|
||||
* limit on the number of nodes, we can't use the generic node numbers
|
||||
* for this. Note that num_cnodes is incremented below as TIOs or
|
||||
* headless/memoryless nodes are discovered.
|
||||
*/
|
||||
num_cnodes = num_online_nodes();
|
||||
|
||||
/* fakeprom does not support klgraph */
|
||||
if (IS_RUNNING_ON_FAKE_PROM())
|
||||
return;
|
||||
|
||||
/* Find TIOs & headless/memoryless nodes and add them to the tables */
|
||||
for_each_online_node(node) {
|
||||
kl_config_hdr_t *klgraph_header;
|
||||
nasid = cnodeid_to_nasid(node);
|
||||
klgraph_header = ia64_sn_get_klconfig_addr(nasid);
|
||||
if (klgraph_header == NULL)
|
||||
BUG();
|
||||
brd = NODE_OFFSET_TO_LBOARD(nasid, klgraph_header->ch_board_info);
|
||||
while (brd) {
|
||||
if (board_needs_cnode(brd->brd_type) && physical_node_map[brd->brd_nasid] < 0) {
|
||||
sn_cnodeid_to_nasid[num_cnodes] = brd->brd_nasid;
|
||||
physical_node_map[brd->brd_nasid] = num_cnodes++;
|
||||
}
|
||||
brd = find_lboard_next(brd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nasid_slice_to_cpuid(int nasid, int slice)
|
||||
{
|
||||
long cpu;
|
||||
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++)
|
||||
if (cpuid_to_nasid(cpu) == nasid &&
|
||||
cpuid_to_slice(cpu) == slice)
|
||||
return cpu;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sn_prom_feature_available(int id)
|
||||
{
|
||||
if (id >= BITS_PER_LONG * MAX_PROM_FEATURE_SETS)
|
||||
return 0;
|
||||
return test_bit(id, sn_prom_features);
|
||||
}
|
||||
EXPORT_SYMBOL(sn_prom_feature_available);
|
||||
|
||||
@@ -1,621 +0,0 @@
|
||||
/*
|
||||
* SN2 Platform specific SMP Support
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2000-2006 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mmzone.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/nodemask.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <asm/numa.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/current.h>
|
||||
#ifdef XEN
|
||||
#include <asm/sn/arch.h>
|
||||
#endif
|
||||
#include <asm/sn/sn_cpuid.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/shub_mmr.h>
|
||||
#include <asm/sn/nodepda.h>
|
||||
#include <asm/sn/rw_mmr.h>
|
||||
|
||||
DEFINE_PER_CPU(struct ptc_stats, ptcstats);
|
||||
DECLARE_PER_CPU(struct ptc_stats, ptcstats);
|
||||
|
||||
static __cacheline_aligned DEFINE_SPINLOCK(sn2_global_ptc_lock);
|
||||
|
||||
extern unsigned long
|
||||
sn2_ptc_deadlock_recovery_core(volatile unsigned long *, unsigned long,
|
||||
volatile unsigned long *, unsigned long,
|
||||
volatile unsigned long *, unsigned long);
|
||||
void
|
||||
sn2_ptc_deadlock_recovery(short *, short, short, int,
|
||||
volatile unsigned long *, unsigned long,
|
||||
volatile unsigned long *, unsigned long);
|
||||
|
||||
/*
|
||||
* Note: some is the following is captured here to make degugging easier
|
||||
* (the macros make more sense if you see the debug patch - not posted)
|
||||
*/
|
||||
#define sn2_ptctest 0
|
||||
#define local_node_uses_ptc_ga(sh1) ((sh1) ? 1 : 0)
|
||||
#define max_active_pio(sh1) ((sh1) ? 32 : 7)
|
||||
#define reset_max_active_on_deadlock() 1
|
||||
#ifndef XEN
|
||||
#define PTC_LOCK(sh1) ((sh1) ? &sn2_global_ptc_lock : &sn_nodepda->ptc_lock)
|
||||
#else
|
||||
#define PTC_LOCK(sh1) &sn2_global_ptc_lock
|
||||
#endif
|
||||
|
||||
struct ptc_stats {
|
||||
unsigned long ptc_l;
|
||||
unsigned long change_rid;
|
||||
unsigned long shub_ptc_flushes;
|
||||
unsigned long nodes_flushed;
|
||||
unsigned long deadlocks;
|
||||
unsigned long deadlocks2;
|
||||
unsigned long lock_itc_clocks;
|
||||
unsigned long shub_itc_clocks;
|
||||
unsigned long shub_itc_clocks_max;
|
||||
unsigned long shub_ptc_flushes_not_my_mm;
|
||||
};
|
||||
|
||||
#define sn2_ptctest 0
|
||||
|
||||
static inline unsigned long wait_piowc(void)
|
||||
{
|
||||
volatile unsigned long *piows;
|
||||
unsigned long zeroval, ws;
|
||||
|
||||
piows = pda->pio_write_status_addr;
|
||||
zeroval = pda->pio_write_status_val;
|
||||
do {
|
||||
cpu_relax();
|
||||
} while (((ws = *piows) & SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK) != zeroval);
|
||||
return (ws & SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_MASK) != 0;
|
||||
}
|
||||
|
||||
#ifndef XEN /* No idea if Xen will ever support this */
|
||||
/**
|
||||
* sn_migrate - SN-specific task migration actions
|
||||
* @task: Task being migrated to new CPU
|
||||
*
|
||||
* SN2 PIO writes from separate CPUs are not guaranteed to arrive in order.
|
||||
* Context switching user threads which have memory-mapped MMIO may cause
|
||||
* PIOs to issue from seperate CPUs, thus the PIO writes must be drained
|
||||
* from the previous CPU's Shub before execution resumes on the new CPU.
|
||||
*/
|
||||
void sn_migrate(struct task_struct *task)
|
||||
{
|
||||
pda_t *last_pda = pdacpu(task_thread_info(task)->last_cpu);
|
||||
volatile unsigned long *adr = last_pda->pio_write_status_addr;
|
||||
unsigned long val = last_pda->pio_write_status_val;
|
||||
|
||||
/* Drain PIO writes from old CPU's Shub */
|
||||
while (unlikely((*adr & SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK)
|
||||
!= val))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
void sn_tlb_migrate_finish(struct mm_struct *mm)
|
||||
{
|
||||
/* flush_tlb_mm is inefficient if more than 1 users of mm */
|
||||
#ifndef XEN
|
||||
if (mm == current->mm && mm && atomic_read(&mm->mm_users) == 1)
|
||||
#else
|
||||
if (mm == ¤t->arch.mm && mm && atomic_read(&mm->mm_users) == 1)
|
||||
#endif
|
||||
flush_tlb_mm(mm);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sn2_global_tlb_purge - globally purge translation cache of virtual address range
|
||||
* @mm: mm_struct containing virtual address range
|
||||
* @start: start of virtual address range
|
||||
* @end: end of virtual address range
|
||||
* @nbits: specifies number of bytes to purge per instruction (num = 1<<(nbits & 0xfc))
|
||||
*
|
||||
* Purges the translation caches of all processors of the given virtual address
|
||||
* range.
|
||||
*
|
||||
* Note:
|
||||
* - cpu_vm_mask is a bit mask that indicates which cpus have loaded the context.
|
||||
* - cpu_vm_mask is converted into a nodemask of the nodes containing the
|
||||
* cpus in cpu_vm_mask.
|
||||
* - if only one bit is set in cpu_vm_mask & it is the current cpu & the
|
||||
* process is purging its own virtual address range, then only the
|
||||
* local TLB needs to be flushed. This flushing can be done using
|
||||
* ptc.l. This is the common case & avoids the global spinlock.
|
||||
* - if multiple cpus have loaded the context, then flushing has to be
|
||||
* done with ptc.g/MMRs under protection of the global ptc_lock.
|
||||
*/
|
||||
|
||||
#ifdef XEN /* Xen is soooooooo stupid! */
|
||||
// static cpumask_t mask_all = CPU_MASK_ALL;
|
||||
#endif
|
||||
|
||||
#ifdef XEN
|
||||
static DEFINE_SPINLOCK(sn2_ptcg_lock);
|
||||
|
||||
struct sn_flush_struct {
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
unsigned long nbits;
|
||||
};
|
||||
|
||||
static void sn_flush_ptcga_cpu(void *ptr)
|
||||
{
|
||||
struct sn_flush_struct *sn_flush = ptr;
|
||||
unsigned long start, end, nbits;
|
||||
|
||||
start = sn_flush->start;
|
||||
end = sn_flush->end;
|
||||
nbits = sn_flush->nbits;
|
||||
|
||||
/*
|
||||
* Contention me harder!!!
|
||||
*/
|
||||
/* HW requires global serialization of ptc.ga. */
|
||||
spin_lock(&sn2_ptcg_lock);
|
||||
{
|
||||
do {
|
||||
/*
|
||||
* Flush ALAT entries also.
|
||||
*/
|
||||
ia64_ptcga(start, (nbits<<2));
|
||||
ia64_srlz_i();
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
}
|
||||
spin_unlock(&sn2_ptcg_lock);
|
||||
}
|
||||
|
||||
void
|
||||
sn2_global_tlb_purge(unsigned long start,
|
||||
unsigned long end, unsigned long nbits)
|
||||
{
|
||||
nodemask_t nodes_flushed;
|
||||
cpumask_t selected_cpus;
|
||||
int cpu, cnode, i;
|
||||
static DEFINE_SPINLOCK(sn2_ptcg_lock2);
|
||||
|
||||
nodes_clear(nodes_flushed);
|
||||
cpumask_clear(&selected_cpus);
|
||||
|
||||
spin_lock(&sn2_ptcg_lock2);
|
||||
node_set(cpu_to_node(smp_processor_id()), nodes_flushed);
|
||||
i = 0;
|
||||
for_each_possible_cpu(cpu) {
|
||||
cnode = cpu_to_node(cpu);
|
||||
if (!node_isset(cnode, nodes_flushed)) {
|
||||
cpumask_set_cpu(cpu, &selected_cpus);
|
||||
i++;
|
||||
}
|
||||
node_set(cnode, nodes_flushed);
|
||||
}
|
||||
|
||||
/* HW requires global serialization of ptc.ga. */
|
||||
spin_lock(&sn2_ptcg_lock);
|
||||
{
|
||||
do {
|
||||
/*
|
||||
* Flush ALAT entries also.
|
||||
*/
|
||||
ia64_ptcga(start, (nbits<<2));
|
||||
ia64_srlz_i();
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
}
|
||||
spin_unlock(&sn2_ptcg_lock);
|
||||
|
||||
if (i) {
|
||||
struct sn_flush_struct flush_data;
|
||||
flush_data.start = start;
|
||||
flush_data.end = end;
|
||||
flush_data.nbits = nbits;
|
||||
on_selected_cpus(&selected_cpus, sn_flush_ptcga_cpu,
|
||||
&flush_data, 1);
|
||||
}
|
||||
spin_unlock(&sn2_ptcg_lock2);
|
||||
}
|
||||
#else
|
||||
void
|
||||
sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end, unsigned long nbits)
|
||||
{
|
||||
int i, ibegin, shub1, cnode, mynasid, cpu, lcpu = 0, nasid;
|
||||
int mymm = (mm == current->active_mm && mm == current->mm);
|
||||
int use_cpu_ptcga;
|
||||
volatile unsigned long *ptc0, *ptc1;
|
||||
unsigned long itc, itc2, flags, data0 = 0, data1 = 0, rr_value, old_rr = 0;
|
||||
short nasids[MAX_NUMNODES], nix;
|
||||
nodemask_t nodes_flushed;
|
||||
int active, max_active, deadlock;
|
||||
|
||||
nodes_clear(nodes_flushed);
|
||||
i = 0;
|
||||
|
||||
#ifndef XEN /* One day Xen will grow up! */
|
||||
for_each_cpu_mask(cpu, mm->cpu_vm_mask) {
|
||||
cnode = cpu_to_node(cpu);
|
||||
node_set(cnode, nodes_flushed);
|
||||
lcpu = cpu;
|
||||
i++;
|
||||
}
|
||||
#else
|
||||
for_each_possible_cpu(cpu) {
|
||||
cnode = cpu_to_node(cpu);
|
||||
node_set(cnode, nodes_flushed);
|
||||
lcpu = cpu;
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i == 0)
|
||||
return;
|
||||
|
||||
preempt_disable();
|
||||
|
||||
#ifndef XEN
|
||||
if (likely(i == 1 && lcpu == smp_processor_id() && mymm)) {
|
||||
do {
|
||||
ia64_ptcl(start, nbits << 2);
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
ia64_srlz_i();
|
||||
__get_cpu_var(ptcstats).ptc_l++;
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_read(&mm->mm_users) == 1 && mymm) {
|
||||
flush_tlb_mm(mm);
|
||||
__get_cpu_var(ptcstats).change_rid++;
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
itc = ia64_get_itc();
|
||||
nix = 0;
|
||||
for_each_node_mask(cnode, nodes_flushed)
|
||||
nasids[nix++] = cnodeid_to_nasid(cnode);
|
||||
|
||||
#ifndef XEN
|
||||
rr_value = (mm->context << 3) | REGION_NUMBER(start);
|
||||
#else
|
||||
rr_value = REGION_NUMBER(start);
|
||||
#endif
|
||||
|
||||
shub1 = is_shub1();
|
||||
if (shub1) {
|
||||
data0 = (1UL << SH1_PTC_0_A_SHFT) |
|
||||
(nbits << SH1_PTC_0_PS_SHFT) |
|
||||
(rr_value << SH1_PTC_0_RID_SHFT) |
|
||||
(1UL << SH1_PTC_0_START_SHFT);
|
||||
#ifndef XEN
|
||||
ptc0 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_0);
|
||||
ptc1 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_1);
|
||||
#else
|
||||
ptc0 = (unsigned long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_0);
|
||||
ptc1 = (unsigned long *)GLOBAL_MMR_PHYS_ADDR(0, SH1_PTC_1);
|
||||
#endif
|
||||
} else {
|
||||
data0 = (1UL << SH2_PTC_A_SHFT) |
|
||||
(nbits << SH2_PTC_PS_SHFT) |
|
||||
(1UL << SH2_PTC_START_SHFT);
|
||||
#ifndef XEN
|
||||
ptc0 = (long *)GLOBAL_MMR_PHYS_ADDR(0, SH2_PTC +
|
||||
#else
|
||||
ptc0 = (unsigned long *)GLOBAL_MMR_PHYS_ADDR(0, SH2_PTC +
|
||||
#endif
|
||||
(rr_value << SH2_PTC_RID_SHFT));
|
||||
ptc1 = NULL;
|
||||
}
|
||||
|
||||
|
||||
mynasid = get_nasid();
|
||||
use_cpu_ptcga = local_node_uses_ptc_ga(shub1);
|
||||
max_active = max_active_pio(shub1);
|
||||
|
||||
itc = ia64_get_itc();
|
||||
spin_lock_irqsave(PTC_LOCK(shub1), flags);
|
||||
itc2 = ia64_get_itc();
|
||||
|
||||
__get_cpu_var(ptcstats).lock_itc_clocks += itc2 - itc;
|
||||
__get_cpu_var(ptcstats).shub_ptc_flushes++;
|
||||
__get_cpu_var(ptcstats).nodes_flushed += nix;
|
||||
if (!mymm)
|
||||
__get_cpu_var(ptcstats).shub_ptc_flushes_not_my_mm++;
|
||||
|
||||
if (use_cpu_ptcga && !mymm) {
|
||||
old_rr = ia64_get_rr(start);
|
||||
ia64_set_rr(start, (old_rr & 0xff) | (rr_value << 8));
|
||||
ia64_srlz_d();
|
||||
}
|
||||
|
||||
wait_piowc();
|
||||
do {
|
||||
if (shub1)
|
||||
data1 = start | (1UL << SH1_PTC_1_START_SHFT);
|
||||
else
|
||||
data0 = (data0 & ~SH2_PTC_ADDR_MASK) | (start & SH2_PTC_ADDR_MASK);
|
||||
deadlock = 0;
|
||||
active = 0;
|
||||
for (ibegin = 0, i = 0; i < nix; i++) {
|
||||
nasid = nasids[i];
|
||||
if (use_cpu_ptcga && unlikely(nasid == mynasid)) {
|
||||
ia64_ptcga(start, nbits << 2);
|
||||
ia64_srlz_i();
|
||||
} else {
|
||||
ptc0 = CHANGE_NASID(nasid, ptc0);
|
||||
if (ptc1)
|
||||
ptc1 = CHANGE_NASID(nasid, ptc1);
|
||||
pio_atomic_phys_write_mmrs(ptc0, data0, ptc1, data1);
|
||||
active++;
|
||||
}
|
||||
if (active >= max_active || i == (nix - 1)) {
|
||||
if ((deadlock = wait_piowc())) {
|
||||
sn2_ptc_deadlock_recovery(nasids, ibegin, i, mynasid, ptc0, data0, ptc1, data1);
|
||||
if (reset_max_active_on_deadlock())
|
||||
max_active = 1;
|
||||
}
|
||||
active = 0;
|
||||
ibegin = i + 1;
|
||||
}
|
||||
}
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
|
||||
itc2 = ia64_get_itc() - itc2;
|
||||
__get_cpu_var(ptcstats).shub_itc_clocks += itc2;
|
||||
if (itc2 > __get_cpu_var(ptcstats).shub_itc_clocks_max)
|
||||
__get_cpu_var(ptcstats).shub_itc_clocks_max = itc2;
|
||||
|
||||
if (old_rr) {
|
||||
ia64_set_rr(start, old_rr);
|
||||
ia64_srlz_d();
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(PTC_LOCK(shub1), flags);
|
||||
|
||||
preempt_enable();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sn2_ptc_deadlock_recovery
|
||||
*
|
||||
* Recover from PTC deadlocks conditions. Recovery requires stepping thru each
|
||||
* TLB flush transaction. The recovery sequence is somewhat tricky & is
|
||||
* coded in assembly language.
|
||||
*/
|
||||
|
||||
void
|
||||
sn2_ptc_deadlock_recovery(short *nasids, short ib, short ie, int mynasid,
|
||||
volatile unsigned long *ptc0, unsigned long data0,
|
||||
volatile unsigned long *ptc1, unsigned long data1)
|
||||
{
|
||||
short nasid, i;
|
||||
unsigned long *piows, zeroval, n;
|
||||
|
||||
__get_cpu_var(ptcstats).deadlocks++;
|
||||
|
||||
piows = (unsigned long *) pda->pio_write_status_addr;
|
||||
zeroval = pda->pio_write_status_val;
|
||||
|
||||
|
||||
for (i=ib; i <= ie; i++) {
|
||||
nasid = nasids[i];
|
||||
if (local_node_uses_ptc_ga(is_shub1()) && nasid == mynasid)
|
||||
continue;
|
||||
ptc0 = CHANGE_NASID(nasid, ptc0);
|
||||
if (ptc1)
|
||||
ptc1 = CHANGE_NASID(nasid, ptc1);
|
||||
|
||||
n = sn2_ptc_deadlock_recovery_core(ptc0, data0, ptc1, data1, piows, zeroval);
|
||||
__get_cpu_var(ptcstats).deadlocks2 += n;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* sn_send_IPI_phys - send an IPI to a Nasid and slice
|
||||
* @nasid: nasid to receive the interrupt (may be outside partition)
|
||||
* @physid: physical cpuid to receive the interrupt.
|
||||
* @vector: command to send
|
||||
* @delivery_mode: delivery mechanism
|
||||
*
|
||||
* Sends an IPI (interprocessor interrupt) to the processor specified by
|
||||
* @physid
|
||||
*
|
||||
* @delivery_mode can be one of the following
|
||||
*
|
||||
* %IA64_IPI_DM_INT - pend an interrupt
|
||||
* %IA64_IPI_DM_PMI - pend a PMI
|
||||
* %IA64_IPI_DM_NMI - pend an NMI
|
||||
* %IA64_IPI_DM_INIT - pend an INIT interrupt
|
||||
*/
|
||||
void sn_send_IPI_phys(int nasid, long physid, int vector, int delivery_mode)
|
||||
{
|
||||
long val;
|
||||
unsigned long flags = 0;
|
||||
volatile long *p;
|
||||
|
||||
p = (long *)GLOBAL_MMR_PHYS_ADDR(nasid, SH_IPI_INT);
|
||||
val = (1UL << SH_IPI_INT_SEND_SHFT) |
|
||||
(physid << SH_IPI_INT_PID_SHFT) |
|
||||
((long)delivery_mode << SH_IPI_INT_TYPE_SHFT) |
|
||||
((long)vector << SH_IPI_INT_IDX_SHFT) |
|
||||
(0x000feeUL << SH_IPI_INT_BASE_SHFT);
|
||||
|
||||
mb();
|
||||
if (enable_shub_wars_1_1()) {
|
||||
spin_lock_irqsave(&sn2_global_ptc_lock, flags);
|
||||
}
|
||||
pio_phys_write_mmr(p, val);
|
||||
if (enable_shub_wars_1_1()) {
|
||||
wait_piowc();
|
||||
spin_unlock_irqrestore(&sn2_global_ptc_lock, flags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sn_send_IPI_phys);
|
||||
|
||||
/**
|
||||
* sn2_send_IPI - send an IPI to a processor
|
||||
* @cpuid: target of the IPI
|
||||
* @vector: command to send
|
||||
* @delivery_mode: delivery mechanism
|
||||
* @redirect: redirect the IPI?
|
||||
*
|
||||
* Sends an IPI (InterProcessor Interrupt) to the processor specified by
|
||||
* @cpuid. @vector specifies the command to send, while @delivery_mode can
|
||||
* be one of the following
|
||||
*
|
||||
* %IA64_IPI_DM_INT - pend an interrupt
|
||||
* %IA64_IPI_DM_PMI - pend a PMI
|
||||
* %IA64_IPI_DM_NMI - pend an NMI
|
||||
* %IA64_IPI_DM_INIT - pend an INIT interrupt
|
||||
*/
|
||||
void sn2_send_IPI(int cpuid, int vector, int delivery_mode, int redirect)
|
||||
{
|
||||
long physid;
|
||||
int nasid;
|
||||
|
||||
physid = cpu_physical_id(cpuid);
|
||||
#ifdef XEN
|
||||
if (!sn_nodepda) {
|
||||
ia64_sn_get_sapic_info(physid, &nasid, NULL, NULL);
|
||||
} else
|
||||
#endif
|
||||
nasid = cpuid_to_nasid(cpuid);
|
||||
|
||||
/* the following is used only when starting cpus at boot time */
|
||||
if (unlikely(nasid == -1))
|
||||
ia64_sn_get_sapic_info(physid, &nasid, NULL, NULL);
|
||||
|
||||
sn_send_IPI_phys(nasid, physid, vector, delivery_mode);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
#define PTC_BASENAME "sgi_sn/ptc_statistics"
|
||||
|
||||
static void *sn2_ptc_seq_start(struct seq_file *file, loff_t * offset)
|
||||
{
|
||||
if (*offset < NR_CPUS)
|
||||
return offset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *sn2_ptc_seq_next(struct seq_file *file, void *data, loff_t * offset)
|
||||
{
|
||||
(*offset)++;
|
||||
if (*offset < NR_CPUS)
|
||||
return offset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sn2_ptc_seq_stop(struct seq_file *file, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static int sn2_ptc_seq_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct ptc_stats *stat;
|
||||
int cpu;
|
||||
|
||||
cpu = *(loff_t *) data;
|
||||
|
||||
if (!cpu) {
|
||||
seq_printf(file,
|
||||
"# cpu ptc_l newrid ptc_flushes nodes_flushed deadlocks lock_nsec shub_nsec shub_nsec_max not_my_mm deadlock2\n");
|
||||
seq_printf(file, "# ptctest %d\n", sn2_ptctest);
|
||||
}
|
||||
|
||||
if (cpu < NR_CPUS && cpu_online(cpu)) {
|
||||
stat = &per_cpu(ptcstats, cpu);
|
||||
seq_printf(file, "cpu %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", cpu, stat->ptc_l,
|
||||
stat->change_rid, stat->shub_ptc_flushes, stat->nodes_flushed,
|
||||
stat->deadlocks,
|
||||
1000 * stat->lock_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec,
|
||||
1000 * stat->shub_itc_clocks / per_cpu(cpu_info, cpu).cyc_per_usec,
|
||||
1000 * stat->shub_itc_clocks_max / per_cpu(cpu_info, cpu).cyc_per_usec,
|
||||
stat->shub_ptc_flushes_not_my_mm,
|
||||
stat->deadlocks2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations sn2_ptc_seq_ops = {
|
||||
.start = sn2_ptc_seq_start,
|
||||
.next = sn2_ptc_seq_next,
|
||||
.stop = sn2_ptc_seq_stop,
|
||||
.show = sn2_ptc_seq_show
|
||||
};
|
||||
|
||||
static int sn2_ptc_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &sn2_ptc_seq_ops);
|
||||
}
|
||||
|
||||
static struct file_operations proc_sn2_ptc_operations = {
|
||||
.open = sn2_ptc_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static struct proc_dir_entry *proc_sn2_ptc;
|
||||
|
||||
static int __init sn2_ptc_init(void)
|
||||
{
|
||||
if (!ia64_platform_is("sn2"))
|
||||
return 0;
|
||||
|
||||
if (!(proc_sn2_ptc = create_proc_entry(PTC_BASENAME, 0444, NULL))) {
|
||||
printk(KERN_ERR "unable to create %s proc entry", PTC_BASENAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
proc_sn2_ptc->proc_fops = &proc_sn2_ptc_operations;
|
||||
spin_lock_init(&sn2_global_ptc_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit sn2_ptc_exit(void)
|
||||
{
|
||||
remove_proc_entry(PTC_BASENAME, NULL);
|
||||
}
|
||||
|
||||
module_init(sn2_ptc_init);
|
||||
module_exit(sn2_ptc_exit);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* linux/arch/ia64/kernel/time.c
|
||||
*
|
||||
* Copyright (C) 1998-2003 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
* David Mosberger <davidm@hpl.hp.com>
|
||||
* Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
|
||||
* Copyright (C) 1999-2000 VA Linux Systems
|
||||
* Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/timex.h>
|
||||
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/sal.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/system.h>
|
||||
#ifdef XEN
|
||||
#include <linux/jiffies.h> // not included by xen/sched.h
|
||||
#endif
|
||||
|
||||
extern unsigned long wall_jiffies;
|
||||
|
||||
u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
|
||||
|
||||
EXPORT_SYMBOL(jiffies_64);
|
||||
|
||||
#define TIME_KEEPER_ID 0 /* smp_processor_id() of time-keeper */
|
||||
|
||||
#ifdef CONFIG_IA64_DEBUG_IRQ
|
||||
|
||||
unsigned long last_cli_ip;
|
||||
EXPORT_SYMBOL(last_cli_ip);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef XEN
|
||||
static struct time_interpolator itc_interpolator = {
|
||||
.shift = 16,
|
||||
.mask = 0xffffffffffffffffLL,
|
||||
.source = TIME_SOURCE_CPU
|
||||
};
|
||||
|
||||
static irqreturn_t
|
||||
timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long new_itm;
|
||||
|
||||
if (unlikely(cpu_is_offline(smp_processor_id()))) {
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
platform_timer_interrupt(irq, dev_id, regs);
|
||||
|
||||
new_itm = local_cpu_data->itm_next;
|
||||
|
||||
if (!time_after(ia64_get_itc(), new_itm))
|
||||
printk(KERN_ERR "Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
|
||||
ia64_get_itc(), new_itm);
|
||||
|
||||
profile_tick(CPU_PROFILING, regs);
|
||||
|
||||
while (1) {
|
||||
update_process_times(user_mode(regs));
|
||||
|
||||
new_itm += local_cpu_data->itm_delta;
|
||||
|
||||
if (smp_processor_id() == TIME_KEEPER_ID) {
|
||||
/*
|
||||
* Here we are in the timer irq handler. We have irqs locally
|
||||
* disabled, but we don't know if the timer_bh is running on
|
||||
* another CPU. We need to avoid to SMP race by acquiring the
|
||||
* xtime_lock.
|
||||
*/
|
||||
write_seqlock(&xtime_lock);
|
||||
do_timer(regs);
|
||||
local_cpu_data->itm_next = new_itm;
|
||||
write_sequnlock(&xtime_lock);
|
||||
} else
|
||||
local_cpu_data->itm_next = new_itm;
|
||||
|
||||
if (time_after(new_itm, ia64_get_itc()))
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* If we're too close to the next clock tick for
|
||||
* comfort, we increase the safety margin by
|
||||
* intentionally dropping the next tick(s). We do NOT
|
||||
* update itm.next because that would force us to call
|
||||
* do_timer() which in turn would let our clock run
|
||||
* too fast (with the potentially devastating effect
|
||||
* of losing monotony of time).
|
||||
*/
|
||||
while (!time_after(new_itm, ia64_get_itc() + local_cpu_data->itm_delta/2))
|
||||
new_itm += local_cpu_data->itm_delta;
|
||||
ia64_set_itm(new_itm);
|
||||
/* double check, in case we got hit by a (slow) PMI: */
|
||||
} while (time_after_eq(ia64_get_itc(), new_itm));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Encapsulate access to the itm structure for SMP.
|
||||
*/
|
||||
void
|
||||
ia64_cpu_local_tick (void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
unsigned long shift = 0, delta;
|
||||
|
||||
/* arrange for the cycle counter to generate a timer interrupt: */
|
||||
ia64_set_itv(IA64_TIMER_VECTOR);
|
||||
|
||||
delta = local_cpu_data->itm_delta;
|
||||
/*
|
||||
* Stagger the timer tick for each CPU so they don't occur all at (almost) the
|
||||
* same time:
|
||||
*/
|
||||
if (cpu) {
|
||||
unsigned long hi = 1UL << ia64_fls(cpu);
|
||||
shift = (2*(cpu - hi) + 1) * delta/hi/2;
|
||||
}
|
||||
local_cpu_data->itm_next = ia64_get_itc() + delta + shift;
|
||||
ia64_set_itm(local_cpu_data->itm_next);
|
||||
}
|
||||
|
||||
static int nojitter;
|
||||
|
||||
static int __init nojitter_setup(char *str)
|
||||
{
|
||||
nojitter = 1;
|
||||
printk("Jitter checking for ITC timers disabled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("nojitter", nojitter_setup);
|
||||
|
||||
|
||||
void __devinit
|
||||
ia64_init_itm (void)
|
||||
{
|
||||
unsigned long platform_base_freq, itc_freq;
|
||||
struct pal_freq_ratio itc_ratio, proc_ratio;
|
||||
#ifdef XEN /* warning cleanup */
|
||||
unsigned long status, platform_base_drift, itc_drift;
|
||||
#else
|
||||
long status, platform_base_drift, itc_drift;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* According to SAL v2.6, we need to use a SAL call to determine the platform base
|
||||
* frequency and then a PAL call to determine the frequency ratio between the ITC
|
||||
* and the base frequency.
|
||||
*/
|
||||
status = ia64_sal_freq_base(SAL_FREQ_BASE_PLATFORM,
|
||||
&platform_base_freq, &platform_base_drift);
|
||||
if (status != 0) {
|
||||
printk(KERN_ERR "SAL_FREQ_BASE_PLATFORM failed: %s\n", ia64_sal_strerror(status));
|
||||
} else {
|
||||
status = ia64_pal_freq_ratios(&proc_ratio, NULL, &itc_ratio);
|
||||
if (status != 0)
|
||||
printk(KERN_ERR "PAL_FREQ_RATIOS failed with status=%ld\n", status);
|
||||
}
|
||||
if (status != 0) {
|
||||
/* invent "random" values */
|
||||
printk(KERN_ERR
|
||||
"SAL/PAL failed to obtain frequency info---inventing reasonable values\n");
|
||||
platform_base_freq = 100000000;
|
||||
platform_base_drift = -1; /* no drift info */
|
||||
itc_ratio.num = 3;
|
||||
itc_ratio.den = 1;
|
||||
}
|
||||
if (platform_base_freq < 40000000) {
|
||||
printk(KERN_ERR "Platform base frequency %lu bogus---resetting to 75MHz!\n",
|
||||
platform_base_freq);
|
||||
platform_base_freq = 75000000;
|
||||
platform_base_drift = -1;
|
||||
}
|
||||
if (!proc_ratio.den)
|
||||
proc_ratio.den = 1; /* avoid division by zero */
|
||||
if (!itc_ratio.den)
|
||||
itc_ratio.den = 1; /* avoid division by zero */
|
||||
|
||||
itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den;
|
||||
|
||||
local_cpu_data->itm_delta = (itc_freq + HZ/2) / HZ;
|
||||
printk(KERN_DEBUG "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, "
|
||||
"ITC freq=%lu.%03luMHz", smp_processor_id(),
|
||||
platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
|
||||
#ifdef XEN
|
||||
(u64)itc_ratio.num, (u64)itc_ratio.den,
|
||||
itc_freq / 1000000, (itc_freq / 1000) % 1000);
|
||||
#else
|
||||
itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000);
|
||||
#endif
|
||||
|
||||
if (platform_base_drift != -1) {
|
||||
itc_drift = platform_base_drift*itc_ratio.num/itc_ratio.den;
|
||||
printk("+/-%ldppm\n", itc_drift);
|
||||
} else {
|
||||
itc_drift = -1;
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
local_cpu_data->proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den;
|
||||
local_cpu_data->itc_freq = itc_freq;
|
||||
local_cpu_data->cyc_per_usec = (itc_freq + USEC_PER_SEC/2) / USEC_PER_SEC;
|
||||
local_cpu_data->nsec_per_cyc = ((NSEC_PER_SEC<<IA64_NSEC_PER_CYC_SHIFT)
|
||||
+ itc_freq/2)/itc_freq;
|
||||
|
||||
if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) {
|
||||
#ifndef XEN
|
||||
itc_interpolator.frequency = local_cpu_data->itc_freq;
|
||||
itc_interpolator.drift = itc_drift;
|
||||
#ifdef CONFIG_SMP
|
||||
/* On IA64 in an SMP configuration ITCs are never accurately synchronized.
|
||||
* Jitter compensation requires a cmpxchg which may limit
|
||||
* the scalability of the syscalls for retrieving time.
|
||||
* The ITC synchronization is usually successful to within a few
|
||||
* ITC ticks but this is not a sure thing. If you need to improve
|
||||
* timer performance in SMP situations then boot the kernel with the
|
||||
* "nojitter" option. However, doing so may result in time fluctuating (maybe
|
||||
* even going backward) if the ITC offsets between the individual CPUs
|
||||
* are too large.
|
||||
*/
|
||||
if (!nojitter) itc_interpolator.jitter = 1;
|
||||
#endif
|
||||
register_time_interpolator(&itc_interpolator);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Setup the CPU local timer tick */
|
||||
ia64_cpu_local_tick();
|
||||
}
|
||||
|
||||
#ifndef XEN
|
||||
static struct irqaction timer_irqaction = {
|
||||
.handler = timer_interrupt,
|
||||
.flags = SA_INTERRUPT,
|
||||
.name = "timer"
|
||||
};
|
||||
|
||||
void __init
|
||||
time_init (void)
|
||||
{
|
||||
register_percpu_irq(IA64_TIMER_VECTOR, &timer_irqaction);
|
||||
efi_gettimeofday(&xtime);
|
||||
ia64_init_itm();
|
||||
|
||||
/*
|
||||
* Initialize wall_to_monotonic such that adding it to xtime will yield zero, the
|
||||
* tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC).
|
||||
*/
|
||||
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
|
||||
}
|
||||
#endif
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* TLB support routines.
|
||||
*
|
||||
* Copyright (C) 1998-2001, 2003 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* 08/02/00 A. Mallick <asit.k.mallick@intel.com>
|
||||
* Modified RID allocation for SMP
|
||||
* Goutham Rao <goutham.rao@intel.com>
|
||||
* IPI based ptc implementation and A-step IPI implementation.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/delay.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pal.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
static struct {
|
||||
unsigned long mask; /* mask of supported purge page-sizes */
|
||||
unsigned long max_bits; /* log2() of largest supported purge page-size */
|
||||
} purge;
|
||||
|
||||
#ifndef XEN
|
||||
struct ia64_ctx ia64_ctx = {
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
.next = 1,
|
||||
.limit = (1 << 15) - 1, /* start out with the safe (architected) limit */
|
||||
.max_ctx = ~0U
|
||||
};
|
||||
|
||||
DEFINE_PER_CPU(u8, ia64_need_tlb_flush);
|
||||
|
||||
/*
|
||||
* Acquire the ia64_ctx.lock before calling this function!
|
||||
*/
|
||||
void
|
||||
wrap_mmu_context (struct mm_struct *mm)
|
||||
{
|
||||
unsigned long tsk_context, max_ctx = ia64_ctx.max_ctx;
|
||||
struct task_struct *tsk;
|
||||
int i;
|
||||
|
||||
if (ia64_ctx.next > max_ctx)
|
||||
ia64_ctx.next = 300; /* skip daemons */
|
||||
ia64_ctx.limit = max_ctx + 1;
|
||||
|
||||
/*
|
||||
* Scan all the task's mm->context and set proper safe range
|
||||
*/
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
repeat:
|
||||
for_each_process(tsk) {
|
||||
if (!tsk->mm)
|
||||
continue;
|
||||
tsk_context = tsk->mm->context;
|
||||
if (tsk_context == ia64_ctx.next) {
|
||||
if (++ia64_ctx.next >= ia64_ctx.limit) {
|
||||
/* empty range: reset the range limit and start over */
|
||||
if (ia64_ctx.next > max_ctx)
|
||||
ia64_ctx.next = 300;
|
||||
ia64_ctx.limit = max_ctx + 1;
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
if ((tsk_context > ia64_ctx.next) && (tsk_context < ia64_ctx.limit))
|
||||
ia64_ctx.limit = tsk_context;
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
/* can't call flush_tlb_all() here because of race condition with O(1) scheduler [EF] */
|
||||
{
|
||||
int cpu = get_cpu(); /* prevent preemption/migration */
|
||||
for (i = 0; i < NR_CPUS; ++i)
|
||||
if (cpu_online(i) && (i != cpu))
|
||||
per_cpu(ia64_need_tlb_flush, i) = 1;
|
||||
put_cpu();
|
||||
}
|
||||
local_flush_tlb_all();
|
||||
}
|
||||
#endif /* XEN */
|
||||
|
||||
void
|
||||
ia64_global_tlb_purge (unsigned long start, unsigned long end, unsigned long nbits)
|
||||
{
|
||||
static DEFINE_SPINLOCK(ptcg_lock);
|
||||
|
||||
/* HW requires global serialization of ptc.ga. */
|
||||
spin_lock(&ptcg_lock);
|
||||
{
|
||||
do {
|
||||
/*
|
||||
* Flush ALAT entries also.
|
||||
*/
|
||||
ia64_ptcga(start, (nbits<<2));
|
||||
ia64_srlz_i();
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
}
|
||||
spin_unlock(&ptcg_lock);
|
||||
}
|
||||
|
||||
void
|
||||
local_flush_tlb_all (void)
|
||||
{
|
||||
unsigned long i, j, flags, count0, count1, stride0, stride1, addr;
|
||||
#ifdef XEN
|
||||
/* increment flush clock before mTLB flush */
|
||||
u32 flush_time = tlbflush_clock_inc_and_return();
|
||||
#endif
|
||||
addr = local_cpu_data->ptce_base;
|
||||
count0 = local_cpu_data->ptce_count[0];
|
||||
count1 = local_cpu_data->ptce_count[1];
|
||||
stride0 = local_cpu_data->ptce_stride[0];
|
||||
stride1 = local_cpu_data->ptce_stride[1];
|
||||
|
||||
local_irq_save(flags);
|
||||
for (i = 0; i < count0; ++i) {
|
||||
for (j = 0; j < count1; ++j) {
|
||||
ia64_ptce(addr);
|
||||
addr += stride1;
|
||||
}
|
||||
addr += stride0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
ia64_srlz_i(); /* srlz.i implies srlz.d */
|
||||
#ifdef XEN
|
||||
/* update after mTLB flush. */
|
||||
tlbflush_update_time(&__get_cpu_var(tlbflush_time), flush_time);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(local_flush_tlb_all);
|
||||
|
||||
#ifndef XEN
|
||||
void
|
||||
flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
unsigned long size = end - start;
|
||||
unsigned long nbits;
|
||||
|
||||
if (mm != current->active_mm) {
|
||||
/* this does happen, but perhaps it's not worth optimizing for? */
|
||||
#ifdef CONFIG_SMP
|
||||
flush_tlb_all();
|
||||
#else
|
||||
mm->context = 0;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
nbits = ia64_fls(size + 0xfff);
|
||||
while (unlikely (((1UL << nbits) & purge.mask) == 0) && (nbits < purge.max_bits))
|
||||
++nbits;
|
||||
if (nbits > purge.max_bits)
|
||||
nbits = purge.max_bits;
|
||||
start &= ~((1UL << nbits) - 1);
|
||||
|
||||
# ifdef CONFIG_SMP
|
||||
platform_global_tlb_purge(start, end, nbits);
|
||||
# else
|
||||
do {
|
||||
ia64_ptcl(start, (nbits<<2));
|
||||
start += (1UL << nbits);
|
||||
} while (start < end);
|
||||
# endif
|
||||
|
||||
ia64_srlz_i(); /* srlz.i implies srlz.d */
|
||||
}
|
||||
EXPORT_SYMBOL(flush_tlb_range);
|
||||
#endif
|
||||
|
||||
void __devinit
|
||||
ia64_tlb_init (void)
|
||||
{
|
||||
#ifndef XEN
|
||||
ia64_ptce_info_t ptce_info;
|
||||
#else
|
||||
ia64_ptce_info_t ptce_info = { 0 };
|
||||
#endif
|
||||
unsigned long tr_pgbits;
|
||||
long status;
|
||||
|
||||
if ((status = ia64_pal_vm_page_size(&tr_pgbits, &purge.mask)) != 0) {
|
||||
printk(KERN_ERR "PAL_VM_PAGE_SIZE failed with status=%ld;"
|
||||
"defaulting to architected purge page-sizes.\n", status);
|
||||
purge.mask = 0x115557000UL;
|
||||
}
|
||||
purge.max_bits = ia64_fls(purge.mask);
|
||||
|
||||
ia64_get_ptce(&ptce_info);
|
||||
local_cpu_data->ptce_base = ptce_info.base;
|
||||
local_cpu_data->ptce_count[0] = ptce_info.count[0];
|
||||
local_cpu_data->ptce_count[1] = ptce_info.count[1];
|
||||
local_cpu_data->ptce_stride[0] = ptce_info.stride[0];
|
||||
local_cpu_data->ptce_stride[1] = ptce_info.stride[1];
|
||||
|
||||
local_flush_tlb_all(); /* nuke left overs from bootstrapping... */
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,459 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Hewlett-Packard Co
|
||||
* Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* Generic IA-64 unwind info decoder.
|
||||
*
|
||||
* This file is used both by the Linux kernel and objdump. Please keep
|
||||
* the two copies of this file in sync.
|
||||
*
|
||||
* You need to customize the decoder by defining the following
|
||||
* macros/constants before including this file:
|
||||
*
|
||||
* Types:
|
||||
* unw_word Unsigned integer type with at least 64 bits
|
||||
*
|
||||
* Register names:
|
||||
* UNW_REG_BSP
|
||||
* UNW_REG_BSPSTORE
|
||||
* UNW_REG_FPSR
|
||||
* UNW_REG_LC
|
||||
* UNW_REG_PFS
|
||||
* UNW_REG_PR
|
||||
* UNW_REG_RNAT
|
||||
* UNW_REG_PSP
|
||||
* UNW_REG_RP
|
||||
* UNW_REG_UNAT
|
||||
*
|
||||
* Decoder action macros:
|
||||
* UNW_DEC_BAD_CODE(code)
|
||||
* UNW_DEC_ABI(fmt,abi,context,arg)
|
||||
* UNW_DEC_BR_GR(fmt,brmask,gr,arg)
|
||||
* UNW_DEC_BR_MEM(fmt,brmask,arg)
|
||||
* UNW_DEC_COPY_STATE(fmt,label,arg)
|
||||
* UNW_DEC_EPILOGUE(fmt,t,ecount,arg)
|
||||
* UNW_DEC_FRGR_MEM(fmt,grmask,frmask,arg)
|
||||
* UNW_DEC_FR_MEM(fmt,frmask,arg)
|
||||
* UNW_DEC_GR_GR(fmt,grmask,gr,arg)
|
||||
* UNW_DEC_GR_MEM(fmt,grmask,arg)
|
||||
* UNW_DEC_LABEL_STATE(fmt,label,arg)
|
||||
* UNW_DEC_MEM_STACK_F(fmt,t,size,arg)
|
||||
* UNW_DEC_MEM_STACK_V(fmt,t,arg)
|
||||
* UNW_DEC_PRIUNAT_GR(fmt,r,arg)
|
||||
* UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg)
|
||||
* UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg)
|
||||
* UNW_DEC_PRIUNAT_WHEN_PSPREL(fmt,pspoff,arg)
|
||||
* UNW_DEC_PRIUNAT_WHEN_SPREL(fmt,spoff,arg)
|
||||
* UNW_DEC_PROLOGUE(fmt,body,rlen,arg)
|
||||
* UNW_DEC_PROLOGUE_GR(fmt,rlen,mask,grsave,arg)
|
||||
* UNW_DEC_REG_PSPREL(fmt,reg,pspoff,arg)
|
||||
* UNW_DEC_REG_REG(fmt,src,dst,arg)
|
||||
* UNW_DEC_REG_SPREL(fmt,reg,spoff,arg)
|
||||
* UNW_DEC_REG_WHEN(fmt,reg,t,arg)
|
||||
* UNW_DEC_RESTORE(fmt,t,abreg,arg)
|
||||
* UNW_DEC_RESTORE_P(fmt,qp,t,abreg,arg)
|
||||
* UNW_DEC_SPILL_BASE(fmt,pspoff,arg)
|
||||
* UNW_DEC_SPILL_MASK(fmt,imaskp,arg)
|
||||
* UNW_DEC_SPILL_PSPREL(fmt,t,abreg,pspoff,arg)
|
||||
* UNW_DEC_SPILL_PSPREL_P(fmt,qp,t,abreg,pspoff,arg)
|
||||
* UNW_DEC_SPILL_REG(fmt,t,abreg,x,ytreg,arg)
|
||||
* UNW_DEC_SPILL_REG_P(fmt,qp,t,abreg,x,ytreg,arg)
|
||||
* UNW_DEC_SPILL_SPREL(fmt,t,abreg,spoff,arg)
|
||||
* UNW_DEC_SPILL_SPREL_P(fmt,qp,t,abreg,pspoff,arg)
|
||||
*/
|
||||
|
||||
static unw_word
|
||||
unw_decode_uleb128 (unsigned char **dpp)
|
||||
{
|
||||
unsigned shift = 0;
|
||||
unw_word byte, result = 0;
|
||||
unsigned char *bp = *dpp;
|
||||
|
||||
while (1)
|
||||
{
|
||||
byte = *bp++;
|
||||
result |= (byte & 0x7f) << shift;
|
||||
if ((byte & 0x80) == 0)
|
||||
break;
|
||||
shift += 7;
|
||||
}
|
||||
*dpp = bp;
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_x1 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char byte1, abreg;
|
||||
unw_word t, off;
|
||||
|
||||
byte1 = *dp++;
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
off = unw_decode_uleb128 (&dp);
|
||||
abreg = (byte1 & 0x7f);
|
||||
if (byte1 & 0x80)
|
||||
UNW_DEC_SPILL_SPREL(X1, t, abreg, off, arg);
|
||||
else
|
||||
UNW_DEC_SPILL_PSPREL(X1, t, abreg, off, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_x2 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char byte1, byte2, abreg, x, ytreg;
|
||||
unw_word t;
|
||||
|
||||
byte1 = *dp++; byte2 = *dp++;
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
abreg = (byte1 & 0x7f);
|
||||
ytreg = byte2;
|
||||
x = (byte1 >> 7) & 1;
|
||||
if ((byte1 & 0x80) == 0 && ytreg == 0)
|
||||
UNW_DEC_RESTORE(X2, t, abreg, arg);
|
||||
else
|
||||
UNW_DEC_SPILL_REG(X2, t, abreg, x, ytreg, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_x3 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char byte1, byte2, abreg, qp;
|
||||
unw_word t, off;
|
||||
|
||||
byte1 = *dp++; byte2 = *dp++;
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
off = unw_decode_uleb128 (&dp);
|
||||
|
||||
qp = (byte1 & 0x3f);
|
||||
abreg = (byte2 & 0x7f);
|
||||
|
||||
if (byte1 & 0x80)
|
||||
UNW_DEC_SPILL_SPREL_P(X3, qp, t, abreg, off, arg);
|
||||
else
|
||||
UNW_DEC_SPILL_PSPREL_P(X3, qp, t, abreg, off, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_x4 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char byte1, byte2, byte3, qp, abreg, x, ytreg;
|
||||
unw_word t;
|
||||
|
||||
byte1 = *dp++; byte2 = *dp++; byte3 = *dp++;
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
|
||||
qp = (byte1 & 0x3f);
|
||||
abreg = (byte2 & 0x7f);
|
||||
x = (byte2 >> 7) & 1;
|
||||
ytreg = byte3;
|
||||
|
||||
if ((byte2 & 0x80) == 0 && byte3 == 0)
|
||||
UNW_DEC_RESTORE_P(X4, qp, t, abreg, arg);
|
||||
else
|
||||
UNW_DEC_SPILL_REG_P(X4, qp, t, abreg, x, ytreg, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_r1 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
int body = (code & 0x20) != 0;
|
||||
unw_word rlen;
|
||||
|
||||
rlen = (code & 0x1f);
|
||||
UNW_DEC_PROLOGUE(R1, body, rlen, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_r2 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char byte1, mask, grsave;
|
||||
unw_word rlen;
|
||||
|
||||
byte1 = *dp++;
|
||||
|
||||
mask = ((code & 0x7) << 1) | ((byte1 >> 7) & 1);
|
||||
grsave = (byte1 & 0x7f);
|
||||
rlen = unw_decode_uleb128 (&dp);
|
||||
UNW_DEC_PROLOGUE_GR(R2, rlen, mask, grsave, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_r3 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unw_word rlen;
|
||||
|
||||
rlen = unw_decode_uleb128 (&dp);
|
||||
UNW_DEC_PROLOGUE(R3, ((code & 0x3) == 1), rlen, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_p1 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char brmask = (code & 0x1f);
|
||||
|
||||
UNW_DEC_BR_MEM(P1, brmask, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_p2_p5 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
if ((code & 0x10) == 0)
|
||||
{
|
||||
unsigned char byte1 = *dp++;
|
||||
|
||||
UNW_DEC_BR_GR(P2, ((code & 0xf) << 1) | ((byte1 >> 7) & 1),
|
||||
(byte1 & 0x7f), arg);
|
||||
}
|
||||
else if ((code & 0x08) == 0)
|
||||
{
|
||||
unsigned char byte1 = *dp++, r, dst;
|
||||
|
||||
r = ((code & 0x7) << 1) | ((byte1 >> 7) & 1);
|
||||
dst = (byte1 & 0x7f);
|
||||
switch (r)
|
||||
{
|
||||
case 0: UNW_DEC_REG_GR(P3, UNW_REG_PSP, dst, arg); break;
|
||||
case 1: UNW_DEC_REG_GR(P3, UNW_REG_RP, dst, arg); break;
|
||||
case 2: UNW_DEC_REG_GR(P3, UNW_REG_PFS, dst, arg); break;
|
||||
case 3: UNW_DEC_REG_GR(P3, UNW_REG_PR, dst, arg); break;
|
||||
case 4: UNW_DEC_REG_GR(P3, UNW_REG_UNAT, dst, arg); break;
|
||||
case 5: UNW_DEC_REG_GR(P3, UNW_REG_LC, dst, arg); break;
|
||||
case 6: UNW_DEC_RP_BR(P3, dst, arg); break;
|
||||
case 7: UNW_DEC_REG_GR(P3, UNW_REG_RNAT, dst, arg); break;
|
||||
case 8: UNW_DEC_REG_GR(P3, UNW_REG_BSP, dst, arg); break;
|
||||
case 9: UNW_DEC_REG_GR(P3, UNW_REG_BSPSTORE, dst, arg); break;
|
||||
case 10: UNW_DEC_REG_GR(P3, UNW_REG_FPSR, dst, arg); break;
|
||||
case 11: UNW_DEC_PRIUNAT_GR(P3, dst, arg); break;
|
||||
default: UNW_DEC_BAD_CODE(r); break;
|
||||
}
|
||||
}
|
||||
else if ((code & 0x7) == 0)
|
||||
UNW_DEC_SPILL_MASK(P4, dp, arg);
|
||||
else if ((code & 0x7) == 1)
|
||||
{
|
||||
unw_word grmask, frmask, byte1, byte2, byte3;
|
||||
|
||||
byte1 = *dp++; byte2 = *dp++; byte3 = *dp++;
|
||||
grmask = ((byte1 >> 4) & 0xf);
|
||||
frmask = ((byte1 & 0xf) << 16) | (byte2 << 8) | byte3;
|
||||
UNW_DEC_FRGR_MEM(P5, grmask, frmask, arg);
|
||||
}
|
||||
else
|
||||
UNW_DEC_BAD_CODE(code);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_p6 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
int gregs = (code & 0x10) != 0;
|
||||
unsigned char mask = (code & 0x0f);
|
||||
|
||||
if (gregs)
|
||||
UNW_DEC_GR_MEM(P6, mask, arg);
|
||||
else
|
||||
UNW_DEC_FR_MEM(P6, mask, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_p7_p10 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unsigned char r, byte1, byte2;
|
||||
unw_word t, size;
|
||||
|
||||
if ((code & 0x10) == 0)
|
||||
{
|
||||
r = (code & 0xf);
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
switch (r)
|
||||
{
|
||||
case 0:
|
||||
size = unw_decode_uleb128 (&dp);
|
||||
UNW_DEC_MEM_STACK_F(P7, t, size, arg);
|
||||
break;
|
||||
|
||||
case 1: UNW_DEC_MEM_STACK_V(P7, t, arg); break;
|
||||
case 2: UNW_DEC_SPILL_BASE(P7, t, arg); break;
|
||||
case 3: UNW_DEC_REG_SPREL(P7, UNW_REG_PSP, t, arg); break;
|
||||
case 4: UNW_DEC_REG_WHEN(P7, UNW_REG_RP, t, arg); break;
|
||||
case 5: UNW_DEC_REG_PSPREL(P7, UNW_REG_RP, t, arg); break;
|
||||
case 6: UNW_DEC_REG_WHEN(P7, UNW_REG_PFS, t, arg); break;
|
||||
case 7: UNW_DEC_REG_PSPREL(P7, UNW_REG_PFS, t, arg); break;
|
||||
case 8: UNW_DEC_REG_WHEN(P7, UNW_REG_PR, t, arg); break;
|
||||
case 9: UNW_DEC_REG_PSPREL(P7, UNW_REG_PR, t, arg); break;
|
||||
case 10: UNW_DEC_REG_WHEN(P7, UNW_REG_LC, t, arg); break;
|
||||
case 11: UNW_DEC_REG_PSPREL(P7, UNW_REG_LC, t, arg); break;
|
||||
case 12: UNW_DEC_REG_WHEN(P7, UNW_REG_UNAT, t, arg); break;
|
||||
case 13: UNW_DEC_REG_PSPREL(P7, UNW_REG_UNAT, t, arg); break;
|
||||
case 14: UNW_DEC_REG_WHEN(P7, UNW_REG_FPSR, t, arg); break;
|
||||
case 15: UNW_DEC_REG_PSPREL(P7, UNW_REG_FPSR, t, arg); break;
|
||||
default: UNW_DEC_BAD_CODE(r); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (code & 0xf)
|
||||
{
|
||||
case 0x0: /* p8 */
|
||||
{
|
||||
r = *dp++;
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
switch (r)
|
||||
{
|
||||
case 1: UNW_DEC_REG_SPREL(P8, UNW_REG_RP, t, arg); break;
|
||||
case 2: UNW_DEC_REG_SPREL(P8, UNW_REG_PFS, t, arg); break;
|
||||
case 3: UNW_DEC_REG_SPREL(P8, UNW_REG_PR, t, arg); break;
|
||||
case 4: UNW_DEC_REG_SPREL(P8, UNW_REG_LC, t, arg); break;
|
||||
case 5: UNW_DEC_REG_SPREL(P8, UNW_REG_UNAT, t, arg); break;
|
||||
case 6: UNW_DEC_REG_SPREL(P8, UNW_REG_FPSR, t, arg); break;
|
||||
case 7: UNW_DEC_REG_WHEN(P8, UNW_REG_BSP, t, arg); break;
|
||||
case 8: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSP, t, arg); break;
|
||||
case 9: UNW_DEC_REG_SPREL(P8, UNW_REG_BSP, t, arg); break;
|
||||
case 10: UNW_DEC_REG_WHEN(P8, UNW_REG_BSPSTORE, t, arg); break;
|
||||
case 11: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSPSTORE, t, arg); break;
|
||||
case 12: UNW_DEC_REG_SPREL(P8, UNW_REG_BSPSTORE, t, arg); break;
|
||||
case 13: UNW_DEC_REG_WHEN(P8, UNW_REG_RNAT, t, arg); break;
|
||||
case 14: UNW_DEC_REG_PSPREL(P8, UNW_REG_RNAT, t, arg); break;
|
||||
case 15: UNW_DEC_REG_SPREL(P8, UNW_REG_RNAT, t, arg); break;
|
||||
case 16: UNW_DEC_PRIUNAT_WHEN_GR(P8, t, arg); break;
|
||||
case 17: UNW_DEC_PRIUNAT_PSPREL(P8, t, arg); break;
|
||||
case 18: UNW_DEC_PRIUNAT_SPREL(P8, t, arg); break;
|
||||
case 19: UNW_DEC_PRIUNAT_WHEN_MEM(P8, t, arg); break;
|
||||
default: UNW_DEC_BAD_CODE(r); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
byte1 = *dp++; byte2 = *dp++;
|
||||
UNW_DEC_GR_GR(P9, (byte1 & 0xf), (byte2 & 0x7f), arg);
|
||||
break;
|
||||
|
||||
case 0xf: /* p10 */
|
||||
byte1 = *dp++; byte2 = *dp++;
|
||||
UNW_DEC_ABI(P10, byte1, byte2, arg);
|
||||
break;
|
||||
|
||||
case 0x9:
|
||||
return unw_decode_x1 (dp, code, arg);
|
||||
|
||||
case 0xa:
|
||||
return unw_decode_x2 (dp, code, arg);
|
||||
|
||||
case 0xb:
|
||||
return unw_decode_x3 (dp, code, arg);
|
||||
|
||||
case 0xc:
|
||||
return unw_decode_x4 (dp, code, arg);
|
||||
|
||||
default:
|
||||
UNW_DEC_BAD_CODE(code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_b1 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unw_word label = (code & 0x1f);
|
||||
|
||||
if ((code & 0x20) != 0)
|
||||
UNW_DEC_COPY_STATE(B1, label, arg);
|
||||
else
|
||||
UNW_DEC_LABEL_STATE(B1, label, arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_b2 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unw_word t;
|
||||
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
UNW_DEC_EPILOGUE(B2, t, (code & 0x1f), arg);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
unw_decode_b3_x4 (unsigned char *dp, unsigned char code, void *arg)
|
||||
{
|
||||
unw_word t, ecount, label;
|
||||
|
||||
if ((code & 0x10) == 0)
|
||||
{
|
||||
t = unw_decode_uleb128 (&dp);
|
||||
ecount = unw_decode_uleb128 (&dp);
|
||||
UNW_DEC_EPILOGUE(B3, t, ecount, arg);
|
||||
}
|
||||
else if ((code & 0x07) == 0)
|
||||
{
|
||||
label = unw_decode_uleb128 (&dp);
|
||||
if ((code & 0x08) != 0)
|
||||
UNW_DEC_COPY_STATE(B4, label, arg);
|
||||
else
|
||||
UNW_DEC_LABEL_STATE(B4, label, arg);
|
||||
}
|
||||
else
|
||||
switch (code & 0x7)
|
||||
{
|
||||
case 1: return unw_decode_x1 (dp, code, arg);
|
||||
case 2: return unw_decode_x2 (dp, code, arg);
|
||||
case 3: return unw_decode_x3 (dp, code, arg);
|
||||
case 4: return unw_decode_x4 (dp, code, arg);
|
||||
default: UNW_DEC_BAD_CODE(code); break;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
typedef unsigned char *(*unw_decoder) (unsigned char *, unsigned char, void *);
|
||||
|
||||
static unw_decoder unw_decode_table[2][8] =
|
||||
{
|
||||
/* prologue table: */
|
||||
{
|
||||
unw_decode_r1, /* 0 */
|
||||
unw_decode_r1,
|
||||
unw_decode_r2,
|
||||
unw_decode_r3,
|
||||
unw_decode_p1, /* 4 */
|
||||
unw_decode_p2_p5,
|
||||
unw_decode_p6,
|
||||
unw_decode_p7_p10
|
||||
},
|
||||
{
|
||||
unw_decode_r1, /* 0 */
|
||||
unw_decode_r1,
|
||||
unw_decode_r2,
|
||||
unw_decode_r3,
|
||||
unw_decode_b1, /* 4 */
|
||||
unw_decode_b1,
|
||||
unw_decode_b2,
|
||||
unw_decode_b3_x4
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode one descriptor and return address of next descriptor.
|
||||
*/
|
||||
static inline unsigned char *
|
||||
unw_decode (unsigned char *dp, int inside_body, void *arg)
|
||||
{
|
||||
unw_decoder decoder;
|
||||
unsigned char code;
|
||||
|
||||
code = *dp++;
|
||||
decoder = unw_decode_table[inside_body][code >> 5];
|
||||
dp = (*decoder) (dp, code, arg);
|
||||
return dp;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2002-2003 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* Kernel unwind support.
|
||||
*/
|
||||
|
||||
#define UNW_VER(x) ((x) >> 48)
|
||||
#define UNW_FLAG_MASK 0x0000ffff00000000
|
||||
#define UNW_FLAG_OSMASK 0x0000f00000000000
|
||||
#define UNW_FLAG_EHANDLER(x) ((x) & 0x0000000100000000L)
|
||||
#define UNW_FLAG_UHANDLER(x) ((x) & 0x0000000200000000L)
|
||||
#define UNW_LENGTH(x) ((x) & 0x00000000ffffffffL)
|
||||
|
||||
enum unw_register_index {
|
||||
/* primary unat: */
|
||||
UNW_REG_PRI_UNAT_GR,
|
||||
UNW_REG_PRI_UNAT_MEM,
|
||||
|
||||
/* register stack */
|
||||
UNW_REG_BSP, /* register stack pointer */
|
||||
UNW_REG_BSPSTORE,
|
||||
UNW_REG_PFS, /* previous function state */
|
||||
UNW_REG_RNAT,
|
||||
/* memory stack */
|
||||
UNW_REG_PSP, /* previous memory stack pointer */
|
||||
/* return pointer: */
|
||||
UNW_REG_RP,
|
||||
|
||||
/* preserved registers: */
|
||||
UNW_REG_R4, UNW_REG_R5, UNW_REG_R6, UNW_REG_R7,
|
||||
UNW_REG_UNAT, UNW_REG_PR, UNW_REG_LC, UNW_REG_FPSR,
|
||||
UNW_REG_B1, UNW_REG_B2, UNW_REG_B3, UNW_REG_B4, UNW_REG_B5,
|
||||
UNW_REG_F2, UNW_REG_F3, UNW_REG_F4, UNW_REG_F5,
|
||||
UNW_REG_F16, UNW_REG_F17, UNW_REG_F18, UNW_REG_F19,
|
||||
UNW_REG_F20, UNW_REG_F21, UNW_REG_F22, UNW_REG_F23,
|
||||
UNW_REG_F24, UNW_REG_F25, UNW_REG_F26, UNW_REG_F27,
|
||||
UNW_REG_F28, UNW_REG_F29, UNW_REG_F30, UNW_REG_F31,
|
||||
UNW_NUM_REGS
|
||||
};
|
||||
|
||||
struct unw_info_block {
|
||||
u64 header;
|
||||
u64 desc[0]; /* unwind descriptors */
|
||||
/* personality routine and language-specific data follow behind descriptors */
|
||||
};
|
||||
|
||||
struct unw_table {
|
||||
struct unw_table *next; /* must be first member! */
|
||||
const char *name;
|
||||
unsigned long gp; /* global pointer for this load-module */
|
||||
unsigned long segment_base; /* base for offsets in the unwind table entries */
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
const struct unw_table_entry *array;
|
||||
unsigned long length;
|
||||
};
|
||||
|
||||
enum unw_where {
|
||||
UNW_WHERE_NONE, /* register isn't saved at all */
|
||||
UNW_WHERE_GR, /* register is saved in a general register */
|
||||
UNW_WHERE_FR, /* register is saved in a floating-point register */
|
||||
UNW_WHERE_BR, /* register is saved in a branch register */
|
||||
UNW_WHERE_SPREL, /* register is saved on memstack (sp-relative) */
|
||||
UNW_WHERE_PSPREL, /* register is saved on memstack (psp-relative) */
|
||||
/*
|
||||
* At the end of each prologue these locations get resolved to
|
||||
* UNW_WHERE_PSPREL and UNW_WHERE_GR, respectively:
|
||||
*/
|
||||
UNW_WHERE_SPILL_HOME, /* register is saved in its spill home */
|
||||
UNW_WHERE_GR_SAVE /* register is saved in next general register */
|
||||
};
|
||||
|
||||
#define UNW_WHEN_NEVER 0x7fffffff
|
||||
|
||||
struct unw_reg_info {
|
||||
unsigned long val; /* save location: register number or offset */
|
||||
enum unw_where where; /* where the register gets saved */
|
||||
int when; /* when the register gets saved */
|
||||
};
|
||||
|
||||
struct unw_reg_state {
|
||||
struct unw_reg_state *next; /* next (outer) element on state stack */
|
||||
struct unw_reg_info reg[UNW_NUM_REGS]; /* register save locations */
|
||||
};
|
||||
|
||||
struct unw_labeled_state {
|
||||
struct unw_labeled_state *next; /* next labeled state (or NULL) */
|
||||
unsigned long label; /* label for this state */
|
||||
struct unw_reg_state saved_state;
|
||||
};
|
||||
|
||||
struct unw_state_record {
|
||||
unsigned int first_region : 1; /* is this the first region? */
|
||||
unsigned int done : 1; /* are we done scanning descriptors? */
|
||||
unsigned int any_spills : 1; /* got any register spills? */
|
||||
unsigned int in_body : 1; /* are we inside a body (as opposed to a prologue)? */
|
||||
unsigned long flags; /* see UNW_FLAG_* in unwind.h */
|
||||
|
||||
u8 *imask; /* imask of spill_mask record or NULL */
|
||||
unsigned long pr_val; /* predicate values */
|
||||
unsigned long pr_mask; /* predicate mask */
|
||||
long spill_offset; /* psp-relative offset for spill base */
|
||||
int region_start;
|
||||
int region_len;
|
||||
int epilogue_start;
|
||||
int epilogue_count;
|
||||
int when_target;
|
||||
|
||||
u8 gr_save_loc; /* next general register to use for saving a register */
|
||||
u8 return_link_reg; /* branch register in which the return link is passed */
|
||||
|
||||
struct unw_labeled_state *labeled_states; /* list of all labeled states */
|
||||
struct unw_reg_state curr; /* current state */
|
||||
};
|
||||
|
||||
enum unw_nat_type {
|
||||
UNW_NAT_NONE, /* NaT not represented */
|
||||
UNW_NAT_VAL, /* NaT represented by NaT value (fp reg) */
|
||||
UNW_NAT_MEMSTK, /* NaT value is in unat word at offset OFF */
|
||||
UNW_NAT_REGSTK /* NaT is in rnat */
|
||||
};
|
||||
|
||||
enum unw_insn_opcode {
|
||||
UNW_INSN_ADD, /* s[dst] += val */
|
||||
UNW_INSN_ADD_PSP, /* s[dst] = (s.psp + val) */
|
||||
UNW_INSN_ADD_SP, /* s[dst] = (s.sp + val) */
|
||||
UNW_INSN_MOVE, /* s[dst] = s[val] */
|
||||
UNW_INSN_MOVE2, /* s[dst] = s[val]; s[dst+1] = s[val+1] */
|
||||
UNW_INSN_MOVE_STACKED, /* s[dst] = ia64_rse_skip(*s.bsp, val) */
|
||||
UNW_INSN_SETNAT_MEMSTK, /* s[dst+1].nat.type = MEMSTK;
|
||||
s[dst+1].nat.off = *s.pri_unat - s[dst] */
|
||||
UNW_INSN_SETNAT_TYPE, /* s[dst+1].nat.type = val */
|
||||
UNW_INSN_LOAD, /* s[dst] = *s[val] */
|
||||
UNW_INSN_MOVE_SCRATCH, /* s[dst] = scratch reg "val" */
|
||||
UNW_INSN_MOVE_CONST, /* s[dst] = constant reg "val" */
|
||||
};
|
||||
|
||||
struct unw_insn {
|
||||
unsigned int opc : 4;
|
||||
unsigned int dst : 9;
|
||||
signed int val : 19;
|
||||
};
|
||||
|
||||
/*
|
||||
* Preserved general static registers (r4-r7) give rise to two script
|
||||
* instructions; everything else yields at most one instruction; at
|
||||
* the end of the script, the psp gets popped, accounting for one more
|
||||
* instruction.
|
||||
*/
|
||||
#define UNW_MAX_SCRIPT_LEN (UNW_NUM_REGS + 5)
|
||||
|
||||
struct unw_script {
|
||||
unsigned long ip; /* ip this script is for */
|
||||
unsigned long pr_mask; /* mask of predicates script depends on */
|
||||
unsigned long pr_val; /* predicate values this script is for */
|
||||
rwlock_t lock;
|
||||
unsigned int flags; /* see UNW_FLAG_* in unwind.h */
|
||||
unsigned short lru_chain; /* used for least-recently-used chain */
|
||||
unsigned short coll_chain; /* used for hash collisions */
|
||||
unsigned short hint; /* hint for next script to try (or -1) */
|
||||
unsigned short count; /* number of instructions in script */
|
||||
struct unw_insn insn[UNW_MAX_SCRIPT_LEN];
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
subdir-y += dig
|
||||
subdir-y += hp
|
||||
subdir-y += sn
|
||||
|
||||
obj-y += bitop.o
|
||||
obj-y += clear_page.o
|
||||
obj-y += clear_user.o
|
||||
obj-y += copy_page_mck.o
|
||||
obj-y += efi_stub.o
|
||||
obj-y += extable.o
|
||||
obj-y += flush.o
|
||||
obj-y += hpsim.o
|
||||
obj-y += linuxextable.o
|
||||
obj-y += machvec.o
|
||||
obj-y += memcpy_mck.o
|
||||
obj-y += memset.o
|
||||
obj-y += numa.o
|
||||
obj-y += pal.o
|
||||
obj-y += strlen.o
|
||||
|
||||
obj-y += __divsi3.o
|
||||
obj-y += __udivsi3.o
|
||||
obj-y += __modsi3.o
|
||||
obj-y += __umodsi3.o
|
||||
obj-y += __divdi3.o
|
||||
obj-y += __udivdi3.o
|
||||
obj-y += __moddi3.o
|
||||
obj-y += __umoddi3.o
|
||||
obj-y += carta_random.o
|
||||
obj-y += io.o
|
||||
|
||||
## variants of divide/modulo
|
||||
## see files in xen/arch/ia64/linux/lib (linux/arch/ia64/lib)
|
||||
__divdi3.o: idiv64.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -o $@ $<
|
||||
|
||||
__udivdi3.o: idiv64.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DUNSIGNED -c -o $@ $<
|
||||
|
||||
__moddi3.o: idiv64.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DMODULO -c -o $@ $<
|
||||
|
||||
__umoddi3.o: idiv64.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DMODULO -DUNSIGNED -c -o $@ $<
|
||||
|
||||
__divsi3.o: idiv32.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -o $@ $<
|
||||
|
||||
__udivsi3.o: idiv32.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DUNSIGNED -c -o $@ $<
|
||||
|
||||
__modsi3.o: idiv32.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DMODULO -c -o $@ $<
|
||||
|
||||
__umodsi3.o: idiv32.S
|
||||
$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -c -DMODULO -DUNSIGNED -c -o $@ $<
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
Source files in this directory are identical copies of linux-2.6.13 files:
|
||||
|
||||
NOTE: DO NOT commit changes to these files! If a file
|
||||
needs to be changed, move it to ../linux-xen and follow
|
||||
the instructions in the README there.
|
||||
|
||||
efi_stub.S -> linux/arch/ia64/kernel/efi_stub.S
|
||||
extable.c -> linux/arch/ia64/mm/extable.c
|
||||
hpsim.S -> linux/arch/ia64/hp/sim/hpsim.S
|
||||
linuxextable.c -> linux/kernel/extable.c
|
||||
machvec.c -> linux/arch/ia64/kernel/machvec.c
|
||||
numa.c -> linux/arch/ia64/mm/numa.c
|
||||
patch.c -> linux/arch/ia64/kernel/patch.c
|
||||
pcdp.h -> linux/drivers/firmware/pcdp.h
|
||||
|
||||
bitop.c -> linux/arch/ia64/lib/bitop.c
|
||||
clear_page.S -> linux/arch/ia64/lib/clear_page.S
|
||||
clear_user.S -> linux/arch/ia64/lib/clear_user.S
|
||||
copy_page_mck.S -> linux/arch/ia64/lib/copy_page_mck.S
|
||||
flush.S -> linux/arch/ia64/lib/flush.S
|
||||
idiv32.S -> linux/arch/ia64/lib/idiv32.S
|
||||
idiv64.S -> linux/arch/ia64/lib/idiv64.S
|
||||
memcpy_mck.S -> linux/arch/ia64/lib/memcpy_mck.S
|
||||
memset.S -> linux/arch/ia64/lib/memset.S
|
||||
strlen.S -> linux/arch/ia64/lib/strlen.S
|
||||
|
||||
# The files below are from Linux-2.6.16.33
|
||||
carta_random.S -> linux/arch/ia64/lib/carta_random.S
|
||||
|
||||
# The files below are from Linux-2.6.19
|
||||
io.c -> linux/arch/ia64/lib/io.c
|
||||
|
||||
# The files below are from Linux-2.6.21
|
||||
pal.S -> linux/arch/ia64/kernel/pal.S
|
||||
@@ -1,88 +0,0 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/intrinsics.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
* Find next zero bit in a bitmap reasonably efficiently..
|
||||
*/
|
||||
|
||||
int __find_next_zero_bit (const void *addr, unsigned long size, unsigned long offset)
|
||||
{
|
||||
unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
|
||||
unsigned long result = offset & ~63UL;
|
||||
unsigned long tmp;
|
||||
|
||||
if (offset >= size)
|
||||
return size;
|
||||
size -= result;
|
||||
offset &= 63UL;
|
||||
if (offset) {
|
||||
tmp = *(p++);
|
||||
tmp |= ~0UL >> (64-offset);
|
||||
if (size < 64)
|
||||
goto found_first;
|
||||
if (~tmp)
|
||||
goto found_middle;
|
||||
size -= 64;
|
||||
result += 64;
|
||||
}
|
||||
while (size & ~63UL) {
|
||||
if (~(tmp = *(p++)))
|
||||
goto found_middle;
|
||||
result += 64;
|
||||
size -= 64;
|
||||
}
|
||||
if (!size)
|
||||
return result;
|
||||
tmp = *p;
|
||||
found_first:
|
||||
tmp |= ~0UL << size;
|
||||
if (tmp == ~0UL) /* any bits zero? */
|
||||
return result + size; /* nope */
|
||||
found_middle:
|
||||
return result + ffz(tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(__find_next_zero_bit);
|
||||
|
||||
/*
|
||||
* Find next bit in a bitmap reasonably efficiently..
|
||||
*/
|
||||
int __find_next_bit(const void *addr, unsigned long size, unsigned long offset)
|
||||
{
|
||||
unsigned long *p = ((unsigned long *) addr) + (offset >> 6);
|
||||
unsigned long result = offset & ~63UL;
|
||||
unsigned long tmp;
|
||||
|
||||
if (offset >= size)
|
||||
return size;
|
||||
size -= result;
|
||||
offset &= 63UL;
|
||||
if (offset) {
|
||||
tmp = *(p++);
|
||||
tmp &= ~0UL << offset;
|
||||
if (size < 64)
|
||||
goto found_first;
|
||||
if (tmp)
|
||||
goto found_middle;
|
||||
size -= 64;
|
||||
result += 64;
|
||||
}
|
||||
while (size & ~63UL) {
|
||||
if ((tmp = *(p++)))
|
||||
goto found_middle;
|
||||
result += 64;
|
||||
size -= 64;
|
||||
}
|
||||
if (!size)
|
||||
return result;
|
||||
tmp = *p;
|
||||
found_first:
|
||||
tmp &= ~0UL >> (64-size);
|
||||
if (tmp == 0UL) /* Are any bits set? */
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + __ffs(tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(__find_next_bit);
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Fast, simple, yet decent quality random number generator based on
|
||||
* a paper by David G. Carta ("Two Fast Implementations of the
|
||||
* `Minimal Standard' Random Number Generator," Communications of the
|
||||
* ACM, January, 1990).
|
||||
*
|
||||
* Copyright (C) 2002 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
#define a r2
|
||||
#define m r3
|
||||
#define lo r8
|
||||
#define hi r9
|
||||
#define t0 r16
|
||||
#define t1 r17
|
||||
#define seed r32
|
||||
|
||||
GLOBAL_ENTRY(carta_random32)
|
||||
movl a = (16807 << 16) | 16807
|
||||
;;
|
||||
pmpyshr2.u t0 = a, seed, 0
|
||||
pmpyshr2.u t1 = a, seed, 16
|
||||
;;
|
||||
unpack2.l t0 = t1, t0
|
||||
dep m = -1, r0, 0, 31
|
||||
;;
|
||||
zxt4 lo = t0
|
||||
shr.u hi = t0, 32
|
||||
;;
|
||||
dep t0 = 0, hi, 15, 49 // t0 = (hi & 0x7fff)
|
||||
;;
|
||||
shl t0 = t0, 16 // t0 = (hi & 0x7fff) << 16
|
||||
shr t1 = hi, 15 // t1 = (hi >> 15)
|
||||
;;
|
||||
add lo = lo, t0
|
||||
;;
|
||||
cmp.gtu p6, p0 = lo, m
|
||||
;;
|
||||
(p6) and lo = lo, m
|
||||
;;
|
||||
(p6) add lo = 1, lo
|
||||
;;
|
||||
add lo = lo, t1
|
||||
;;
|
||||
cmp.gtu p6, p0 = lo, m
|
||||
;;
|
||||
(p6) and lo = lo, m
|
||||
;;
|
||||
(p6) add lo = 1, lo
|
||||
br.ret.sptk.many rp
|
||||
END(carta_random32)
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 1999-2002 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
* Copyright (C) 2002 Ken Chen <kenneth.w.chen@intel.com>
|
||||
*
|
||||
* 1/06/01 davidm Tuned for Itanium.
|
||||
* 2/12/02 kchen Tuned for both Itanium and McKinley
|
||||
* 3/08/02 davidm Some more tweaking
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#ifdef CONFIG_ITANIUM
|
||||
# define L3_LINE_SIZE 64 // Itanium L3 line size
|
||||
# define PREFETCH_LINES 9 // magic number
|
||||
#else
|
||||
# define L3_LINE_SIZE 128 // McKinley L3 line size
|
||||
# define PREFETCH_LINES 12 // magic number
|
||||
#endif
|
||||
|
||||
#define saved_lc r2
|
||||
#define dst_fetch r3
|
||||
#define dst1 r8
|
||||
#define dst2 r9
|
||||
#define dst3 r10
|
||||
#define dst4 r11
|
||||
|
||||
#define dst_last r31
|
||||
|
||||
GLOBAL_ENTRY(clear_page)
|
||||
.prologue
|
||||
.regstk 1,0,0,0
|
||||
mov r16 = PAGE_SIZE/L3_LINE_SIZE-1 // main loop count, -1=repeat/until
|
||||
.save ar.lc, saved_lc
|
||||
mov saved_lc = ar.lc
|
||||
|
||||
.body
|
||||
mov ar.lc = (PREFETCH_LINES - 1)
|
||||
mov dst_fetch = in0
|
||||
adds dst1 = 16, in0
|
||||
adds dst2 = 32, in0
|
||||
;;
|
||||
.fetch: stf.spill.nta [dst_fetch] = f0, L3_LINE_SIZE
|
||||
adds dst3 = 48, in0 // executing this multiple times is harmless
|
||||
br.cloop.sptk.few .fetch
|
||||
;;
|
||||
addl dst_last = (PAGE_SIZE - PREFETCH_LINES*L3_LINE_SIZE), dst_fetch
|
||||
mov ar.lc = r16 // one L3 line per iteration
|
||||
adds dst4 = 64, in0
|
||||
;;
|
||||
#ifdef CONFIG_ITANIUM
|
||||
// Optimized for Itanium
|
||||
1: stf.spill.nta [dst1] = f0, 64
|
||||
stf.spill.nta [dst2] = f0, 64
|
||||
cmp.lt p8,p0=dst_fetch, dst_last
|
||||
;;
|
||||
#else
|
||||
// Optimized for McKinley
|
||||
1: stf.spill.nta [dst1] = f0, 64
|
||||
stf.spill.nta [dst2] = f0, 64
|
||||
stf.spill.nta [dst3] = f0, 64
|
||||
stf.spill.nta [dst4] = f0, 128
|
||||
cmp.lt p8,p0=dst_fetch, dst_last
|
||||
;;
|
||||
stf.spill.nta [dst1] = f0, 64
|
||||
stf.spill.nta [dst2] = f0, 64
|
||||
#endif
|
||||
stf.spill.nta [dst3] = f0, 64
|
||||
(p8) stf.spill.nta [dst_fetch] = f0, L3_LINE_SIZE
|
||||
br.cloop.sptk.few 1b
|
||||
;;
|
||||
mov ar.lc = saved_lc // restore lc
|
||||
br.ret.sptk.many rp
|
||||
END(clear_page)
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* This routine clears to zero a linear memory buffer in user space.
|
||||
*
|
||||
* Inputs:
|
||||
* in0: address of buffer
|
||||
* in1: length of buffer in bytes
|
||||
* Outputs:
|
||||
* r8: number of bytes that didn't get cleared due to a fault
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2001 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
//
|
||||
// arguments
|
||||
//
|
||||
#define buf r32
|
||||
#define len r33
|
||||
|
||||
//
|
||||
// local registers
|
||||
//
|
||||
#define cnt r16
|
||||
#define buf2 r17
|
||||
#define saved_lc r18
|
||||
#define saved_pfs r19
|
||||
#define tmp r20
|
||||
#define len2 r21
|
||||
#define len3 r22
|
||||
|
||||
//
|
||||
// Theory of operations:
|
||||
// - we check whether or not the buffer is small, i.e., less than 17
|
||||
// in which case we do the byte by byte loop.
|
||||
//
|
||||
// - Otherwise we go progressively from 1 byte store to 8byte store in
|
||||
// the head part, the body is a 16byte store loop and we finish we the
|
||||
// tail for the last 15 bytes.
|
||||
// The good point about this breakdown is that the long buffer handling
|
||||
// contains only 2 branches.
|
||||
//
|
||||
// The reason for not using shifting & masking for both the head and the
|
||||
// tail is to stay semantically correct. This routine is not supposed
|
||||
// to write bytes outside of the buffer. While most of the time this would
|
||||
// be ok, we can't tolerate a mistake. A classical example is the case
|
||||
// of multithreaded code were to the extra bytes touched is actually owned
|
||||
// by another thread which runs concurrently to ours. Another, less likely,
|
||||
// example is with device drivers where reading an I/O mapped location may
|
||||
// have side effects (same thing for writing).
|
||||
//
|
||||
|
||||
GLOBAL_ENTRY(__do_clear_user)
|
||||
.prologue
|
||||
.save ar.pfs, saved_pfs
|
||||
alloc saved_pfs=ar.pfs,2,0,0,0
|
||||
cmp.eq p6,p0=r0,len // check for zero length
|
||||
.save ar.lc, saved_lc
|
||||
mov saved_lc=ar.lc // preserve ar.lc (slow)
|
||||
.body
|
||||
;; // avoid WAW on CFM
|
||||
adds tmp=-1,len // br.ctop is repeat/until
|
||||
mov ret0=len // return value is length at this point
|
||||
(p6) br.ret.spnt.many rp
|
||||
;;
|
||||
cmp.lt p6,p0=16,len // if len > 16 then long memset
|
||||
mov ar.lc=tmp // initialize lc for small count
|
||||
(p6) br.cond.dptk .long_do_clear
|
||||
;; // WAR on ar.lc
|
||||
//
|
||||
// worst case 16 iterations, avg 8 iterations
|
||||
//
|
||||
// We could have played with the predicates to use the extra
|
||||
// M slot for 2 stores/iteration but the cost the initialization
|
||||
// the various counters compared to how long the loop is supposed
|
||||
// to last on average does not make this solution viable.
|
||||
//
|
||||
1:
|
||||
EX( .Lexit1, st1 [buf]=r0,1 )
|
||||
adds len=-1,len // countdown length using len
|
||||
br.cloop.dptk 1b
|
||||
;; // avoid RAW on ar.lc
|
||||
//
|
||||
// .Lexit4: comes from byte by byte loop
|
||||
// len contains bytes left
|
||||
.Lexit1:
|
||||
mov ret0=len // faster than using ar.lc
|
||||
mov ar.lc=saved_lc
|
||||
br.ret.sptk.many rp // end of short clear_user
|
||||
|
||||
|
||||
//
|
||||
// At this point we know we have more than 16 bytes to copy
|
||||
// so we focus on alignment (no branches required)
|
||||
//
|
||||
// The use of len/len2 for countdown of the number of bytes left
|
||||
// instead of ret0 is due to the fact that the exception code
|
||||
// changes the values of r8.
|
||||
//
|
||||
.long_do_clear:
|
||||
tbit.nz p6,p0=buf,0 // odd alignment (for long_do_clear)
|
||||
;;
|
||||
EX( .Lexit3, (p6) st1 [buf]=r0,1 ) // 1-byte aligned
|
||||
(p6) adds len=-1,len;; // sync because buf is modified
|
||||
tbit.nz p6,p0=buf,1
|
||||
;;
|
||||
EX( .Lexit3, (p6) st2 [buf]=r0,2 ) // 2-byte aligned
|
||||
(p6) adds len=-2,len;;
|
||||
tbit.nz p6,p0=buf,2
|
||||
;;
|
||||
EX( .Lexit3, (p6) st4 [buf]=r0,4 ) // 4-byte aligned
|
||||
(p6) adds len=-4,len;;
|
||||
tbit.nz p6,p0=buf,3
|
||||
;;
|
||||
EX( .Lexit3, (p6) st8 [buf]=r0,8 ) // 8-byte aligned
|
||||
(p6) adds len=-8,len;;
|
||||
shr.u cnt=len,4 // number of 128-bit (2x64bit) words
|
||||
;;
|
||||
cmp.eq p6,p0=r0,cnt
|
||||
adds tmp=-1,cnt
|
||||
(p6) br.cond.dpnt .dotail // we have less than 16 bytes left
|
||||
;;
|
||||
adds buf2=8,buf // setup second base pointer
|
||||
mov ar.lc=tmp
|
||||
;;
|
||||
|
||||
//
|
||||
// 16bytes/iteration core loop
|
||||
//
|
||||
// The second store can never generate a fault because
|
||||
// we come into the loop only when we are 16-byte aligned.
|
||||
// This means that if we cross a page then it will always be
|
||||
// in the first store and never in the second.
|
||||
//
|
||||
//
|
||||
// We need to keep track of the remaining length. A possible (optimistic)
|
||||
// way would be to use ar.lc and derive how many byte were left by
|
||||
// doing : left= 16*ar.lc + 16. this would avoid the addition at
|
||||
// every iteration.
|
||||
// However we need to keep the synchronization point. A template
|
||||
// M;;MB does not exist and thus we can keep the addition at no
|
||||
// extra cycle cost (use a nop slot anyway). It also simplifies the
|
||||
// (unlikely) error recovery code
|
||||
//
|
||||
|
||||
2: EX(.Lexit3, st8 [buf]=r0,16 )
|
||||
;; // needed to get len correct when error
|
||||
st8 [buf2]=r0,16
|
||||
adds len=-16,len
|
||||
br.cloop.dptk 2b
|
||||
;;
|
||||
mov ar.lc=saved_lc
|
||||
//
|
||||
// tail correction based on len only
|
||||
//
|
||||
// We alternate the use of len3,len2 to allow parallelism and correct
|
||||
// error handling. We also reuse p6/p7 to return correct value.
|
||||
// The addition of len2/len3 does not cost anything more compared to
|
||||
// the regular memset as we had empty slots.
|
||||
//
|
||||
.dotail:
|
||||
mov len2=len // for parallelization of error handling
|
||||
mov len3=len
|
||||
tbit.nz p6,p0=len,3
|
||||
;;
|
||||
EX( .Lexit2, (p6) st8 [buf]=r0,8 ) // at least 8 bytes
|
||||
(p6) adds len3=-8,len2
|
||||
tbit.nz p7,p6=len,2
|
||||
;;
|
||||
EX( .Lexit2, (p7) st4 [buf]=r0,4 ) // at least 4 bytes
|
||||
(p7) adds len2=-4,len3
|
||||
tbit.nz p6,p7=len,1
|
||||
;;
|
||||
EX( .Lexit2, (p6) st2 [buf]=r0,2 ) // at least 2 bytes
|
||||
(p6) adds len3=-2,len2
|
||||
tbit.nz p7,p6=len,0
|
||||
;;
|
||||
EX( .Lexit2, (p7) st1 [buf]=r0 ) // only 1 byte left
|
||||
mov ret0=r0 // success
|
||||
br.ret.sptk.many rp // end of most likely path
|
||||
|
||||
//
|
||||
// Outlined error handling code
|
||||
//
|
||||
|
||||
//
|
||||
// .Lexit3: comes from core loop, need restore pr/lc
|
||||
// len contains bytes left
|
||||
//
|
||||
//
|
||||
// .Lexit2:
|
||||
// if p6 -> coming from st8 or st2 : len2 contains what's left
|
||||
// if p7 -> coming from st4 or st1 : len3 contains what's left
|
||||
// We must restore lc/pr even though might not have been used.
|
||||
.Lexit2:
|
||||
.pred.rel "mutex", p6, p7
|
||||
(p6) mov len=len2
|
||||
(p7) mov len=len3
|
||||
;;
|
||||
//
|
||||
// .Lexit4: comes from head, need not restore pr/lc
|
||||
// len contains bytes left
|
||||
//
|
||||
.Lexit3:
|
||||
mov ret0=len
|
||||
mov ar.lc=saved_lc
|
||||
br.ret.sptk.many rp
|
||||
END(__do_clear_user)
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* McKinley-optimized version of copy_page().
|
||||
*
|
||||
* Copyright (C) 2002 Hewlett-Packard Co
|
||||
* David Mosberger <davidm@hpl.hp.com>
|
||||
*
|
||||
* Inputs:
|
||||
* in0: address of target page
|
||||
* in1: address of source page
|
||||
* Output:
|
||||
* no return value
|
||||
*
|
||||
* General idea:
|
||||
* - use regular loads and stores to prefetch data to avoid consuming M-slot just for
|
||||
* lfetches => good for in-cache performance
|
||||
* - avoid l2 bank-conflicts by not storing into the same 16-byte bank within a single
|
||||
* cycle
|
||||
*
|
||||
* Principle of operation:
|
||||
* First, note that L1 has a line-size of 64 bytes and L2 a line-size of 128 bytes.
|
||||
* To avoid secondary misses in L2, we prefetch both source and destination with a line-size
|
||||
* of 128 bytes. When both of these lines are in the L2 and the first half of the
|
||||
* source line is in L1, we start copying the remaining words. The second half of the
|
||||
* source line is prefetched in an earlier iteration, so that by the time we start
|
||||
* accessing it, it's also present in the L1.
|
||||
*
|
||||
* We use a software-pipelined loop to control the overall operation. The pipeline
|
||||
* has 2*PREFETCH_DIST+K stages. The first PREFETCH_DIST stages are used for prefetching
|
||||
* source cache-lines. The second PREFETCH_DIST stages are used for prefetching destination
|
||||
* cache-lines, the last K stages are used to copy the cache-line words not copied by
|
||||
* the prefetches. The four relevant points in the pipelined are called A, B, C, D:
|
||||
* p[A] is TRUE if a source-line should be prefetched, p[B] is TRUE if a destination-line
|
||||
* should be prefetched, p[C] is TRUE if the second half of an L2 line should be brought
|
||||
* into L1D and p[D] is TRUE if a cacheline needs to be copied.
|
||||
*
|
||||
* This all sounds very complicated, but thanks to the modulo-scheduled loop support,
|
||||
* the resulting code is very regular and quite easy to follow (once you get the idea).
|
||||
*
|
||||
* As a secondary optimization, the first 2*PREFETCH_DIST iterations are implemented
|
||||
* as the separate .prefetch_loop. Logically, this loop performs exactly like the
|
||||
* main-loop (.line_copy), but has all known-to-be-predicated-off instructions removed,
|
||||
* so that each loop iteration is faster (again, good for cached case).
|
||||
*
|
||||
* When reading the code, it helps to keep the following picture in mind:
|
||||
*
|
||||
* word 0 word 1
|
||||
* +------+------+---
|
||||
* | v[x] | t1 | ^
|
||||
* | t2 | t3 | |
|
||||
* | t4 | t5 | |
|
||||
* | t6 | t7 | | 128 bytes
|
||||
* | n[y] | t9 | | (L2 cache line)
|
||||
* | t10 | t11 | |
|
||||
* | t12 | t13 | |
|
||||
* | t14 | t15 | v
|
||||
* +------+------+---
|
||||
*
|
||||
* Here, v[x] is copied by the (memory) prefetch. n[y] is loaded at p[C]
|
||||
* to fetch the second-half of the L2 cache line into L1, and the tX words are copied in
|
||||
* an order that avoids bank conflicts.
|
||||
*/
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#define PREFETCH_DIST 8 // McKinley sustains 16 outstanding L2 misses (8 ld, 8 st)
|
||||
|
||||
#define src0 r2
|
||||
#define src1 r3
|
||||
#define dst0 r9
|
||||
#define dst1 r10
|
||||
#define src_pre_mem r11
|
||||
#define dst_pre_mem r14
|
||||
#define src_pre_l2 r15
|
||||
#define dst_pre_l2 r16
|
||||
#define t1 r17
|
||||
#define t2 r18
|
||||
#define t3 r19
|
||||
#define t4 r20
|
||||
#define t5 t1 // alias!
|
||||
#define t6 t2 // alias!
|
||||
#define t7 t3 // alias!
|
||||
#define t9 t5 // alias!
|
||||
#define t10 t4 // alias!
|
||||
#define t11 t7 // alias!
|
||||
#define t12 t6 // alias!
|
||||
#define t14 t10 // alias!
|
||||
#define t13 r21
|
||||
#define t15 r22
|
||||
|
||||
#define saved_lc r23
|
||||
#define saved_pr r24
|
||||
|
||||
#define A 0
|
||||
#define B (PREFETCH_DIST)
|
||||
#define C (B + PREFETCH_DIST)
|
||||
#define D (C + 3)
|
||||
#define N (D + 1)
|
||||
#define Nrot ((N + 7) & ~7)
|
||||
|
||||
GLOBAL_ENTRY(copy_page)
|
||||
.prologue
|
||||
alloc r8 = ar.pfs, 2, Nrot-2, 0, Nrot
|
||||
|
||||
.rotr v[2*PREFETCH_DIST], n[D-C+1]
|
||||
.rotp p[N]
|
||||
|
||||
.save ar.lc, saved_lc
|
||||
mov saved_lc = ar.lc
|
||||
.save pr, saved_pr
|
||||
mov saved_pr = pr
|
||||
.body
|
||||
|
||||
mov src_pre_mem = in1
|
||||
mov pr.rot = 0x10000
|
||||
mov ar.ec = 1 // special unrolled loop
|
||||
|
||||
mov dst_pre_mem = in0
|
||||
mov ar.lc = 2*PREFETCH_DIST - 1
|
||||
|
||||
add src_pre_l2 = 8*8, in1
|
||||
add dst_pre_l2 = 8*8, in0
|
||||
add src0 = 8, in1 // first t1 src
|
||||
add src1 = 3*8, in1 // first t3 src
|
||||
add dst0 = 8, in0 // first t1 dst
|
||||
add dst1 = 3*8, in0 // first t3 dst
|
||||
mov t1 = (PAGE_SIZE/128) - (2*PREFETCH_DIST) - 1
|
||||
nop.m 0
|
||||
nop.i 0
|
||||
;;
|
||||
// same as .line_copy loop, but with all predicated-off instructions removed:
|
||||
.prefetch_loop:
|
||||
(p[A]) ld8 v[A] = [src_pre_mem], 128 // M0
|
||||
(p[B]) st8 [dst_pre_mem] = v[B], 128 // M2
|
||||
br.ctop.sptk .prefetch_loop
|
||||
;;
|
||||
cmp.eq p16, p0 = r0, r0 // reset p16 to 1 (br.ctop cleared it to zero)
|
||||
mov ar.lc = t1 // with 64KB pages, t1 is too big to fit in 8 bits!
|
||||
mov ar.ec = N // # of stages in pipeline
|
||||
;;
|
||||
.line_copy:
|
||||
(p[D]) ld8 t2 = [src0], 3*8 // M0
|
||||
(p[D]) ld8 t4 = [src1], 3*8 // M1
|
||||
(p[B]) st8 [dst_pre_mem] = v[B], 128 // M2 prefetch dst from memory
|
||||
(p[D]) st8 [dst_pre_l2] = n[D-C], 128 // M3 prefetch dst from L2
|
||||
;;
|
||||
(p[A]) ld8 v[A] = [src_pre_mem], 128 // M0 prefetch src from memory
|
||||
(p[C]) ld8 n[0] = [src_pre_l2], 128 // M1 prefetch src from L2
|
||||
(p[D]) st8 [dst0] = t1, 8 // M2
|
||||
(p[D]) st8 [dst1] = t3, 8 // M3
|
||||
;;
|
||||
(p[D]) ld8 t5 = [src0], 8
|
||||
(p[D]) ld8 t7 = [src1], 3*8
|
||||
(p[D]) st8 [dst0] = t2, 3*8
|
||||
(p[D]) st8 [dst1] = t4, 3*8
|
||||
;;
|
||||
(p[D]) ld8 t6 = [src0], 3*8
|
||||
(p[D]) ld8 t10 = [src1], 8
|
||||
(p[D]) st8 [dst0] = t5, 8
|
||||
(p[D]) st8 [dst1] = t7, 3*8
|
||||
;;
|
||||
(p[D]) ld8 t9 = [src0], 3*8
|
||||
(p[D]) ld8 t11 = [src1], 3*8
|
||||
(p[D]) st8 [dst0] = t6, 3*8
|
||||
(p[D]) st8 [dst1] = t10, 8
|
||||
;;
|
||||
(p[D]) ld8 t12 = [src0], 8
|
||||
(p[D]) ld8 t14 = [src1], 8
|
||||
(p[D]) st8 [dst0] = t9, 3*8
|
||||
(p[D]) st8 [dst1] = t11, 3*8
|
||||
;;
|
||||
(p[D]) ld8 t13 = [src0], 4*8
|
||||
(p[D]) ld8 t15 = [src1], 4*8
|
||||
(p[D]) st8 [dst0] = t12, 8
|
||||
(p[D]) st8 [dst1] = t14, 8
|
||||
;;
|
||||
(p[D-1])ld8 t1 = [src0], 8
|
||||
(p[D-1])ld8 t3 = [src1], 8
|
||||
(p[D]) st8 [dst0] = t13, 4*8
|
||||
(p[D]) st8 [dst1] = t15, 4*8
|
||||
br.ctop.sptk .line_copy
|
||||
;;
|
||||
mov ar.lc = saved_lc
|
||||
mov pr = saved_pr, -1
|
||||
br.ret.sptk.many rp
|
||||
END(copy_page)
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += machvec.o
|
||||
@@ -1,7 +0,0 @@
|
||||
Source files in this directory are identical copies of linux-2.6.19 files:
|
||||
|
||||
NOTE: DO NOT commit changes to these files! If a file
|
||||
needs to be changed, move it to ../linux-xen and follow
|
||||
the instructions in the README there.
|
||||
|
||||
machvec.c -> linux/arch/ia64/dig/machvec.c
|
||||
@@ -1,3 +0,0 @@
|
||||
#define MACHVEC_PLATFORM_NAME dig
|
||||
#define MACHVEC_PLATFORM_HEADER <asm/machvec_dig.h>
|
||||
#include <asm/machvec_init.h>
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* EFI call stub.
|
||||
*
|
||||
* Copyright (C) 1999-2001 Hewlett-Packard Co
|
||||
* David Mosberger <davidm@hpl.hp.com>
|
||||
*
|
||||
* This stub allows us to make EFI calls in physical mode with interrupts
|
||||
* turned off. We need this because we can't call SetVirtualMap() until
|
||||
* the kernel has booted far enough to allow allocation of struct vma_struct
|
||||
* entries (which we would need to map stuff with memory attributes other
|
||||
* than uncached or writeback...). Since the GetTime() service gets called
|
||||
* earlier than that, we need to be able to make physical mode EFI calls from
|
||||
* the kernel.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PSR settings as per SAL spec (Chapter 8 in the "IA-64 System
|
||||
* Abstraction Layer Specification", revision 2.6e). Note that
|
||||
* psr.dfl and psr.dfh MUST be cleared, despite what this manual says.
|
||||
* Otherwise, SAL dies whenever it's trying to do an IA-32 BIOS call
|
||||
* (the br.ia instruction fails unless psr.dfl and psr.dfh are
|
||||
* cleared). Fortunately, SAL promises not to touch the floating
|
||||
* point regs, so at least we don't have to save f2-f127.
|
||||
*/
|
||||
#define PSR_BITS_TO_CLEAR \
|
||||
(IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | \
|
||||
IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \
|
||||
IA64_PSR_DFL | IA64_PSR_DFH)
|
||||
|
||||
#define PSR_BITS_TO_SET \
|
||||
(IA64_PSR_BN)
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
/*
|
||||
* Inputs:
|
||||
* in0 = address of function descriptor of EFI routine to call
|
||||
* in1..in7 = arguments to routine
|
||||
*
|
||||
* Outputs:
|
||||
* r8 = EFI_STATUS returned by called function
|
||||
*/
|
||||
|
||||
GLOBAL_ENTRY(efi_call_phys)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
|
||||
alloc loc1=ar.pfs,8,7,7,0
|
||||
ld8 r2=[in0],8 // load EFI function's entry point
|
||||
mov loc0=rp
|
||||
.body
|
||||
;;
|
||||
mov loc2=gp // save global pointer
|
||||
mov loc4=ar.rsc // save RSE configuration
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
;;
|
||||
ld8 gp=[in0] // load EFI function's global pointer
|
||||
movl r16=PSR_BITS_TO_CLEAR
|
||||
mov loc3=psr // save processor status word
|
||||
movl r17=PSR_BITS_TO_SET
|
||||
;;
|
||||
or loc3=loc3,r17
|
||||
mov b6=r2
|
||||
;;
|
||||
andcm r16=loc3,r16 // get psr with IT, DT, and RT bits cleared
|
||||
br.call.sptk.many rp=ia64_switch_mode_phys
|
||||
.ret0: mov out4=in5
|
||||
mov out0=in1
|
||||
mov out1=in2
|
||||
mov out2=in3
|
||||
mov out3=in4
|
||||
mov out5=in6
|
||||
mov out6=in7
|
||||
mov loc5=r19
|
||||
mov loc6=r20
|
||||
br.call.sptk.many rp=b6 // call the EFI function
|
||||
.ret1: mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
mov r16=loc3
|
||||
mov r19=loc5
|
||||
mov r20=loc6
|
||||
br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode
|
||||
.ret2: mov ar.rsc=loc4 // restore RSE configuration
|
||||
mov ar.pfs=loc1
|
||||
mov rp=loc0
|
||||
mov gp=loc2
|
||||
br.ret.sptk.many rp
|
||||
END(efi_call_phys)
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Kernel exception handling table support. Derived from arch/alpha/mm/extable.c.
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2001-2002, 2004 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/module.h>
|
||||
|
||||
static int cmp_ex(const void *a, const void *b)
|
||||
{
|
||||
const struct exception_table_entry *l = a, *r = b;
|
||||
u64 lip = (u64) &l->addr + l->addr;
|
||||
u64 rip = (u64) &r->addr + r->addr;
|
||||
|
||||
/* avoid overflow */
|
||||
if (lip > rip)
|
||||
return 1;
|
||||
if (lip < rip)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void swap_ex(void *a, void *b, int size)
|
||||
{
|
||||
struct exception_table_entry *l = a, *r = b, tmp;
|
||||
u64 delta = (u64) r - (u64) l;
|
||||
|
||||
tmp = *l;
|
||||
l->addr = r->addr + delta;
|
||||
l->cont = r->cont + delta;
|
||||
r->addr = tmp.addr - delta;
|
||||
r->cont = tmp.cont - delta;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the exception table. It's usually already sorted, but there
|
||||
* may be unordered entries due to multiple text sections (such as the
|
||||
* .init text section). Note that the exception-table-entries contain
|
||||
* location-relative addresses, which requires a bit of care during
|
||||
* sorting to avoid overflows in the offset members (e.g., it would
|
||||
* not be safe to make a temporary copy of an exception-table entry on
|
||||
* the stack, because the stack may be more than 2GB away from the
|
||||
* exception-table).
|
||||
*/
|
||||
void sort_extable (struct exception_table_entry *start,
|
||||
struct exception_table_entry *finish)
|
||||
{
|
||||
sort(start, finish - start, sizeof(struct exception_table_entry),
|
||||
cmp_ex, swap_ex);
|
||||
}
|
||||
|
||||
const struct exception_table_entry *
|
||||
search_extable (const struct exception_table_entry *first,
|
||||
const struct exception_table_entry *last,
|
||||
unsigned long ip)
|
||||
{
|
||||
const struct exception_table_entry *mid;
|
||||
unsigned long mid_ip;
|
||||
long diff;
|
||||
|
||||
while (first <= last) {
|
||||
mid = &first[(last - first)/2];
|
||||
mid_ip = (u64) &mid->addr + mid->addr;
|
||||
diff = mid_ip - ip;
|
||||
if (diff == 0)
|
||||
return mid;
|
||||
else if (diff < 0)
|
||||
first = mid + 1;
|
||||
else
|
||||
last = mid - 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ia64_handle_exception (struct pt_regs *regs, const struct exception_table_entry *e)
|
||||
{
|
||||
long fix = (u64) &e->cont + e->cont;
|
||||
|
||||
regs->r8 = -EFAULT;
|
||||
if (fix & 4)
|
||||
regs->r9 = 0;
|
||||
regs->cr_iip = fix & ~0xf;
|
||||
ia64_psr(regs)->ri = fix & 0x3; /* set continuation slot number */
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Cache flushing routines.
|
||||
*
|
||||
* Copyright (C) 1999-2001, 2005 Hewlett-Packard Co
|
||||
* David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* 05/28/05 Zoltan Menyhart Dynamic stride size
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
|
||||
/*
|
||||
* flush_icache_range(start,end)
|
||||
*
|
||||
* Make i-cache(s) coherent with d-caches.
|
||||
*
|
||||
* Must deal with range from start to end-1 but nothing else (need to
|
||||
* be careful not to touch addresses that may be unmapped).
|
||||
*
|
||||
* Note: "in0" and "in1" are preserved for debugging purposes.
|
||||
*/
|
||||
GLOBAL_ENTRY(flush_icache_range)
|
||||
|
||||
.prologue
|
||||
alloc r2=ar.pfs,2,0,0,0
|
||||
movl r3=ia64_i_cache_stride_shift
|
||||
mov r21=1
|
||||
;;
|
||||
ld8 r20=[r3] // r20: stride shift
|
||||
sub r22=in1,r0,1 // last byte address
|
||||
;;
|
||||
shr.u r23=in0,r20 // start / (stride size)
|
||||
shr.u r22=r22,r20 // (last byte address) / (stride size)
|
||||
shl r21=r21,r20 // r21: stride size of the i-cache(s)
|
||||
;;
|
||||
sub r8=r22,r23 // number of strides - 1
|
||||
shl r24=r23,r20 // r24: addresses for "fc.i" =
|
||||
// "start" rounded down to stride boundary
|
||||
.save ar.lc,r3
|
||||
mov r3=ar.lc // save ar.lc
|
||||
;;
|
||||
|
||||
.body
|
||||
mov ar.lc=r8
|
||||
;;
|
||||
/*
|
||||
* 32 byte aligned loop, even number of (actually 2) bundles
|
||||
*/
|
||||
.Loop: fc.i r24 // issuable on M0 only
|
||||
add r24=r21,r24 // we flush "stride size" bytes per iteration
|
||||
nop.i 0
|
||||
br.cloop.sptk.few .Loop
|
||||
;;
|
||||
sync.i
|
||||
;;
|
||||
srlz.i
|
||||
;;
|
||||
mov ar.lc=r3 // restore ar.lc
|
||||
br.ret.sptk.many rp
|
||||
END(flush_icache_range)
|
||||
@@ -1 +0,0 @@
|
||||
subdir-y += zx1
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += hpzx1_machvec.o
|
||||
@@ -1,7 +0,0 @@
|
||||
Source files in this directory are identical copies of linux-2.6.19 files:
|
||||
|
||||
NOTE: DO NOT commit changes to these files! If a file
|
||||
needs to be changed, move it to ../linux-xen and follow
|
||||
the instructions in the README there.
|
||||
|
||||
hpzx1_machvec.c -> linux/arch/ia64/hp/zx1/hpzx1_machvec.c
|
||||
@@ -1,3 +0,0 @@
|
||||
#define MACHVEC_PLATFORM_NAME hpzx1
|
||||
#define MACHVEC_PLATFORM_HEADER <asm/machvec_hpzx1.h>
|
||||
#include <asm/machvec_init.h>
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
/*
|
||||
* Simulator system call.
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_ssc)
|
||||
mov r15=r36
|
||||
break 0x80001
|
||||
br.ret.sptk.many rp
|
||||
END(ia64_ssc)
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Hewlett-Packard Co
|
||||
* Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* 32-bit integer division.
|
||||
*
|
||||
* This code is based on the application note entitled "Divide, Square Root
|
||||
* and Remainder Algorithms for the IA-64 Architecture". This document
|
||||
* is available as Intel document number 248725-002 or via the web at
|
||||
* http://developer.intel.com/software/opensource/numerics/
|
||||
*
|
||||
* For more details on the theory behind these algorithms, see "IA-64
|
||||
* and Elementary Functions" by Peter Markstein; HP Professional Books
|
||||
* (http://www.hp.com/go/retailbooks/)
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
#ifdef MODULO
|
||||
# define OP mod
|
||||
#else
|
||||
# define OP div
|
||||
#endif
|
||||
|
||||
#ifdef UNSIGNED
|
||||
# define SGN u
|
||||
# define EXTEND zxt4
|
||||
# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b
|
||||
# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b
|
||||
#else
|
||||
# define SGN
|
||||
# define EXTEND sxt4
|
||||
# define INT_TO_FP(a,b) fcvt.xf a=b
|
||||
# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b
|
||||
#endif
|
||||
|
||||
#define PASTE1(a,b) a##b
|
||||
#define PASTE(a,b) PASTE1(a,b)
|
||||
#define NAME PASTE(PASTE(__,SGN),PASTE(OP,si3))
|
||||
|
||||
GLOBAL_ENTRY(NAME)
|
||||
.regstk 2,0,0,0
|
||||
// Transfer inputs to FP registers.
|
||||
mov r2 = 0xffdd // r2 = -34 + 65535 (fp reg format bias)
|
||||
EXTEND in0 = in0 // in0 = a
|
||||
EXTEND in1 = in1 // in1 = b
|
||||
;;
|
||||
setf.sig f8 = in0
|
||||
setf.sig f9 = in1
|
||||
#ifdef MODULO
|
||||
sub in1 = r0, in1 // in1 = -b
|
||||
#endif
|
||||
;;
|
||||
// Convert the inputs to FP, to avoid FP software-assist faults.
|
||||
INT_TO_FP(f8, f8)
|
||||
INT_TO_FP(f9, f9)
|
||||
;;
|
||||
setf.exp f7 = r2 // f7 = 2^-34
|
||||
frcpa.s1 f6, p6 = f8, f9 // y0 = frcpa(b)
|
||||
;;
|
||||
(p6) fmpy.s1 f8 = f8, f6 // q0 = a*y0
|
||||
(p6) fnma.s1 f6 = f9, f6, f1 // e0 = -b*y0 + 1
|
||||
;;
|
||||
#ifdef MODULO
|
||||
setf.sig f9 = in1 // f9 = -b
|
||||
#endif
|
||||
(p6) fma.s1 f8 = f6, f8, f8 // q1 = e0*q0 + q0
|
||||
(p6) fma.s1 f6 = f6, f6, f7 // e1 = e0*e0 + 2^-34
|
||||
;;
|
||||
#ifdef MODULO
|
||||
setf.sig f7 = in0
|
||||
#endif
|
||||
(p6) fma.s1 f6 = f6, f8, f8 // q2 = e1*q1 + q1
|
||||
;;
|
||||
FP_TO_INT(f6, f6) // q = trunc(q2)
|
||||
;;
|
||||
#ifdef MODULO
|
||||
xma.l f6 = f6, f9, f7 // r = q*(-b) + a
|
||||
;;
|
||||
#endif
|
||||
getf.sig r8 = f6 // transfer result to result register
|
||||
br.ret.sptk.many rp
|
||||
END(NAME)
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 1999-2000 Hewlett-Packard Co
|
||||
* Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
*
|
||||
* 64-bit integer division.
|
||||
*
|
||||
* This code is based on the application note entitled "Divide, Square Root
|
||||
* and Remainder Algorithms for the IA-64 Architecture". This document
|
||||
* is available as Intel document number 248725-002 or via the web at
|
||||
* http://developer.intel.com/software/opensource/numerics/
|
||||
*
|
||||
* For more details on the theory behind these algorithms, see "IA-64
|
||||
* and Elementary Functions" by Peter Markstein; HP Professional Books
|
||||
* (http://www.hp.com/go/retailbooks/)
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
#ifdef MODULO
|
||||
# define OP mod
|
||||
#else
|
||||
# define OP div
|
||||
#endif
|
||||
|
||||
#ifdef UNSIGNED
|
||||
# define SGN u
|
||||
# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b
|
||||
# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b
|
||||
#else
|
||||
# define SGN
|
||||
# define INT_TO_FP(a,b) fcvt.xf a=b
|
||||
# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b
|
||||
#endif
|
||||
|
||||
#define PASTE1(a,b) a##b
|
||||
#define PASTE(a,b) PASTE1(a,b)
|
||||
#define NAME PASTE(PASTE(__,SGN),PASTE(OP,di3))
|
||||
|
||||
GLOBAL_ENTRY(NAME)
|
||||
.regstk 2,0,0,0
|
||||
// Transfer inputs to FP registers.
|
||||
setf.sig f8 = in0
|
||||
setf.sig f9 = in1
|
||||
;;
|
||||
// Convert the inputs to FP, to avoid FP software-assist faults.
|
||||
INT_TO_FP(f8, f8)
|
||||
INT_TO_FP(f9, f9)
|
||||
;;
|
||||
frcpa.s1 f11, p6 = f8, f9 // y0 = frcpa(b)
|
||||
;;
|
||||
(p6) fmpy.s1 f7 = f8, f11 // q0 = a*y0
|
||||
(p6) fnma.s1 f6 = f9, f11, f1 // e0 = -b*y0 + 1
|
||||
;;
|
||||
(p6) fma.s1 f10 = f7, f6, f7 // q1 = q0*e0 + q0
|
||||
(p6) fmpy.s1 f7 = f6, f6 // e1 = e0*e0
|
||||
;;
|
||||
#ifdef MODULO
|
||||
sub in1 = r0, in1 // in1 = -b
|
||||
#endif
|
||||
(p6) fma.s1 f10 = f10, f7, f10 // q2 = q1*e1 + q1
|
||||
(p6) fma.s1 f6 = f11, f6, f11 // y1 = y0*e0 + y0
|
||||
;;
|
||||
(p6) fma.s1 f6 = f6, f7, f6 // y2 = y1*e1 + y1
|
||||
(p6) fnma.s1 f7 = f9, f10, f8 // r = -b*q2 + a
|
||||
;;
|
||||
#ifdef MODULO
|
||||
setf.sig f8 = in0 // f8 = a
|
||||
setf.sig f9 = in1 // f9 = -b
|
||||
#endif
|
||||
(p6) fma.s1 f11 = f7, f6, f10 // q3 = r*y2 + q2
|
||||
;;
|
||||
FP_TO_INT(f11, f11) // q = trunc(q3)
|
||||
;;
|
||||
#ifdef MODULO
|
||||
xma.l f11 = f11, f9, f8 // r = q*(-b) + a
|
||||
;;
|
||||
#endif
|
||||
getf.sig r8 = f11 // transfer result to result register
|
||||
br.ret.sptk.many rp
|
||||
END(NAME)
|
||||
@@ -1,164 +0,0 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* Copy data from IO memory space to "real" memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void memcpy_fromio(void *to, const volatile void __iomem *from, long count)
|
||||
{
|
||||
char *dst = to;
|
||||
|
||||
while (count) {
|
||||
count--;
|
||||
*dst++ = readb(from++);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(memcpy_fromio);
|
||||
|
||||
/*
|
||||
* Copy data from "real" memory space to IO memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void memcpy_toio(volatile void __iomem *to, const void *from, long count)
|
||||
{
|
||||
const char *src = from;
|
||||
|
||||
while (count) {
|
||||
count--;
|
||||
writeb(*src++, to++);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(memcpy_toio);
|
||||
|
||||
/*
|
||||
* "memset" on IO memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void memset_io(volatile void __iomem *dst, int c, long count)
|
||||
{
|
||||
unsigned char ch = (char)(c & 0xff);
|
||||
|
||||
while (count) {
|
||||
count--;
|
||||
writeb(ch, dst);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(memset_io);
|
||||
|
||||
#ifdef CONFIG_IA64_GENERIC
|
||||
|
||||
#undef __ia64_inb
|
||||
#undef __ia64_inw
|
||||
#undef __ia64_inl
|
||||
#undef __ia64_outb
|
||||
#undef __ia64_outw
|
||||
#undef __ia64_outl
|
||||
#undef __ia64_readb
|
||||
#undef __ia64_readw
|
||||
#undef __ia64_readl
|
||||
#undef __ia64_readq
|
||||
#undef __ia64_readb_relaxed
|
||||
#undef __ia64_readw_relaxed
|
||||
#undef __ia64_readl_relaxed
|
||||
#undef __ia64_readq_relaxed
|
||||
#undef __ia64_writeb
|
||||
#undef __ia64_writew
|
||||
#undef __ia64_writel
|
||||
#undef __ia64_writeq
|
||||
#undef __ia64_mmiowb
|
||||
|
||||
unsigned int
|
||||
__ia64_inb (unsigned long port)
|
||||
{
|
||||
return ___ia64_inb(port);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
__ia64_inw (unsigned long port)
|
||||
{
|
||||
return ___ia64_inw(port);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
__ia64_inl (unsigned long port)
|
||||
{
|
||||
return ___ia64_inl(port);
|
||||
}
|
||||
|
||||
void
|
||||
__ia64_outb (unsigned char val, unsigned long port)
|
||||
{
|
||||
___ia64_outb(val, port);
|
||||
}
|
||||
|
||||
void
|
||||
__ia64_outw (unsigned short val, unsigned long port)
|
||||
{
|
||||
___ia64_outw(val, port);
|
||||
}
|
||||
|
||||
void
|
||||
__ia64_outl (unsigned int val, unsigned long port)
|
||||
{
|
||||
___ia64_outl(val, port);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
__ia64_readb (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readb (addr);
|
||||
}
|
||||
|
||||
unsigned short
|
||||
__ia64_readw (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readw (addr);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
__ia64_readl (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readl (addr);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
__ia64_readq (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readq (addr);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
__ia64_readb_relaxed (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readb (addr);
|
||||
}
|
||||
|
||||
unsigned short
|
||||
__ia64_readw_relaxed (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readw (addr);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
__ia64_readl_relaxed (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readl (addr);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
__ia64_readq_relaxed (void __iomem *addr)
|
||||
{
|
||||
return ___ia64_readq (addr);
|
||||
}
|
||||
|
||||
void
|
||||
__ia64_mmiowb(void)
|
||||
{
|
||||
___ia64_mmiowb();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IA64_GENERIC */
|
||||
@@ -1,71 +0,0 @@
|
||||
/* Rewritten by Rusty Russell, on the backs of many others...
|
||||
Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
extern void *search_module_extables(unsigned long addr);
|
||||
extern void *__module_text_address(unsigned long addr);
|
||||
extern void *module_text_address(unsigned long addr);
|
||||
|
||||
extern struct exception_table_entry __start___ex_table[];
|
||||
extern struct exception_table_entry __stop___ex_table[];
|
||||
|
||||
/* Sort the kernel's built-in exception table */
|
||||
void __init sort_main_extable(void)
|
||||
{
|
||||
sort_extable(__start___ex_table, __stop___ex_table);
|
||||
}
|
||||
|
||||
/* Given an address, look for it in the exception tables. */
|
||||
const struct exception_table_entry *search_exception_tables(unsigned long addr)
|
||||
{
|
||||
const struct exception_table_entry *e;
|
||||
|
||||
e = search_extable(__start___ex_table, __stop___ex_table-1, addr);
|
||||
if (!e)
|
||||
e = search_module_extables(addr);
|
||||
return e;
|
||||
}
|
||||
|
||||
static int core_kernel_text(unsigned long addr)
|
||||
{
|
||||
if (addr >= (unsigned long)_stext &&
|
||||
addr <= (unsigned long)_etext)
|
||||
return 1;
|
||||
|
||||
if (addr >= (unsigned long)_sinittext &&
|
||||
addr <= (unsigned long)_einittext)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kernel_text_address(unsigned long addr)
|
||||
{
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
return __module_text_address(addr) != NULL;
|
||||
}
|
||||
|
||||
int kernel_text_address(unsigned long addr)
|
||||
{
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
return module_text_address(addr) != NULL;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#ifdef CONFIG_IA64_GENERIC
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
struct ia64_machine_vector ia64_mv;
|
||||
EXPORT_SYMBOL(ia64_mv);
|
||||
|
||||
static struct ia64_machine_vector *
|
||||
lookup_machvec (const char *name)
|
||||
{
|
||||
extern struct ia64_machine_vector machvec_start[];
|
||||
extern struct ia64_machine_vector machvec_end[];
|
||||
struct ia64_machine_vector *mv;
|
||||
|
||||
for (mv = machvec_start; mv < machvec_end; ++mv)
|
||||
if (strcmp (mv->name, name) == 0)
|
||||
return mv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
machvec_init (const char *name)
|
||||
{
|
||||
struct ia64_machine_vector *mv;
|
||||
|
||||
mv = lookup_machvec(name);
|
||||
if (!mv) {
|
||||
panic("generic kernel failed to find machine vector for platform %s!", name);
|
||||
}
|
||||
ia64_mv = *mv;
|
||||
printk(KERN_INFO "booting generic kernel on platform %s\n", name);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IA64_GENERIC */
|
||||
|
||||
void
|
||||
machvec_setup (char **arg)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(machvec_setup);
|
||||
|
||||
void
|
||||
machvec_timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(machvec_timer_interrupt);
|
||||
|
||||
void
|
||||
machvec_dma_sync_single (struct device *hwdev, dma_addr_t dma_handle, size_t size, int dir)
|
||||
{
|
||||
mb();
|
||||
}
|
||||
EXPORT_SYMBOL(machvec_dma_sync_single);
|
||||
|
||||
void
|
||||
machvec_dma_sync_sg (struct device *hwdev, struct scatterlist *sg, int n, int dir)
|
||||
{
|
||||
mb();
|
||||
}
|
||||
EXPORT_SYMBOL(machvec_dma_sync_sg);
|
||||
@@ -1,661 +0,0 @@
|
||||
/*
|
||||
* Itanium 2-optimized version of memcpy and copy_user function
|
||||
*
|
||||
* Inputs:
|
||||
* in0: destination address
|
||||
* in1: source address
|
||||
* in2: number of bytes to copy
|
||||
* Output:
|
||||
* 0 if success, or number of byte NOT copied if error occurred.
|
||||
*
|
||||
* Copyright (C) 2002 Intel Corp.
|
||||
* Copyright (C) 2002 Ken Chen <kenneth.w.chen@intel.com>
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#define EK(y...) EX(y)
|
||||
|
||||
/* McKinley specific optimization */
|
||||
|
||||
#define retval r8
|
||||
#define saved_pfs r31
|
||||
#define saved_lc r10
|
||||
#define saved_pr r11
|
||||
#define saved_in0 r14
|
||||
#define saved_in1 r15
|
||||
#define saved_in2 r16
|
||||
|
||||
#define src0 r2
|
||||
#define src1 r3
|
||||
#define dst0 r17
|
||||
#define dst1 r18
|
||||
#define cnt r9
|
||||
|
||||
/* r19-r30 are temp for each code section */
|
||||
#define PREFETCH_DIST 8
|
||||
#define src_pre_mem r19
|
||||
#define dst_pre_mem r20
|
||||
#define src_pre_l2 r21
|
||||
#define dst_pre_l2 r22
|
||||
#define t1 r23
|
||||
#define t2 r24
|
||||
#define t3 r25
|
||||
#define t4 r26
|
||||
#define t5 t1 // alias!
|
||||
#define t6 t2 // alias!
|
||||
#define t7 t3 // alias!
|
||||
#define n8 r27
|
||||
#define t9 t5 // alias!
|
||||
#define t10 t4 // alias!
|
||||
#define t11 t7 // alias!
|
||||
#define t12 t6 // alias!
|
||||
#define t14 t10 // alias!
|
||||
#define t13 r28
|
||||
#define t15 r29
|
||||
#define tmp r30
|
||||
|
||||
/* defines for long_copy block */
|
||||
#define A 0
|
||||
#define B (PREFETCH_DIST)
|
||||
#define C (B + PREFETCH_DIST)
|
||||
#define D (C + 1)
|
||||
#define N (D + 1)
|
||||
#define Nrot ((N + 7) & ~7)
|
||||
|
||||
/* alias */
|
||||
#define in0 r32
|
||||
#define in1 r33
|
||||
#define in2 r34
|
||||
|
||||
GLOBAL_ENTRY(memcpy)
|
||||
and r28=0x7,in0
|
||||
and r29=0x7,in1
|
||||
mov f6=f0
|
||||
br.cond.sptk .common_code
|
||||
;;
|
||||
END(memcpy)
|
||||
GLOBAL_ENTRY(__copy_user)
|
||||
.prologue
|
||||
// check dest alignment
|
||||
and r28=0x7,in0
|
||||
and r29=0x7,in1
|
||||
mov f6=f1
|
||||
mov saved_in0=in0 // save dest pointer
|
||||
mov saved_in1=in1 // save src pointer
|
||||
mov saved_in2=in2 // save len
|
||||
;;
|
||||
.common_code:
|
||||
cmp.gt p15,p0=8,in2 // check for small size
|
||||
cmp.ne p13,p0=0,r28 // check dest alignment
|
||||
cmp.ne p14,p0=0,r29 // check src alignment
|
||||
add src0=0,in1
|
||||
sub r30=8,r28 // for .align_dest
|
||||
mov retval=r0 // initialize return value
|
||||
;;
|
||||
add dst0=0,in0
|
||||
add dst1=1,in0 // dest odd index
|
||||
cmp.le p6,p0 = 1,r30 // for .align_dest
|
||||
(p15) br.cond.dpnt .memcpy_short
|
||||
(p13) br.cond.dpnt .align_dest
|
||||
(p14) br.cond.dpnt .unaligned_src
|
||||
;;
|
||||
|
||||
// both dest and src are aligned on 8-byte boundary
|
||||
.aligned_src:
|
||||
.save ar.pfs, saved_pfs
|
||||
alloc saved_pfs=ar.pfs,3,Nrot-3,0,Nrot
|
||||
.save pr, saved_pr
|
||||
mov saved_pr=pr
|
||||
|
||||
shr.u cnt=in2,7 // this much cache line
|
||||
;;
|
||||
cmp.lt p6,p0=2*PREFETCH_DIST,cnt
|
||||
cmp.lt p7,p8=1,cnt
|
||||
.save ar.lc, saved_lc
|
||||
mov saved_lc=ar.lc
|
||||
.body
|
||||
add cnt=-1,cnt
|
||||
add src_pre_mem=0,in1 // prefetch src pointer
|
||||
add dst_pre_mem=0,in0 // prefetch dest pointer
|
||||
;;
|
||||
(p7) mov ar.lc=cnt // prefetch count
|
||||
(p8) mov ar.lc=r0
|
||||
(p6) br.cond.dpnt .long_copy
|
||||
;;
|
||||
|
||||
.prefetch:
|
||||
lfetch.fault [src_pre_mem], 128
|
||||
lfetch.fault.excl [dst_pre_mem], 128
|
||||
br.cloop.dptk.few .prefetch
|
||||
;;
|
||||
|
||||
.medium_copy:
|
||||
and tmp=31,in2 // copy length after iteration
|
||||
shr.u r29=in2,5 // number of 32-byte iteration
|
||||
add dst1=8,dst0 // 2nd dest pointer
|
||||
;;
|
||||
add cnt=-1,r29 // ctop iteration adjustment
|
||||
cmp.eq p10,p0=r29,r0 // do we really need to loop?
|
||||
add src1=8,src0 // 2nd src pointer
|
||||
cmp.le p6,p0=8,tmp
|
||||
;;
|
||||
cmp.le p7,p0=16,tmp
|
||||
mov ar.lc=cnt // loop setup
|
||||
cmp.eq p16,p17 = r0,r0
|
||||
mov ar.ec=2
|
||||
(p10) br.dpnt.few .aligned_src_tail
|
||||
;;
|
||||
TEXT_ALIGN(32)
|
||||
1:
|
||||
EX(.ex_handler, (p16) ld8 r34=[src0],16)
|
||||
EK(.ex_handler, (p16) ld8 r38=[src1],16)
|
||||
EX(.ex_handler, (p17) st8 [dst0]=r33,16)
|
||||
EK(.ex_handler, (p17) st8 [dst1]=r37,16)
|
||||
;;
|
||||
EX(.ex_handler, (p16) ld8 r32=[src0],16)
|
||||
EK(.ex_handler, (p16) ld8 r36=[src1],16)
|
||||
EX(.ex_handler, (p16) st8 [dst0]=r34,16)
|
||||
EK(.ex_handler, (p16) st8 [dst1]=r38,16)
|
||||
br.ctop.dptk.few 1b
|
||||
;;
|
||||
|
||||
.aligned_src_tail:
|
||||
EX(.ex_handler, (p6) ld8 t1=[src0])
|
||||
mov ar.lc=saved_lc
|
||||
mov ar.pfs=saved_pfs
|
||||
EX(.ex_hndlr_s, (p7) ld8 t2=[src1],8)
|
||||
cmp.le p8,p0=24,tmp
|
||||
and r21=-8,tmp
|
||||
;;
|
||||
EX(.ex_hndlr_s, (p8) ld8 t3=[src1])
|
||||
EX(.ex_handler, (p6) st8 [dst0]=t1) // store byte 1
|
||||
and in2=7,tmp // remaining length
|
||||
EX(.ex_hndlr_d, (p7) st8 [dst1]=t2,8) // store byte 2
|
||||
add src0=src0,r21 // setting up src pointer
|
||||
add dst0=dst0,r21 // setting up dest pointer
|
||||
;;
|
||||
EX(.ex_handler, (p8) st8 [dst1]=t3) // store byte 3
|
||||
mov pr=saved_pr,-1
|
||||
br.dptk.many .memcpy_short
|
||||
;;
|
||||
|
||||
/* code taken from copy_page_mck */
|
||||
.long_copy:
|
||||
.rotr v[2*PREFETCH_DIST]
|
||||
.rotp p[N]
|
||||
|
||||
mov src_pre_mem = src0
|
||||
mov pr.rot = 0x10000
|
||||
mov ar.ec = 1 // special unrolled loop
|
||||
|
||||
mov dst_pre_mem = dst0
|
||||
|
||||
add src_pre_l2 = 8*8, src0
|
||||
add dst_pre_l2 = 8*8, dst0
|
||||
;;
|
||||
add src0 = 8, src_pre_mem // first t1 src
|
||||
mov ar.lc = 2*PREFETCH_DIST - 1
|
||||
shr.u cnt=in2,7 // number of lines
|
||||
add src1 = 3*8, src_pre_mem // first t3 src
|
||||
add dst0 = 8, dst_pre_mem // first t1 dst
|
||||
add dst1 = 3*8, dst_pre_mem // first t3 dst
|
||||
;;
|
||||
and tmp=127,in2 // remaining bytes after this block
|
||||
add cnt = -(2*PREFETCH_DIST) - 1, cnt
|
||||
// same as .line_copy loop, but with all predicated-off instructions removed:
|
||||
.prefetch_loop:
|
||||
EX(.ex_hndlr_lcpy_1, (p[A]) ld8 v[A] = [src_pre_mem], 128) // M0
|
||||
EK(.ex_hndlr_lcpy_1, (p[B]) st8 [dst_pre_mem] = v[B], 128) // M2
|
||||
br.ctop.sptk .prefetch_loop
|
||||
;;
|
||||
cmp.eq p16, p0 = r0, r0 // reset p16 to 1
|
||||
mov ar.lc = cnt
|
||||
mov ar.ec = N // # of stages in pipeline
|
||||
;;
|
||||
.line_copy:
|
||||
EX(.ex_handler, (p[D]) ld8 t2 = [src0], 3*8) // M0
|
||||
EK(.ex_handler, (p[D]) ld8 t4 = [src1], 3*8) // M1
|
||||
EX(.ex_handler_lcpy, (p[B]) st8 [dst_pre_mem] = v[B], 128) // M2 prefetch dst from memory
|
||||
EK(.ex_handler_lcpy, (p[D]) st8 [dst_pre_l2] = n8, 128) // M3 prefetch dst from L2
|
||||
;;
|
||||
EX(.ex_handler_lcpy, (p[A]) ld8 v[A] = [src_pre_mem], 128) // M0 prefetch src from memory
|
||||
EK(.ex_handler_lcpy, (p[C]) ld8 n8 = [src_pre_l2], 128) // M1 prefetch src from L2
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t1, 8) // M2
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t3, 8) // M3
|
||||
;;
|
||||
EX(.ex_handler, (p[D]) ld8 t5 = [src0], 8)
|
||||
EK(.ex_handler, (p[D]) ld8 t7 = [src1], 3*8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t2, 3*8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t4, 3*8)
|
||||
;;
|
||||
EX(.ex_handler, (p[D]) ld8 t6 = [src0], 3*8)
|
||||
EK(.ex_handler, (p[D]) ld8 t10 = [src1], 8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t5, 8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t7, 3*8)
|
||||
;;
|
||||
EX(.ex_handler, (p[D]) ld8 t9 = [src0], 3*8)
|
||||
EK(.ex_handler, (p[D]) ld8 t11 = [src1], 3*8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t6, 3*8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t10, 8)
|
||||
;;
|
||||
EX(.ex_handler, (p[D]) ld8 t12 = [src0], 8)
|
||||
EK(.ex_handler, (p[D]) ld8 t14 = [src1], 8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t9, 3*8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t11, 3*8)
|
||||
;;
|
||||
EX(.ex_handler, (p[D]) ld8 t13 = [src0], 4*8)
|
||||
EK(.ex_handler, (p[D]) ld8 t15 = [src1], 4*8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t12, 8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t14, 8)
|
||||
;;
|
||||
EX(.ex_handler, (p[C]) ld8 t1 = [src0], 8)
|
||||
EK(.ex_handler, (p[C]) ld8 t3 = [src1], 8)
|
||||
EX(.ex_handler, (p[D]) st8 [dst0] = t13, 4*8)
|
||||
EK(.ex_handler, (p[D]) st8 [dst1] = t15, 4*8)
|
||||
br.ctop.sptk .line_copy
|
||||
;;
|
||||
|
||||
add dst0=-8,dst0
|
||||
add src0=-8,src0
|
||||
mov in2=tmp
|
||||
.restore sp
|
||||
br.sptk.many .medium_copy
|
||||
;;
|
||||
|
||||
#define BLOCK_SIZE 128*32
|
||||
#define blocksize r23
|
||||
#define curlen r24
|
||||
|
||||
// dest is on 8-byte boundary, src is not. We need to do
|
||||
// ld8-ld8, shrp, then st8. Max 8 byte copy per cycle.
|
||||
.unaligned_src:
|
||||
.prologue
|
||||
.save ar.pfs, saved_pfs
|
||||
alloc saved_pfs=ar.pfs,3,5,0,8
|
||||
.save ar.lc, saved_lc
|
||||
mov saved_lc=ar.lc
|
||||
.save pr, saved_pr
|
||||
mov saved_pr=pr
|
||||
.body
|
||||
.4k_block:
|
||||
mov saved_in0=dst0 // need to save all input arguments
|
||||
mov saved_in2=in2
|
||||
mov blocksize=BLOCK_SIZE
|
||||
;;
|
||||
cmp.lt p6,p7=blocksize,in2
|
||||
mov saved_in1=src0
|
||||
;;
|
||||
(p6) mov in2=blocksize
|
||||
;;
|
||||
shr.u r21=in2,7 // this much cache line
|
||||
shr.u r22=in2,4 // number of 16-byte iteration
|
||||
and curlen=15,in2 // copy length after iteration
|
||||
and r30=7,src0 // source alignment
|
||||
;;
|
||||
cmp.lt p7,p8=1,r21
|
||||
add cnt=-1,r21
|
||||
;;
|
||||
|
||||
add src_pre_mem=0,src0 // prefetch src pointer
|
||||
add dst_pre_mem=0,dst0 // prefetch dest pointer
|
||||
and src0=-8,src0 // 1st src pointer
|
||||
(p7) mov ar.lc = cnt
|
||||
(p8) mov ar.lc = r0
|
||||
;;
|
||||
TEXT_ALIGN(32)
|
||||
1: lfetch.fault [src_pre_mem], 128
|
||||
lfetch.fault.excl [dst_pre_mem], 128
|
||||
br.cloop.dptk.few 1b
|
||||
;;
|
||||
|
||||
shladd dst1=r22,3,dst0 // 2nd dest pointer
|
||||
shladd src1=r22,3,src0 // 2nd src pointer
|
||||
cmp.eq p8,p9=r22,r0 // do we really need to loop?
|
||||
cmp.le p6,p7=8,curlen; // have at least 8 byte remaining?
|
||||
add cnt=-1,r22 // ctop iteration adjustment
|
||||
;;
|
||||
EX(.ex_handler, (p9) ld8 r33=[src0],8) // loop primer
|
||||
EK(.ex_handler, (p9) ld8 r37=[src1],8)
|
||||
(p8) br.dpnt.few .noloop
|
||||
;;
|
||||
|
||||
// The jump address is calculated based on src alignment. The COPYU
|
||||
// macro below need to confine its size to power of two, so an entry
|
||||
// can be caulated using shl instead of an expensive multiply. The
|
||||
// size is then hard coded by the following #define to match the
|
||||
// actual size. This make it somewhat tedious when COPYU macro gets
|
||||
// changed and this need to be adjusted to match.
|
||||
#define LOOP_SIZE 6
|
||||
1:
|
||||
mov r29=ip // jmp_table thread
|
||||
mov ar.lc=cnt
|
||||
;;
|
||||
add r29=.jump_table - 1b - (.jmp1-.jump_table), r29
|
||||
shl r28=r30, LOOP_SIZE // jmp_table thread
|
||||
mov ar.ec=2 // loop setup
|
||||
;;
|
||||
add r29=r29,r28 // jmp_table thread
|
||||
cmp.eq p16,p17=r0,r0
|
||||
;;
|
||||
mov b6=r29 // jmp_table thread
|
||||
;;
|
||||
br.cond.sptk.few b6
|
||||
|
||||
// for 8-15 byte case
|
||||
// We will skip the loop, but need to replicate the side effect
|
||||
// that the loop produces.
|
||||
.noloop:
|
||||
EX(.ex_handler, (p6) ld8 r37=[src1],8)
|
||||
add src0=8,src0
|
||||
(p6) shl r25=r30,3
|
||||
;;
|
||||
EX(.ex_handler, (p6) ld8 r27=[src1])
|
||||
(p6) shr.u r28=r37,r25
|
||||
(p6) sub r26=64,r25
|
||||
;;
|
||||
(p6) shl r27=r27,r26
|
||||
;;
|
||||
(p6) or r21=r28,r27
|
||||
|
||||
.unaligned_src_tail:
|
||||
/* check if we have more than blocksize to copy, if so go back */
|
||||
cmp.gt p8,p0=saved_in2,blocksize
|
||||
;;
|
||||
(p8) add dst0=saved_in0,blocksize
|
||||
(p8) add src0=saved_in1,blocksize
|
||||
(p8) sub in2=saved_in2,blocksize
|
||||
(p8) br.dpnt .4k_block
|
||||
;;
|
||||
|
||||
/* we have up to 15 byte to copy in the tail.
|
||||
* part of work is already done in the jump table code
|
||||
* we are at the following state.
|
||||
* src side:
|
||||
*
|
||||
* xxxxxx xx <----- r21 has xxxxxxxx already
|
||||
* -------- -------- --------
|
||||
* 0 8 16
|
||||
* ^
|
||||
* |
|
||||
* src1
|
||||
*
|
||||
* dst
|
||||
* -------- -------- --------
|
||||
* ^
|
||||
* |
|
||||
* dst1
|
||||
*/
|
||||
EX(.ex_handler, (p6) st8 [dst1]=r21,8) // more than 8 byte to copy
|
||||
(p6) add curlen=-8,curlen // update length
|
||||
mov ar.pfs=saved_pfs
|
||||
;;
|
||||
mov ar.lc=saved_lc
|
||||
mov pr=saved_pr,-1
|
||||
mov in2=curlen // remaining length
|
||||
mov dst0=dst1 // dest pointer
|
||||
add src0=src1,r30 // forward by src alignment
|
||||
;;
|
||||
|
||||
// 7 byte or smaller.
|
||||
.memcpy_short:
|
||||
cmp.le p8,p9 = 1,in2
|
||||
cmp.le p10,p11 = 2,in2
|
||||
cmp.le p12,p13 = 3,in2
|
||||
cmp.le p14,p15 = 4,in2
|
||||
add src1=1,src0 // second src pointer
|
||||
add dst1=1,dst0 // second dest pointer
|
||||
;;
|
||||
|
||||
EX(.ex_handler_short, (p8) ld1 t1=[src0],2)
|
||||
EK(.ex_handler_short, (p10) ld1 t2=[src1],2)
|
||||
(p9) br.ret.dpnt rp // 0 byte copy
|
||||
;;
|
||||
|
||||
EX(.ex_handler_short, (p8) st1 [dst0]=t1,2)
|
||||
EK(.ex_handler_short, (p10) st1 [dst1]=t2,2)
|
||||
(p11) br.ret.dpnt rp // 1 byte copy
|
||||
|
||||
EX(.ex_handler_short, (p12) ld1 t3=[src0],2)
|
||||
EK(.ex_handler_short, (p14) ld1 t4=[src1],2)
|
||||
(p13) br.ret.dpnt rp // 2 byte copy
|
||||
;;
|
||||
|
||||
cmp.le p6,p7 = 5,in2
|
||||
cmp.le p8,p9 = 6,in2
|
||||
cmp.le p10,p11 = 7,in2
|
||||
|
||||
EX(.ex_handler_short, (p12) st1 [dst0]=t3,2)
|
||||
EK(.ex_handler_short, (p14) st1 [dst1]=t4,2)
|
||||
(p15) br.ret.dpnt rp // 3 byte copy
|
||||
;;
|
||||
|
||||
EX(.ex_handler_short, (p6) ld1 t5=[src0],2)
|
||||
EK(.ex_handler_short, (p8) ld1 t6=[src1],2)
|
||||
(p7) br.ret.dpnt rp // 4 byte copy
|
||||
;;
|
||||
|
||||
EX(.ex_handler_short, (p6) st1 [dst0]=t5,2)
|
||||
EK(.ex_handler_short, (p8) st1 [dst1]=t6,2)
|
||||
(p9) br.ret.dptk rp // 5 byte copy
|
||||
|
||||
EX(.ex_handler_short, (p10) ld1 t7=[src0],2)
|
||||
(p11) br.ret.dptk rp // 6 byte copy
|
||||
;;
|
||||
|
||||
EX(.ex_handler_short, (p10) st1 [dst0]=t7,2)
|
||||
br.ret.dptk rp // done all cases
|
||||
|
||||
|
||||
/* Align dest to nearest 8-byte boundary. We know we have at
|
||||
* least 7 bytes to copy, enough to crawl to 8-byte boundary.
|
||||
* Actual number of byte to crawl depend on the dest alignment.
|
||||
* 7 byte or less is taken care at .memcpy_short
|
||||
|
||||
* src0 - source even index
|
||||
* src1 - source odd index
|
||||
* dst0 - dest even index
|
||||
* dst1 - dest odd index
|
||||
* r30 - distance to 8-byte boundary
|
||||
*/
|
||||
|
||||
.align_dest:
|
||||
add src1=1,in1 // source odd index
|
||||
cmp.le p7,p0 = 2,r30 // for .align_dest
|
||||
cmp.le p8,p0 = 3,r30 // for .align_dest
|
||||
EX(.ex_handler_short, (p6) ld1 t1=[src0],2)
|
||||
cmp.le p9,p0 = 4,r30 // for .align_dest
|
||||
cmp.le p10,p0 = 5,r30
|
||||
;;
|
||||
EX(.ex_handler_short, (p7) ld1 t2=[src1],2)
|
||||
EK(.ex_handler_short, (p8) ld1 t3=[src0],2)
|
||||
cmp.le p11,p0 = 6,r30
|
||||
EX(.ex_handler_short, (p6) st1 [dst0] = t1,2)
|
||||
cmp.le p12,p0 = 7,r30
|
||||
;;
|
||||
EX(.ex_handler_short, (p9) ld1 t4=[src1],2)
|
||||
EK(.ex_handler_short, (p10) ld1 t5=[src0],2)
|
||||
EX(.ex_handler_short, (p7) st1 [dst1] = t2,2)
|
||||
EK(.ex_handler_short, (p8) st1 [dst0] = t3,2)
|
||||
;;
|
||||
EX(.ex_handler_short, (p11) ld1 t6=[src1],2)
|
||||
EK(.ex_handler_short, (p12) ld1 t7=[src0],2)
|
||||
cmp.eq p6,p7=r28,r29
|
||||
EX(.ex_handler_short, (p9) st1 [dst1] = t4,2)
|
||||
EK(.ex_handler_short, (p10) st1 [dst0] = t5,2)
|
||||
sub in2=in2,r30
|
||||
;;
|
||||
EX(.ex_handler_short, (p11) st1 [dst1] = t6,2)
|
||||
EK(.ex_handler_short, (p12) st1 [dst0] = t7)
|
||||
add dst0=in0,r30 // setup arguments
|
||||
add src0=in1,r30
|
||||
(p6) br.cond.dptk .aligned_src
|
||||
(p7) br.cond.dpnt .unaligned_src
|
||||
;;
|
||||
|
||||
/* main loop body in jump table format */
|
||||
#define COPYU(shift) \
|
||||
1: \
|
||||
EX(.ex_handler, (p16) ld8 r32=[src0],8); /* 1 */ \
|
||||
EK(.ex_handler, (p16) ld8 r36=[src1],8); \
|
||||
(p17) shrp r35=r33,r34,shift;; /* 1 */ \
|
||||
EX(.ex_handler, (p6) ld8 r22=[src1]); /* common, prime for tail section */ \
|
||||
nop.m 0; \
|
||||
(p16) shrp r38=r36,r37,shift; \
|
||||
EX(.ex_handler, (p17) st8 [dst0]=r35,8); /* 1 */ \
|
||||
EK(.ex_handler, (p17) st8 [dst1]=r39,8); \
|
||||
br.ctop.dptk.few 1b;; \
|
||||
(p7) add src1=-8,src1; /* back out for <8 byte case */ \
|
||||
shrp r21=r22,r38,shift; /* speculative work */ \
|
||||
br.sptk.few .unaligned_src_tail /* branch out of jump table */ \
|
||||
;;
|
||||
TEXT_ALIGN(32)
|
||||
.jump_table:
|
||||
COPYU(8) // unaligned cases
|
||||
.jmp1:
|
||||
COPYU(16)
|
||||
COPYU(24)
|
||||
COPYU(32)
|
||||
COPYU(40)
|
||||
COPYU(48)
|
||||
COPYU(56)
|
||||
|
||||
#undef A
|
||||
#undef B
|
||||
#undef C
|
||||
#undef D
|
||||
|
||||
/*
|
||||
* Due to lack of local tag support in gcc 2.x assembler, it is not clear which
|
||||
* instruction failed in the bundle. The exception algorithm is that we
|
||||
* first figure out the faulting address, then detect if there is any
|
||||
* progress made on the copy, if so, redo the copy from last known copied
|
||||
* location up to the faulting address (exclusive). In the copy_from_user
|
||||
* case, remaining byte in kernel buffer will be zeroed.
|
||||
*
|
||||
* Take copy_from_user as an example, in the code there are multiple loads
|
||||
* in a bundle and those multiple loads could span over two pages, the
|
||||
* faulting address is calculated as page_round_down(max(src0, src1)).
|
||||
* This is based on knowledge that if we can access one byte in a page, we
|
||||
* can access any byte in that page.
|
||||
*
|
||||
* predicate used in the exception handler:
|
||||
* p6-p7: direction
|
||||
* p10-p11: src faulting addr calculation
|
||||
* p12-p13: dst faulting addr calculation
|
||||
*/
|
||||
|
||||
#define A r19
|
||||
#define B r20
|
||||
#define C r21
|
||||
#define D r22
|
||||
#define F r28
|
||||
|
||||
#define memset_arg0 r32
|
||||
#define memset_arg2 r33
|
||||
|
||||
#define saved_retval loc0
|
||||
#define saved_rtlink loc1
|
||||
#define saved_pfs_stack loc2
|
||||
|
||||
.ex_hndlr_s:
|
||||
add src0=8,src0
|
||||
br.sptk .ex_handler
|
||||
;;
|
||||
.ex_hndlr_d:
|
||||
add dst0=8,dst0
|
||||
br.sptk .ex_handler
|
||||
;;
|
||||
.ex_hndlr_lcpy_1:
|
||||
mov src1=src_pre_mem
|
||||
mov dst1=dst_pre_mem
|
||||
cmp.gtu p10,p11=src_pre_mem,saved_in1
|
||||
cmp.gtu p12,p13=dst_pre_mem,saved_in0
|
||||
;;
|
||||
(p10) add src0=8,saved_in1
|
||||
(p11) mov src0=saved_in1
|
||||
(p12) add dst0=8,saved_in0
|
||||
(p13) mov dst0=saved_in0
|
||||
br.sptk .ex_handler
|
||||
.ex_handler_lcpy:
|
||||
// in line_copy block, the preload addresses should always ahead
|
||||
// of the other two src/dst pointers. Furthermore, src1/dst1 should
|
||||
// always ahead of src0/dst0.
|
||||
mov src1=src_pre_mem
|
||||
mov dst1=dst_pre_mem
|
||||
.ex_handler:
|
||||
mov pr=saved_pr,-1 // first restore pr, lc, and pfs
|
||||
mov ar.lc=saved_lc
|
||||
mov ar.pfs=saved_pfs
|
||||
;;
|
||||
.ex_handler_short: // fault occurred in these sections didn't change pr, lc, pfs
|
||||
cmp.ltu p6,p7=saved_in0, saved_in1 // get the copy direction
|
||||
cmp.ltu p10,p11=src0,src1
|
||||
cmp.ltu p12,p13=dst0,dst1
|
||||
fcmp.eq p8,p0=f6,f0 // is it memcpy?
|
||||
mov tmp = dst0
|
||||
;;
|
||||
(p11) mov src1 = src0 // pick the larger of the two
|
||||
(p13) mov dst0 = dst1 // make dst0 the smaller one
|
||||
(p13) mov dst1 = tmp // and dst1 the larger one
|
||||
;;
|
||||
(p6) dep F = r0,dst1,0,PAGE_SHIFT // usr dst round down to page boundary
|
||||
(p7) dep F = r0,src1,0,PAGE_SHIFT // usr src round down to page boundary
|
||||
;;
|
||||
(p6) cmp.le p14,p0=dst0,saved_in0 // no progress has been made on store
|
||||
(p7) cmp.le p14,p0=src0,saved_in1 // no progress has been made on load
|
||||
mov retval=saved_in2
|
||||
(p8) ld1 tmp=[src1] // force an oops for memcpy call
|
||||
(p8) st1 [dst1]=r0 // force an oops for memcpy call
|
||||
(p14) br.ret.sptk.many rp
|
||||
|
||||
/*
|
||||
* The remaining byte to copy is calculated as:
|
||||
*
|
||||
* A = (faulting_addr - orig_src) -> len to faulting ld address
|
||||
* or
|
||||
* (faulting_addr - orig_dst) -> len to faulting st address
|
||||
* B = (cur_dst - orig_dst) -> len copied so far
|
||||
* C = A - B -> len need to be copied
|
||||
* D = orig_len - A -> len need to be zeroed
|
||||
*/
|
||||
(p6) sub A = F, saved_in0
|
||||
(p7) sub A = F, saved_in1
|
||||
clrrrb
|
||||
;;
|
||||
alloc saved_pfs_stack=ar.pfs,3,3,3,0
|
||||
sub B = dst0, saved_in0 // how many byte copied so far
|
||||
;;
|
||||
sub C = A, B
|
||||
sub D = saved_in2, A
|
||||
;;
|
||||
cmp.gt p8,p0=C,r0 // more than 1 byte?
|
||||
add memset_arg0=saved_in0, A
|
||||
(p6) mov memset_arg2=0 // copy_to_user should not call memset
|
||||
(p7) mov memset_arg2=D // copy_from_user need to have kbuf zeroed
|
||||
mov r8=0
|
||||
mov saved_retval = D
|
||||
mov saved_rtlink = b0
|
||||
|
||||
add out0=saved_in0, B
|
||||
add out1=saved_in1, B
|
||||
mov out2=C
|
||||
(p8) br.call.sptk.few b0=__copy_user // recursive call
|
||||
;;
|
||||
|
||||
add saved_retval=saved_retval,r8 // above might return non-zero value
|
||||
cmp.gt p8,p0=memset_arg2,r0 // more than 1 byte?
|
||||
mov out0=memset_arg0 // *s
|
||||
mov out1=r0 // c
|
||||
mov out2=memset_arg2 // n
|
||||
(p8) br.call.sptk.few b0=memset
|
||||
;;
|
||||
|
||||
mov retval=saved_retval
|
||||
mov ar.pfs=saved_pfs_stack
|
||||
mov b0=saved_rtlink
|
||||
br.ret.sptk.many rp
|
||||
|
||||
/* end of McKinley specific optimization */
|
||||
END(__copy_user)
|
||||
@@ -1,362 +0,0 @@
|
||||
/* Optimized version of the standard memset() function.
|
||||
|
||||
Copyright (c) 2002 Hewlett-Packard Co/CERN
|
||||
Sverre Jarp <Sverre.Jarp@cern.ch>
|
||||
|
||||
Return: dest
|
||||
|
||||
Inputs:
|
||||
in0: dest
|
||||
in1: value
|
||||
in2: count
|
||||
|
||||
The algorithm is fairly straightforward: set byte by byte until we
|
||||
we get to a 16B-aligned address, then loop on 128 B chunks using an
|
||||
early store as prefetching, then loop on 32B chucks, then clear remaining
|
||||
words, finally clear remaining bytes.
|
||||
Since a stf.spill f0 can store 16B in one go, we use this instruction
|
||||
to get peak speed when value = 0. */
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#undef ret
|
||||
|
||||
#define dest in0
|
||||
#define value in1
|
||||
#define cnt in2
|
||||
|
||||
#define tmp r31
|
||||
#define save_lc r30
|
||||
#define ptr0 r29
|
||||
#define ptr1 r28
|
||||
#define ptr2 r27
|
||||
#define ptr3 r26
|
||||
#define ptr9 r24
|
||||
#define loopcnt r23
|
||||
#define linecnt r22
|
||||
#define bytecnt r21
|
||||
|
||||
#define fvalue f6
|
||||
|
||||
// This routine uses only scratch predicate registers (p6 - p15)
|
||||
#define p_scr p6 // default register for same-cycle branches
|
||||
#define p_nz p7
|
||||
#define p_zr p8
|
||||
#define p_unalgn p9
|
||||
#define p_y p11
|
||||
#define p_n p12
|
||||
#define p_yy p13
|
||||
#define p_nn p14
|
||||
|
||||
#define MIN1 15
|
||||
#define MIN1P1HALF 8
|
||||
#define LINE_SIZE 128
|
||||
#define LSIZE_SH 7 // shift amount
|
||||
#define PREF_AHEAD 8
|
||||
|
||||
GLOBAL_ENTRY(memset)
|
||||
{ .mmi
|
||||
.prologue
|
||||
alloc tmp = ar.pfs, 3, 0, 0, 0
|
||||
lfetch.nt1 [dest] //
|
||||
.save ar.lc, save_lc
|
||||
mov.i save_lc = ar.lc
|
||||
.body
|
||||
} { .mmi
|
||||
mov ret0 = dest // return value
|
||||
cmp.ne p_nz, p_zr = value, r0 // use stf.spill if value is zero
|
||||
cmp.eq p_scr, p0 = cnt, r0
|
||||
;; }
|
||||
{ .mmi
|
||||
and ptr2 = -(MIN1+1), dest // aligned address
|
||||
and tmp = MIN1, dest // prepare to check for correct alignment
|
||||
tbit.nz p_y, p_n = dest, 0 // Do we have an odd address? (M_B_U)
|
||||
} { .mib
|
||||
mov ptr1 = dest
|
||||
mux1 value = value, @brcst // create 8 identical bytes in word
|
||||
(p_scr) br.ret.dpnt.many rp // return immediately if count = 0
|
||||
;; }
|
||||
{ .mib
|
||||
cmp.ne p_unalgn, p0 = tmp, r0 //
|
||||
} { .mib
|
||||
sub bytecnt = (MIN1+1), tmp // NB: # of bytes to move is 1 higher than loopcnt
|
||||
cmp.gt p_scr, p0 = 16, cnt // is it a minimalistic task?
|
||||
(p_scr) br.cond.dptk.many .move_bytes_unaligned // go move just a few (M_B_U)
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_unalgn) add ptr1 = (MIN1+1), ptr2 // after alignment
|
||||
(p_unalgn) add ptr2 = MIN1P1HALF, ptr2 // after alignment
|
||||
(p_unalgn) tbit.nz.unc p_y, p_n = bytecnt, 3 // should we do a st8 ?
|
||||
;; }
|
||||
{ .mib
|
||||
(p_y) add cnt = -8, cnt //
|
||||
(p_unalgn) tbit.nz.unc p_yy, p_nn = bytecnt, 2 // should we do a st4 ?
|
||||
} { .mib
|
||||
(p_y) st8 [ptr2] = value,-4 //
|
||||
(p_n) add ptr2 = 4, ptr2 //
|
||||
;; }
|
||||
{ .mib
|
||||
(p_yy) add cnt = -4, cnt //
|
||||
(p_unalgn) tbit.nz.unc p_y, p_n = bytecnt, 1 // should we do a st2 ?
|
||||
} { .mib
|
||||
(p_yy) st4 [ptr2] = value,-2 //
|
||||
(p_nn) add ptr2 = 2, ptr2 //
|
||||
;; }
|
||||
{ .mmi
|
||||
mov tmp = LINE_SIZE+1 // for compare
|
||||
(p_y) add cnt = -2, cnt //
|
||||
(p_unalgn) tbit.nz.unc p_yy, p_nn = bytecnt, 0 // should we do a st1 ?
|
||||
} { .mmi
|
||||
setf.sig fvalue=value // transfer value to FLP side
|
||||
(p_y) st2 [ptr2] = value,-1 //
|
||||
(p_n) add ptr2 = 1, ptr2 //
|
||||
;; }
|
||||
|
||||
{ .mmi
|
||||
(p_yy) st1 [ptr2] = value //
|
||||
cmp.gt p_scr, p0 = tmp, cnt // is it a minimalistic task?
|
||||
} { .mbb
|
||||
(p_yy) add cnt = -1, cnt //
|
||||
(p_scr) br.cond.dpnt.many .fraction_of_line // go move just a few
|
||||
;; }
|
||||
|
||||
{ .mib
|
||||
nop.m 0
|
||||
shr.u linecnt = cnt, LSIZE_SH
|
||||
(p_zr) br.cond.dptk.many .l1b // Jump to use stf.spill
|
||||
;; }
|
||||
|
||||
TEXT_ALIGN(32) // --------------------- // L1A: store ahead into cache lines; fill later
|
||||
{ .mmi
|
||||
and tmp = -(LINE_SIZE), cnt // compute end of range
|
||||
mov ptr9 = ptr1 // used for prefetching
|
||||
and cnt = (LINE_SIZE-1), cnt // remainder
|
||||
} { .mmi
|
||||
mov loopcnt = PREF_AHEAD-1 // default prefetch loop
|
||||
cmp.gt p_scr, p0 = PREF_AHEAD, linecnt // check against actual value
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_scr) add loopcnt = -1, linecnt //
|
||||
add ptr2 = 8, ptr1 // start of stores (beyond prefetch stores)
|
||||
add ptr1 = tmp, ptr1 // first address beyond total range
|
||||
;; }
|
||||
{ .mmi
|
||||
add tmp = -1, linecnt // next loop count
|
||||
mov.i ar.lc = loopcnt //
|
||||
;; }
|
||||
.pref_l1a:
|
||||
{ .mib
|
||||
stf8 [ptr9] = fvalue, 128 // Do stores one cache line apart
|
||||
nop.i 0
|
||||
br.cloop.dptk.few .pref_l1a
|
||||
;; }
|
||||
{ .mmi
|
||||
add ptr0 = 16, ptr2 // Two stores in parallel
|
||||
mov.i ar.lc = tmp //
|
||||
;; }
|
||||
.l1ax:
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 8
|
||||
stf8 [ptr0] = fvalue, 8
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 24
|
||||
stf8 [ptr0] = fvalue, 24
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 8
|
||||
stf8 [ptr0] = fvalue, 8
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 24
|
||||
stf8 [ptr0] = fvalue, 24
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 8
|
||||
stf8 [ptr0] = fvalue, 8
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 24
|
||||
stf8 [ptr0] = fvalue, 24
|
||||
;; }
|
||||
{ .mmi
|
||||
stf8 [ptr2] = fvalue, 8
|
||||
stf8 [ptr0] = fvalue, 32
|
||||
cmp.lt p_scr, p0 = ptr9, ptr1 // do we need more prefetching?
|
||||
;; }
|
||||
{ .mmb
|
||||
stf8 [ptr2] = fvalue, 24
|
||||
(p_scr) stf8 [ptr9] = fvalue, 128
|
||||
br.cloop.dptk.few .l1ax
|
||||
;; }
|
||||
{ .mbb
|
||||
cmp.le p_scr, p0 = 8, cnt // just a few bytes left ?
|
||||
(p_scr) br.cond.dpnt.many .fraction_of_line // Branch no. 2
|
||||
br.cond.dpnt.many .move_bytes_from_alignment // Branch no. 3
|
||||
;; }
|
||||
|
||||
TEXT_ALIGN(32)
|
||||
.l1b: // ------------------------------------ // L1B: store ahead into cache lines; fill later
|
||||
{ .mmi
|
||||
and tmp = -(LINE_SIZE), cnt // compute end of range
|
||||
mov ptr9 = ptr1 // used for prefetching
|
||||
and cnt = (LINE_SIZE-1), cnt // remainder
|
||||
} { .mmi
|
||||
mov loopcnt = PREF_AHEAD-1 // default prefetch loop
|
||||
cmp.gt p_scr, p0 = PREF_AHEAD, linecnt // check against actual value
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_scr) add loopcnt = -1, linecnt
|
||||
add ptr2 = 16, ptr1 // start of stores (beyond prefetch stores)
|
||||
add ptr1 = tmp, ptr1 // first address beyond total range
|
||||
;; }
|
||||
{ .mmi
|
||||
add tmp = -1, linecnt // next loop count
|
||||
mov.i ar.lc = loopcnt
|
||||
;; }
|
||||
.pref_l1b:
|
||||
{ .mib
|
||||
stf.spill [ptr9] = f0, 128 // Do stores one cache line apart
|
||||
nop.i 0
|
||||
br.cloop.dptk.few .pref_l1b
|
||||
;; }
|
||||
{ .mmi
|
||||
add ptr0 = 16, ptr2 // Two stores in parallel
|
||||
mov.i ar.lc = tmp
|
||||
;; }
|
||||
.l1bx:
|
||||
{ .mmi
|
||||
stf.spill [ptr2] = f0, 32
|
||||
stf.spill [ptr0] = f0, 32
|
||||
;; }
|
||||
{ .mmi
|
||||
stf.spill [ptr2] = f0, 32
|
||||
stf.spill [ptr0] = f0, 32
|
||||
;; }
|
||||
{ .mmi
|
||||
stf.spill [ptr2] = f0, 32
|
||||
stf.spill [ptr0] = f0, 64
|
||||
cmp.lt p_scr, p0 = ptr9, ptr1 // do we need more prefetching?
|
||||
;; }
|
||||
{ .mmb
|
||||
stf.spill [ptr2] = f0, 32
|
||||
(p_scr) stf.spill [ptr9] = f0, 128
|
||||
br.cloop.dptk.few .l1bx
|
||||
;; }
|
||||
{ .mib
|
||||
cmp.gt p_scr, p0 = 8, cnt // just a few bytes left ?
|
||||
(p_scr) br.cond.dpnt.many .move_bytes_from_alignment //
|
||||
;; }
|
||||
|
||||
.fraction_of_line:
|
||||
{ .mib
|
||||
add ptr2 = 16, ptr1
|
||||
shr.u loopcnt = cnt, 5 // loopcnt = cnt / 32
|
||||
;; }
|
||||
{ .mib
|
||||
cmp.eq p_scr, p0 = loopcnt, r0
|
||||
add loopcnt = -1, loopcnt
|
||||
(p_scr) br.cond.dpnt.many .store_words
|
||||
;; }
|
||||
{ .mib
|
||||
and cnt = 0x1f, cnt // compute the remaining cnt
|
||||
mov.i ar.lc = loopcnt
|
||||
;; }
|
||||
TEXT_ALIGN(32)
|
||||
.l2: // ------------------------------------ // L2A: store 32B in 2 cycles
|
||||
{ .mmb
|
||||
stf8 [ptr1] = fvalue, 8
|
||||
stf8 [ptr2] = fvalue, 8
|
||||
;; } { .mmb
|
||||
stf8 [ptr1] = fvalue, 24
|
||||
stf8 [ptr2] = fvalue, 24
|
||||
br.cloop.dptk.many .l2
|
||||
;; }
|
||||
.store_words:
|
||||
{ .mib
|
||||
cmp.gt p_scr, p0 = 8, cnt // just a few bytes left ?
|
||||
(p_scr) br.cond.dpnt.many .move_bytes_from_alignment // Branch
|
||||
;; }
|
||||
|
||||
{ .mmi
|
||||
stf8 [ptr1] = fvalue, 8 // store
|
||||
cmp.le p_y, p_n = 16, cnt
|
||||
add cnt = -8, cnt // subtract
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_y) stf8 [ptr1] = fvalue, 8 // store
|
||||
(p_y) cmp.le.unc p_yy, p_nn = 16, cnt
|
||||
(p_y) add cnt = -8, cnt // subtract
|
||||
;; }
|
||||
{ .mmi // store
|
||||
(p_yy) stf8 [ptr1] = fvalue, 8
|
||||
(p_yy) add cnt = -8, cnt // subtract
|
||||
;; }
|
||||
|
||||
.move_bytes_from_alignment:
|
||||
{ .mib
|
||||
cmp.eq p_scr, p0 = cnt, r0
|
||||
tbit.nz.unc p_y, p0 = cnt, 2 // should we terminate with a st4 ?
|
||||
(p_scr) br.cond.dpnt.few .restore_and_exit
|
||||
;; }
|
||||
{ .mib
|
||||
(p_y) st4 [ptr1] = value,4
|
||||
tbit.nz.unc p_yy, p0 = cnt, 1 // should we terminate with a st2 ?
|
||||
;; }
|
||||
{ .mib
|
||||
(p_yy) st2 [ptr1] = value,2
|
||||
tbit.nz.unc p_y, p0 = cnt, 0 // should we terminate with a st1 ?
|
||||
;; }
|
||||
|
||||
{ .mib
|
||||
(p_y) st1 [ptr1] = value
|
||||
;; }
|
||||
.restore_and_exit:
|
||||
{ .mib
|
||||
nop.m 0
|
||||
mov.i ar.lc = save_lc
|
||||
br.ret.sptk.many rp
|
||||
;; }
|
||||
|
||||
.move_bytes_unaligned:
|
||||
{ .mmi
|
||||
.pred.rel "mutex",p_y, p_n
|
||||
.pred.rel "mutex",p_yy, p_nn
|
||||
(p_n) cmp.le p_yy, p_nn = 4, cnt
|
||||
(p_y) cmp.le p_yy, p_nn = 5, cnt
|
||||
(p_n) add ptr2 = 2, ptr1
|
||||
} { .mmi
|
||||
(p_y) add ptr2 = 3, ptr1
|
||||
(p_y) st1 [ptr1] = value, 1 // fill 1 (odd-aligned) byte [15, 14 (or less) left]
|
||||
(p_y) add cnt = -1, cnt
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_yy) cmp.le.unc p_y, p0 = 8, cnt
|
||||
add ptr3 = ptr1, cnt // prepare last store
|
||||
mov.i ar.lc = save_lc
|
||||
} { .mmi
|
||||
(p_yy) st2 [ptr1] = value, 4 // fill 2 (aligned) bytes
|
||||
(p_yy) st2 [ptr2] = value, 4 // fill 2 (aligned) bytes [11, 10 (o less) left]
|
||||
(p_yy) add cnt = -4, cnt
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_y) cmp.le.unc p_yy, p0 = 8, cnt
|
||||
add ptr3 = -1, ptr3 // last store
|
||||
tbit.nz p_scr, p0 = cnt, 1 // will there be a st2 at the end ?
|
||||
} { .mmi
|
||||
(p_y) st2 [ptr1] = value, 4 // fill 2 (aligned) bytes
|
||||
(p_y) st2 [ptr2] = value, 4 // fill 2 (aligned) bytes [7, 6 (or less) left]
|
||||
(p_y) add cnt = -4, cnt
|
||||
;; }
|
||||
{ .mmi
|
||||
(p_yy) st2 [ptr1] = value, 4 // fill 2 (aligned) bytes
|
||||
(p_yy) st2 [ptr2] = value, 4 // fill 2 (aligned) bytes [3, 2 (or less) left]
|
||||
tbit.nz p_y, p0 = cnt, 0 // will there be a st1 at the end ?
|
||||
} { .mmi
|
||||
(p_yy) add cnt = -4, cnt
|
||||
;; }
|
||||
{ .mmb
|
||||
(p_scr) st2 [ptr1] = value // fill 2 (aligned) bytes
|
||||
(p_y) st1 [ptr3] = value // fill last byte (using ptr3)
|
||||
br.ret.sptk.many rp
|
||||
}
|
||||
END(memset)
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* This file contains NUMA specific variables and functions which can
|
||||
* be split away from DISCONTIGMEM and are used on NUMA machines with
|
||||
* contiguous memory.
|
||||
*
|
||||
* 2002/08/07 Erich Focht <efocht@ess.nec.de>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/node.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/numa.h>
|
||||
|
||||
|
||||
/*
|
||||
* The following structures are usually initialized by ACPI or
|
||||
* similar mechanisms and describe the NUMA characteristics of the machine.
|
||||
*/
|
||||
int num_node_memblks;
|
||||
struct node_memblk_s node_memblk[NR_NODE_MEMBLKS];
|
||||
struct node_cpuid_s node_cpuid[NR_CPUS];
|
||||
/*
|
||||
* This is a matrix with "distances" between nodes, they should be
|
||||
* proportional to the memory access latency ratios.
|
||||
*/
|
||||
u8 numa_slit[MAX_NUMNODES * MAX_NUMNODES];
|
||||
|
||||
/* Identify which cnode a physical address resides on */
|
||||
int
|
||||
paddr_to_nid(unsigned long paddr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_node_memblks; i++)
|
||||
if (paddr >= node_memblk[i].start_paddr &&
|
||||
paddr < node_memblk[i].start_paddr + node_memblk[i].size)
|
||||
break;
|
||||
|
||||
return (i < num_node_memblks) ? node_memblk[i].nid : (num_node_memblks ? -1 : 0);
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* PAL Firmware support
|
||||
* IA-64 Processor Programmers Reference Vol 2
|
||||
*
|
||||
* Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
|
||||
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
|
||||
* Copyright (C) 1999-2001, 2003 Hewlett-Packard Co
|
||||
* David Mosberger <davidm@hpl.hp.com>
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*
|
||||
* 05/22/2000 eranian Added support for stacked register calls
|
||||
* 05/24/2000 eranian Added support for physical mode static calls
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
.data
|
||||
pal_entry_point:
|
||||
data8 ia64_pal_default_handler
|
||||
.text
|
||||
|
||||
/*
|
||||
* Set the PAL entry point address. This could be written in C code, but we
|
||||
* do it here to keep it all in one module (besides, it's so trivial that it's
|
||||
* not a big deal).
|
||||
*
|
||||
* in0 Address of the PAL entry point (text address, NOT a function
|
||||
* descriptor).
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_pal_handler_init)
|
||||
alloc r3=ar.pfs,1,0,0,0
|
||||
movl r2=pal_entry_point
|
||||
;;
|
||||
st8 [r2]=in0
|
||||
br.ret.sptk.many rp
|
||||
END(ia64_pal_handler_init)
|
||||
|
||||
/*
|
||||
* Default PAL call handler. This needs to be coded in assembly because it
|
||||
* uses the static calling convention, i.e., the RSE may not be used and
|
||||
* calls are done via "br.cond" (not "br.call").
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_pal_default_handler)
|
||||
mov r8=-1
|
||||
br.cond.sptk.many rp
|
||||
END(ia64_pal_default_handler)
|
||||
|
||||
/*
|
||||
* Make a PAL call using the static calling convention.
|
||||
*
|
||||
* in0 Index of PAL service
|
||||
* in1 - in3 Remaining PAL arguments
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_pal_call_static)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4)
|
||||
alloc loc1 = ar.pfs,4,5,0,0
|
||||
movl loc2 = pal_entry_point
|
||||
1: {
|
||||
mov r28 = in0
|
||||
mov r29 = in1
|
||||
mov r8 = ip
|
||||
}
|
||||
;;
|
||||
ld8 loc2 = [loc2] // loc2 <- entry point
|
||||
adds r8 = 1f-1b,r8
|
||||
mov loc4=ar.rsc // save RSE configuration
|
||||
;;
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
mov loc3 = psr
|
||||
mov loc0 = rp
|
||||
.body
|
||||
mov r30 = in2
|
||||
|
||||
mov r31 = in3
|
||||
mov b7 = loc2
|
||||
|
||||
rsm psr.i
|
||||
;;
|
||||
mov rp = r8
|
||||
br.cond.sptk.many b7
|
||||
1: mov psr.l = loc3
|
||||
mov ar.rsc = loc4 // restore RSE configuration
|
||||
mov ar.pfs = loc1
|
||||
mov rp = loc0
|
||||
;;
|
||||
srlz.d // seralize restoration of psr.l
|
||||
br.ret.sptk.many b0
|
||||
END(ia64_pal_call_static)
|
||||
|
||||
/*
|
||||
* Make a PAL call using the stacked registers calling convention.
|
||||
*
|
||||
* Inputs:
|
||||
* in0 Index of PAL service
|
||||
* in2 - in3 Remaining PAL arguments
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_pal_call_stacked)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4)
|
||||
alloc loc1 = ar.pfs,4,4,4,0
|
||||
movl loc2 = pal_entry_point
|
||||
|
||||
mov r28 = in0 // Index MUST be copied to r28
|
||||
mov out0 = in0 // AND in0 of PAL function
|
||||
mov loc0 = rp
|
||||
.body
|
||||
;;
|
||||
ld8 loc2 = [loc2] // loc2 <- entry point
|
||||
mov out1 = in1
|
||||
mov out2 = in2
|
||||
mov out3 = in3
|
||||
mov loc3 = psr
|
||||
;;
|
||||
rsm psr.i
|
||||
mov b7 = loc2
|
||||
;;
|
||||
br.call.sptk.many rp=b7 // now make the call
|
||||
.ret0: mov psr.l = loc3
|
||||
mov ar.pfs = loc1
|
||||
mov rp = loc0
|
||||
;;
|
||||
srlz.d // serialize restoration of psr.l
|
||||
br.ret.sptk.many b0
|
||||
END(ia64_pal_call_stacked)
|
||||
|
||||
/*
|
||||
* Make a physical mode PAL call using the static registers calling convention.
|
||||
*
|
||||
* Inputs:
|
||||
* in0 Index of PAL service
|
||||
* in2 - in3 Remaining PAL arguments
|
||||
*
|
||||
* PSR_LP, PSR_TB, PSR_ID, PSR_DA are never set by the kernel.
|
||||
* So we don't need to clear them.
|
||||
*/
|
||||
#define PAL_PSR_BITS_TO_CLEAR \
|
||||
(IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_DB | IA64_PSR_RT |\
|
||||
IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \
|
||||
IA64_PSR_DFL | IA64_PSR_DFH)
|
||||
|
||||
#define PAL_PSR_BITS_TO_SET \
|
||||
(IA64_PSR_BN)
|
||||
|
||||
|
||||
GLOBAL_ENTRY(ia64_pal_call_phys_static)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(4)
|
||||
alloc loc1 = ar.pfs,4,7,0,0
|
||||
movl loc2 = pal_entry_point
|
||||
1: {
|
||||
mov r28 = in0 // copy procedure index
|
||||
mov r8 = ip // save ip to compute branch
|
||||
mov loc0 = rp // save rp
|
||||
}
|
||||
.body
|
||||
;;
|
||||
ld8 loc2 = [loc2] // loc2 <- entry point
|
||||
mov r29 = in1 // first argument
|
||||
mov r30 = in2 // copy arg2
|
||||
mov r31 = in3 // copy arg3
|
||||
;;
|
||||
mov loc3 = psr // save psr
|
||||
adds r8 = 1f-1b,r8 // calculate return address for call
|
||||
;;
|
||||
mov loc4=ar.rsc // save RSE configuration
|
||||
dep.z loc2=loc2,0,61 // convert pal entry point to physical
|
||||
tpa r8=r8 // convert rp to physical
|
||||
;;
|
||||
mov b7 = loc2 // install target to branch reg
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
movl r16=PAL_PSR_BITS_TO_CLEAR
|
||||
movl r17=PAL_PSR_BITS_TO_SET
|
||||
;;
|
||||
or loc3=loc3,r17 // add in psr the bits to set
|
||||
;;
|
||||
andcm r16=loc3,r16 // removes bits to clear from psr
|
||||
br.call.sptk.many rp=ia64_switch_mode_phys
|
||||
mov rp = r8 // install return address (physical)
|
||||
mov loc5 = r19
|
||||
mov loc6 = r20
|
||||
br.cond.sptk.many b7
|
||||
1:
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
mov r16=loc3 // r16= original psr
|
||||
mov r19=loc5
|
||||
mov r20=loc6
|
||||
br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode
|
||||
mov psr.l = loc3 // restore init PSR
|
||||
|
||||
mov ar.pfs = loc1
|
||||
mov rp = loc0
|
||||
;;
|
||||
mov ar.rsc=loc4 // restore RSE configuration
|
||||
srlz.d // seralize restoration of psr.l
|
||||
br.ret.sptk.many b0
|
||||
END(ia64_pal_call_phys_static)
|
||||
|
||||
/*
|
||||
* Make a PAL call using the stacked registers in physical mode.
|
||||
*
|
||||
* Inputs:
|
||||
* in0 Index of PAL service
|
||||
* in2 - in3 Remaining PAL arguments
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_pal_call_phys_stacked)
|
||||
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5)
|
||||
alloc loc1 = ar.pfs,5,7,4,0
|
||||
movl loc2 = pal_entry_point
|
||||
1: {
|
||||
mov r28 = in0 // copy procedure index
|
||||
mov loc0 = rp // save rp
|
||||
}
|
||||
.body
|
||||
;;
|
||||
ld8 loc2 = [loc2] // loc2 <- entry point
|
||||
mov loc3 = psr // save psr
|
||||
;;
|
||||
mov loc4=ar.rsc // save RSE configuration
|
||||
dep.z loc2=loc2,0,61 // convert pal entry point to physical
|
||||
;;
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
movl r16=PAL_PSR_BITS_TO_CLEAR
|
||||
movl r17=PAL_PSR_BITS_TO_SET
|
||||
;;
|
||||
or loc3=loc3,r17 // add in psr the bits to set
|
||||
mov b7 = loc2 // install target to branch reg
|
||||
;;
|
||||
andcm r16=loc3,r16 // removes bits to clear from psr
|
||||
br.call.sptk.many rp=ia64_switch_mode_phys
|
||||
|
||||
mov out0 = in0 // first argument
|
||||
mov out1 = in1 // copy arg2
|
||||
mov out2 = in2 // copy arg3
|
||||
mov out3 = in3 // copy arg3
|
||||
mov loc5 = r19
|
||||
mov loc6 = r20
|
||||
|
||||
br.call.sptk.many rp=b7 // now make the call
|
||||
|
||||
mov ar.rsc=0 // put RSE in enforced lazy, LE mode
|
||||
mov r16=loc3 // r16= original psr
|
||||
mov r19=loc5
|
||||
mov r20=loc6
|
||||
br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode
|
||||
|
||||
mov psr.l = loc3 // restore init PSR
|
||||
mov ar.pfs = loc1
|
||||
mov rp = loc0
|
||||
;;
|
||||
mov ar.rsc=loc4 // restore RSE configuration
|
||||
srlz.d // seralize restoration of psr.l
|
||||
br.ret.sptk.many b0
|
||||
END(ia64_pal_call_phys_stacked)
|
||||
|
||||
/*
|
||||
* Save scratch fp scratch regs which aren't saved in pt_regs already
|
||||
* (fp10-fp15).
|
||||
*
|
||||
* NOTE: We need to do this since firmware (SAL and PAL) may use any of the
|
||||
* scratch regs fp-low partition.
|
||||
*
|
||||
* Inputs:
|
||||
* in0 Address of stack storage for fp regs
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_save_scratch_fpregs)
|
||||
alloc r3=ar.pfs,1,0,0,0
|
||||
add r2=16,in0
|
||||
;;
|
||||
stf.spill [in0] = f10,32
|
||||
stf.spill [r2] = f11,32
|
||||
;;
|
||||
stf.spill [in0] = f12,32
|
||||
stf.spill [r2] = f13,32
|
||||
;;
|
||||
stf.spill [in0] = f14,32
|
||||
stf.spill [r2] = f15,32
|
||||
br.ret.sptk.many rp
|
||||
END(ia64_save_scratch_fpregs)
|
||||
|
||||
/*
|
||||
* Load scratch fp scratch regs (fp10-fp15)
|
||||
*
|
||||
* Inputs:
|
||||
* in0 Address of stack storage for fp regs
|
||||
*/
|
||||
GLOBAL_ENTRY(ia64_load_scratch_fpregs)
|
||||
alloc r3=ar.pfs,1,0,0,0
|
||||
add r2=16,in0
|
||||
;;
|
||||
ldf.fill f10 = [in0],32
|
||||
ldf.fill f11 = [r2],32
|
||||
;;
|
||||
ldf.fill f12 = [in0],32
|
||||
ldf.fill f13 = [r2],32
|
||||
;;
|
||||
ldf.fill f14 = [in0],32
|
||||
ldf.fill f15 = [r2],32
|
||||
br.ret.sptk.many rp
|
||||
END(ia64_load_scratch_fpregs)
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Definitions for PCDP-defined console devices
|
||||
*
|
||||
* v1.0a: http://www.dig64.org/specifications/DIG64_HCDPv10a_01.pdf
|
||||
* v2.0: http://www.dig64.org/specifications/DIG64_PCDPv20.pdf
|
||||
*
|
||||
* (c) Copyright 2002, 2004 Hewlett-Packard Development Company, L.P.
|
||||
* Khalid Aziz <khalid.aziz@hp.com>
|
||||
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define PCDP_CONSOLE 0
|
||||
#define PCDP_DEBUG 1
|
||||
#define PCDP_CONSOLE_OUTPUT 2
|
||||
#define PCDP_CONSOLE_INPUT 3
|
||||
|
||||
#define PCDP_UART (0 << 3)
|
||||
#define PCDP_VGA (1 << 3)
|
||||
#define PCDP_USB (2 << 3)
|
||||
|
||||
/* pcdp_uart.type and pcdp_device.type */
|
||||
#define PCDP_CONSOLE_UART (PCDP_UART | PCDP_CONSOLE)
|
||||
#define PCDP_DEBUG_UART (PCDP_UART | PCDP_DEBUG)
|
||||
#define PCDP_CONSOLE_VGA (PCDP_VGA | PCDP_CONSOLE_OUTPUT)
|
||||
#define PCDP_CONSOLE_USB (PCDP_USB | PCDP_CONSOLE_INPUT)
|
||||
|
||||
/* pcdp_uart.flags */
|
||||
#define PCDP_UART_EDGE_SENSITIVE (1 << 0)
|
||||
#define PCDP_UART_ACTIVE_LOW (1 << 1)
|
||||
#define PCDP_UART_PRIMARY_CONSOLE (1 << 2)
|
||||
#define PCDP_UART_IRQ (1 << 6) /* in pci_func for rev < 3 */
|
||||
#define PCDP_UART_PCI (1 << 7) /* in pci_func for rev < 3 */
|
||||
|
||||
struct pcdp_uart {
|
||||
u8 type;
|
||||
u8 bits;
|
||||
u8 parity;
|
||||
u8 stop_bits;
|
||||
u8 pci_seg;
|
||||
u8 pci_bus;
|
||||
u8 pci_dev;
|
||||
u8 pci_func;
|
||||
u64 baud;
|
||||
struct acpi_generic_address addr;
|
||||
u16 pci_dev_id;
|
||||
u16 pci_vendor_id;
|
||||
u32 gsi;
|
||||
u32 clock_rate;
|
||||
u8 pci_prog_intfc;
|
||||
u8 flags;
|
||||
u16 conout_index;
|
||||
u32 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PCDP_IF_PCI 1
|
||||
|
||||
/* pcdp_if_pci.trans */
|
||||
#define PCDP_PCI_TRANS_IOPORT 0x02
|
||||
#define PCDP_PCI_TRANS_MMIO 0x01
|
||||
|
||||
struct pcdp_if_pci {
|
||||
u8 interconnect;
|
||||
u8 reserved;
|
||||
u16 length;
|
||||
u8 segment;
|
||||
u8 bus;
|
||||
u8 dev;
|
||||
u8 fun;
|
||||
u16 dev_id;
|
||||
u16 vendor_id;
|
||||
u32 acpi_interrupt;
|
||||
u64 mmio_tra;
|
||||
u64 ioport_tra;
|
||||
u8 flags;
|
||||
u8 trans;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcdp_vga {
|
||||
u8 count; /* address space descriptors */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* pcdp_device.flags */
|
||||
#define PCDP_PRIMARY_CONSOLE 1
|
||||
|
||||
struct pcdp_device {
|
||||
u8 type;
|
||||
u8 flags;
|
||||
u16 length;
|
||||
u16 efi_index;
|
||||
/* next data is pcdp_if_pci or pcdp_if_acpi (not yet supported) */
|
||||
/* next data is device specific type (currently only pcdp_vga) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcdp {
|
||||
u8 signature[4];
|
||||
u32 length;
|
||||
u8 rev; /* PCDP v2.0 is rev 3 */
|
||||
u8 chksum;
|
||||
u8 oemid[6];
|
||||
u8 oem_tabid[8];
|
||||
u32 oem_rev;
|
||||
u8 creator_id[4];
|
||||
u32 creator_rev;
|
||||
u32 num_uarts;
|
||||
struct pcdp_uart uart[0]; /* actual size is num_uarts */
|
||||
/* remainder of table is pcdp_device structures */
|
||||
} __attribute__((packed));
|
||||
@@ -1,2 +0,0 @@
|
||||
subdir-y += kernel
|
||||
subdir-y += pci
|
||||
@@ -1,3 +0,0 @@
|
||||
obj-y += machvec.o
|
||||
obj-y += pio_phys.o
|
||||
obj-y += ptc_deadlock.o
|
||||
@@ -1,9 +0,0 @@
|
||||
Source files in this directory are identical copies of linux-2.6.19 files:
|
||||
|
||||
NOTE: DO NOT commit changes to these files! If a file
|
||||
needs to be changed, move it to ../linux-xen and follow
|
||||
the instructions in the README there.
|
||||
|
||||
machvec.c -> linux/arch/ia64/sn/kernel/machvec.c
|
||||
pio_phys.S -> linux/arch/ia64/sn/kernel/pio_phys.S
|
||||
ptc_deadlock.S -> linux/arch/ia64/sn/kernel/sn2/ptc_deadlock.S
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (c) 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#define MACHVEC_PLATFORM_NAME sn2
|
||||
#define MACHVEC_PLATFORM_HEADER <asm/machvec_sn2.h>
|
||||
#include <asm/machvec_init.h>
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
|
||||
*
|
||||
* This file contains macros used to access MMR registers via
|
||||
* uncached physical addresses.
|
||||
* pio_phys_read_mmr - read an MMR
|
||||
* pio_phys_write_mmr - write an MMR
|
||||
* pio_atomic_phys_write_mmrs - atomically write 1 or 2 MMRs with psr.ic=0
|
||||
* Second MMR will be skipped if address is NULL
|
||||
*
|
||||
* Addresses passed to these routines should be uncached physical addresses
|
||||
* ie., 0x80000....
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
GLOBAL_ENTRY(pio_phys_read_mmr)
|
||||
.prologue
|
||||
.regstk 1,0,0,0
|
||||
.body
|
||||
mov r2=psr
|
||||
rsm psr.i | psr.dt
|
||||
;;
|
||||
srlz.d
|
||||
ld8.acq r8=[r32]
|
||||
;;
|
||||
mov psr.l=r2;;
|
||||
srlz.d
|
||||
br.ret.sptk.many rp
|
||||
END(pio_phys_read_mmr)
|
||||
|
||||
GLOBAL_ENTRY(pio_phys_write_mmr)
|
||||
.prologue
|
||||
.regstk 2,0,0,0
|
||||
.body
|
||||
mov r2=psr
|
||||
rsm psr.i | psr.dt
|
||||
;;
|
||||
srlz.d
|
||||
st8.rel [r32]=r33
|
||||
;;
|
||||
mov psr.l=r2;;
|
||||
srlz.d
|
||||
br.ret.sptk.many rp
|
||||
END(pio_phys_write_mmr)
|
||||
|
||||
GLOBAL_ENTRY(pio_atomic_phys_write_mmrs)
|
||||
.prologue
|
||||
.regstk 4,0,0,0
|
||||
.body
|
||||
mov r2=psr
|
||||
cmp.ne p9,p0=r34,r0;
|
||||
rsm psr.i | psr.dt | psr.ic
|
||||
;;
|
||||
srlz.d
|
||||
st8.rel [r32]=r33
|
||||
(p9) st8.rel [r34]=r35
|
||||
;;
|
||||
mov psr.l=r2;;
|
||||
srlz.d
|
||||
br.ret.sptk.many rp
|
||||
END(pio_atomic_phys_write_mmrs)
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2000-2005 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/sn/shub_mmr.h>
|
||||
|
||||
#define DEADLOCKBIT SH_PIO_WRITE_STATUS_WRITE_DEADLOCK_SHFT
|
||||
#define WRITECOUNTMASK SH_PIO_WRITE_STATUS_PENDING_WRITE_COUNT_MASK
|
||||
#define ALIAS_OFFSET 8
|
||||
|
||||
|
||||
.global sn2_ptc_deadlock_recovery_core
|
||||
.proc sn2_ptc_deadlock_recovery_core
|
||||
|
||||
sn2_ptc_deadlock_recovery_core:
|
||||
.regstk 6,0,0,0
|
||||
|
||||
ptc0 = in0
|
||||
data0 = in1
|
||||
ptc1 = in2
|
||||
data1 = in3
|
||||
piowc = in4
|
||||
zeroval = in5
|
||||
piowcphy = r30
|
||||
psrsave = r2
|
||||
scr1 = r16
|
||||
scr2 = r17
|
||||
mask = r18
|
||||
|
||||
|
||||
extr.u piowcphy=piowc,0,61;; // Convert piowc to uncached physical address
|
||||
dep piowcphy=-1,piowcphy,63,1
|
||||
movl mask=WRITECOUNTMASK
|
||||
mov r8=r0
|
||||
|
||||
1:
|
||||
cmp.ne p8,p9=r0,ptc1 // Test for shub type (ptc1 non-null on shub1)
|
||||
// p8 = 1 if shub1, p9 = 1 if shub2
|
||||
|
||||
add scr2=ALIAS_OFFSET,piowc // Address of WRITE_STATUS alias register
|
||||
mov scr1=7;; // Clear DEADLOCK, WRITE_ERROR, MULTI_WRITE_ERROR
|
||||
(p8) st8.rel [scr2]=scr1;;
|
||||
(p9) ld8.acq scr1=[scr2];;
|
||||
|
||||
5: ld8.acq scr1=[piowc];; // Wait for PIOs to complete.
|
||||
hint @pause
|
||||
and scr2=scr1,mask;; // mask of writecount bits
|
||||
cmp.ne p6,p0=zeroval,scr2
|
||||
(p6) br.cond.sptk 5b
|
||||
|
||||
|
||||
|
||||
////////////// BEGIN PHYSICAL MODE ////////////////////
|
||||
mov psrsave=psr // Disable IC (no PMIs)
|
||||
rsm psr.i | psr.dt | psr.ic;;
|
||||
srlz.i;;
|
||||
|
||||
st8.rel [ptc0]=data0 // Write PTC0 & wait for completion.
|
||||
|
||||
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
|
||||
hint @pause
|
||||
and scr2=scr1,mask;; // mask of writecount bits
|
||||
cmp.ne p6,p0=zeroval,scr2
|
||||
(p6) br.cond.sptk 5b;;
|
||||
|
||||
tbit.nz p8,p7=scr1,DEADLOCKBIT;;// Test for DEADLOCK
|
||||
(p7) cmp.ne p7,p0=r0,ptc1;; // Test for non-null ptc1
|
||||
|
||||
(p7) st8.rel [ptc1]=data1;; // Now write PTC1.
|
||||
|
||||
5: ld8.acq scr1=[piowcphy];; // Wait for PIOs to complete.
|
||||
hint @pause
|
||||
and scr2=scr1,mask;; // mask of writecount bits
|
||||
cmp.ne p6,p0=zeroval,scr2
|
||||
(p6) br.cond.sptk 5b
|
||||
|
||||
tbit.nz p8,p0=scr1,DEADLOCKBIT;;// Test for DEADLOCK
|
||||
|
||||
mov psr.l=psrsave;; // Reenable IC
|
||||
srlz.i;;
|
||||
////////////// END PHYSICAL MODE ////////////////////
|
||||
|
||||
(p8) add r8=1,r8
|
||||
(p8) br.cond.spnt 1b;; // Repeat if DEADLOCK occurred.
|
||||
|
||||
br.ret.sptk rp
|
||||
.endp sn2_ptc_deadlock_recovery_core
|
||||
@@ -1 +0,0 @@
|
||||
subdir-y += pcibr
|
||||
@@ -1 +0,0 @@
|
||||
obj-y += pcibr_reg.o
|
||||
@@ -1,7 +0,0 @@
|
||||
Source files in this directory are identical copies of linux-2.6.19 files:
|
||||
|
||||
NOTE: DO NOT commit changes to these files! If a file
|
||||
needs to be changed, move it to ../linux-xen and follow
|
||||
the instructions in the README there.
|
||||
|
||||
pcibr_reg.c -> linux/arch/ia64/sn/pci/pcibr/pcibr_reg.c
|
||||
@@ -1,285 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/sn/io.h>
|
||||
#include <asm/sn/pcibr_provider.h>
|
||||
#include <asm/sn/pcibus_provider_defs.h>
|
||||
#include <asm/sn/pcidev.h>
|
||||
#include <asm/sn/pic.h>
|
||||
#include <asm/sn/tiocp.h>
|
||||
|
||||
union br_ptr {
|
||||
struct tiocp tio;
|
||||
struct pic pic;
|
||||
};
|
||||
|
||||
/*
|
||||
* Control Register Access -- Read/Write 0000_0020
|
||||
*/
|
||||
void pcireg_control_bit_clr(struct pcibus_info *pcibus_info, u64 bits)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
__sn_clrq_relaxed(&ptr->tio.cp_control, bits);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
__sn_clrq_relaxed(&ptr->pic.p_wid_control, bits);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_control_bit_clr: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcireg_control_bit_set(struct pcibus_info *pcibus_info, u64 bits)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
__sn_setq_relaxed(&ptr->tio.cp_control, bits);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
__sn_setq_relaxed(&ptr->pic.p_wid_control, bits);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_control_bit_set: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI/PCIX Target Flush Register Access -- Read Only 0000_0050
|
||||
*/
|
||||
u64 pcireg_tflush_get(struct pcibus_info *pcibus_info)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
u64 ret = 0;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
ret = __sn_readq_relaxed(&ptr->tio.cp_tflush);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
ret = __sn_readq_relaxed(&ptr->pic.p_wid_tflush);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_tflush_get: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read of the Target Flush should always return zero */
|
||||
if (ret != 0)
|
||||
panic("pcireg_tflush_get:Target Flush failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt Status Register Access -- Read Only 0000_0100
|
||||
*/
|
||||
u64 pcireg_intr_status_get(struct pcibus_info * pcibus_info)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
u64 ret = 0;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
ret = __sn_readq_relaxed(&ptr->tio.cp_int_status);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
ret = __sn_readq_relaxed(&ptr->pic.p_int_status);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_intr_status_get: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt Enable Register Access -- Read/Write 0000_0108
|
||||
*/
|
||||
void pcireg_intr_enable_bit_clr(struct pcibus_info *pcibus_info, u64 bits)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
__sn_clrq_relaxed(&ptr->tio.cp_int_enable, bits);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
__sn_clrq_relaxed(&ptr->pic.p_int_enable, bits);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_intr_enable_bit_clr: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pcireg_intr_enable_bit_set(struct pcibus_info *pcibus_info, u64 bits)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
__sn_setq_relaxed(&ptr->tio.cp_int_enable, bits);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
__sn_setq_relaxed(&ptr->pic.p_int_enable, bits);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_intr_enable_bit_set: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Intr Host Address Register (int_addr) -- Read/Write 0000_0130 - 0000_0168
|
||||
*/
|
||||
void pcireg_intr_addr_addr_set(struct pcibus_info *pcibus_info, int int_n,
|
||||
u64 addr)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
__sn_clrq_relaxed(&ptr->tio.cp_int_addr[int_n],
|
||||
TIOCP_HOST_INTR_ADDR);
|
||||
__sn_setq_relaxed(&ptr->tio.cp_int_addr[int_n],
|
||||
(addr & TIOCP_HOST_INTR_ADDR));
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
__sn_clrq_relaxed(&ptr->pic.p_int_addr[int_n],
|
||||
PIC_HOST_INTR_ADDR);
|
||||
__sn_setq_relaxed(&ptr->pic.p_int_addr[int_n],
|
||||
(addr & PIC_HOST_INTR_ADDR));
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_intr_addr_addr_get: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Force Interrupt Register Access -- Write Only 0000_01C0 - 0000_01F8
|
||||
*/
|
||||
void pcireg_force_intr_set(struct pcibus_info *pcibus_info, int int_n)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
writeq(1, &ptr->tio.cp_force_pin[int_n]);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
writeq(1, &ptr->pic.p_force_pin[int_n]);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_force_intr_set: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Device(x) Write Buffer Flush Reg Access -- Read Only 0000_0240 - 0000_0258
|
||||
*/
|
||||
u64 pcireg_wrb_flush_get(struct pcibus_info *pcibus_info, int device)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
u64 ret = 0;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
ret =
|
||||
__sn_readq_relaxed(&ptr->tio.cp_wr_req_buf[device]);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
ret =
|
||||
__sn_readq_relaxed(&ptr->pic.p_wr_req_buf[device]);
|
||||
break;
|
||||
default:
|
||||
panic("pcireg_wrb_flush_get: unknown bridgetype bridge 0x%p", ptr);
|
||||
}
|
||||
|
||||
}
|
||||
/* Read of the Write Buffer Flush should always return zero */
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pcireg_int_ate_set(struct pcibus_info *pcibus_info, int ate_index,
|
||||
u64 val)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
writeq(val, &ptr->tio.cp_int_ate_ram[ate_index]);
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
writeq(val, &ptr->pic.p_int_ate_ram[ate_index]);
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_int_ate_set: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 __iomem *pcireg_int_ate_addr(struct pcibus_info *pcibus_info, int ate_index)
|
||||
{
|
||||
union br_ptr __iomem *ptr = (union br_ptr __iomem *)pcibus_info->pbi_buscommon.bs_base;
|
||||
u64 __iomem *ret = NULL;
|
||||
|
||||
if (pcibus_info) {
|
||||
switch (pcibus_info->pbi_bridge_type) {
|
||||
case PCIBR_BRIDGETYPE_TIOCP:
|
||||
ret = &ptr->tio.cp_int_ate_ram[ate_index];
|
||||
break;
|
||||
case PCIBR_BRIDGETYPE_PIC:
|
||||
ret = &ptr->pic.p_int_ate_ram[ate_index];
|
||||
break;
|
||||
default:
|
||||
panic
|
||||
("pcireg_int_ate_addr: unknown bridgetype bridge 0x%p",
|
||||
ptr);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Optimized version of the standard strlen() function
|
||||
*
|
||||
*
|
||||
* Inputs:
|
||||
* in0 address of string
|
||||
*
|
||||
* Outputs:
|
||||
* ret0 the number of characters in the string (0 if empty string)
|
||||
* does not count the \0
|
||||
*
|
||||
* Copyright (C) 1999, 2001 Hewlett-Packard Co
|
||||
* Stephane Eranian <eranian@hpl.hp.com>
|
||||
*
|
||||
* 09/24/99 S.Eranian add speculation recovery code
|
||||
*/
|
||||
|
||||
#include <asm/asmmacro.h>
|
||||
|
||||
//
|
||||
//
|
||||
// This is an enhanced version of the basic strlen. it includes a combination
|
||||
// of compute zero index (czx), parallel comparisons, speculative loads and
|
||||
// loop unroll using rotating registers.
|
||||
//
|
||||
// General Ideas about the algorithm:
|
||||
// The goal is to look at the string in chunks of 8 bytes.
|
||||
// so we need to do a few extra checks at the beginning because the
|
||||
// string may not be 8-byte aligned. In this case we load the 8byte
|
||||
// quantity which includes the start of the string and mask the unused
|
||||
// bytes with 0xff to avoid confusing czx.
|
||||
// We use speculative loads and software pipelining to hide memory
|
||||
// latency and do read ahead safely. This way we defer any exception.
|
||||
//
|
||||
// Because we don't want the kernel to be relying on particular
|
||||
// settings of the DCR register, we provide recovery code in case
|
||||
// speculation fails. The recovery code is going to "redo" the work using
|
||||
// only normal loads. If we still get a fault then we generate a
|
||||
// kernel panic. Otherwise we return the strlen as usual.
|
||||
//
|
||||
// The fact that speculation may fail can be caused, for instance, by
|
||||
// the DCR.dm bit being set. In this case TLB misses are deferred, i.e.,
|
||||
// a NaT bit will be set if the translation is not present. The normal
|
||||
// load, on the other hand, will cause the translation to be inserted
|
||||
// if the mapping exists.
|
||||
//
|
||||
// It should be noted that we execute recovery code only when we need
|
||||
// to use the data that has been speculatively loaded: we don't execute
|
||||
// recovery code on pure read ahead data.
|
||||
//
|
||||
// Remarks:
|
||||
// - the cmp r0,r0 is used as a fast way to initialize a predicate
|
||||
// register to 1. This is required to make sure that we get the parallel
|
||||
// compare correct.
|
||||
//
|
||||
// - we don't use the epilogue counter to exit the loop but we need to set
|
||||
// it to zero beforehand.
|
||||
//
|
||||
// - after the loop we must test for Nat values because neither the
|
||||
// czx nor cmp instruction raise a NaT consumption fault. We must be
|
||||
// careful not to look too far for a Nat for which we don't care.
|
||||
// For instance we don't need to look at a NaT in val2 if the zero byte
|
||||
// was in val1.
|
||||
//
|
||||
// - Clearly performance tuning is required.
|
||||
//
|
||||
//
|
||||
//
|
||||
#define saved_pfs r11
|
||||
#define tmp r10
|
||||
#define base r16
|
||||
#define orig r17
|
||||
#define saved_pr r18
|
||||
#define src r19
|
||||
#define mask r20
|
||||
#define val r21
|
||||
#define val1 r22
|
||||
#define val2 r23
|
||||
|
||||
GLOBAL_ENTRY(strlen)
|
||||
.prologue
|
||||
.save ar.pfs, saved_pfs
|
||||
alloc saved_pfs=ar.pfs,11,0,0,8 // rotating must be multiple of 8
|
||||
|
||||
.rotr v[2], w[2] // declares our 4 aliases
|
||||
|
||||
extr.u tmp=in0,0,3 // tmp=least significant 3 bits
|
||||
mov orig=in0 // keep trackof initial byte address
|
||||
dep src=0,in0,0,3 // src=8byte-aligned in0 address
|
||||
.save pr, saved_pr
|
||||
mov saved_pr=pr // preserve predicates (rotation)
|
||||
;;
|
||||
|
||||
.body
|
||||
|
||||
ld8 v[1]=[src],8 // must not speculate: can fail here
|
||||
shl tmp=tmp,3 // multiply by 8bits/byte
|
||||
mov mask=-1 // our mask
|
||||
;;
|
||||
ld8.s w[1]=[src],8 // speculatively load next
|
||||
cmp.eq p6,p0=r0,r0 // sets p6 to true for cmp.and
|
||||
sub tmp=64,tmp // how many bits to shift our mask on the right
|
||||
;;
|
||||
shr.u mask=mask,tmp // zero enough bits to hold v[1] valuable part
|
||||
mov ar.ec=r0 // clear epilogue counter (saved in ar.pfs)
|
||||
;;
|
||||
add base=-16,src // keep track of aligned base
|
||||
or v[1]=v[1],mask // now we have a safe initial byte pattern
|
||||
;;
|
||||
1:
|
||||
ld8.s v[0]=[src],8 // speculatively load next
|
||||
czx1.r val1=v[1] // search 0 byte from right
|
||||
czx1.r val2=w[1] // search 0 byte from right following 8bytes
|
||||
;;
|
||||
ld8.s w[0]=[src],8 // speculatively load next to next
|
||||
cmp.eq.and p6,p0=8,val1 // p6 = p6 and val1==8
|
||||
cmp.eq.and p6,p0=8,val2 // p6 = p6 and mask==8
|
||||
(p6) br.wtop.dptk 1b // loop until p6 == 0
|
||||
;;
|
||||
//
|
||||
// We must return try the recovery code iff
|
||||
// val1_is_nat || (val1==8 && val2_is_nat)
|
||||
//
|
||||
// XXX Fixme
|
||||
// - there must be a better way of doing the test
|
||||
//
|
||||
cmp.eq p8,p9=8,val1 // p6 = val1 had zero (disambiguate)
|
||||
tnat.nz p6,p7=val1 // test NaT on val1
|
||||
(p6) br.cond.spnt .recover // jump to recovery if val1 is NaT
|
||||
;;
|
||||
//
|
||||
// if we come here p7 is true, i.e., initialized for // cmp
|
||||
//
|
||||
cmp.eq.and p7,p0=8,val1// val1==8?
|
||||
tnat.nz.and p7,p0=val2 // test NaT if val2
|
||||
(p7) br.cond.spnt .recover // jump to recovery if val2 is NaT
|
||||
;;
|
||||
(p8) mov val1=val2 // the other test got us out of the loop
|
||||
(p8) adds src=-16,src // correct position when 3 ahead
|
||||
(p9) adds src=-24,src // correct position when 4 ahead
|
||||
;;
|
||||
sub ret0=src,orig // distance from base
|
||||
sub tmp=8,val1 // which byte in word
|
||||
mov pr=saved_pr,0xffffffffffff0000
|
||||
;;
|
||||
sub ret0=ret0,tmp // adjust
|
||||
mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what
|
||||
br.ret.sptk.many rp // end of normal execution
|
||||
|
||||
//
|
||||
// Outlined recovery code when speculation failed
|
||||
//
|
||||
// This time we don't use speculation and rely on the normal exception
|
||||
// mechanism. that's why the loop is not as good as the previous one
|
||||
// because read ahead is not possible
|
||||
//
|
||||
// IMPORTANT:
|
||||
// Please note that in the case of strlen() as opposed to strlen_user()
|
||||
// we don't use the exception mechanism, as this function is not
|
||||
// supposed to fail. If that happens it means we have a bug and the
|
||||
// code will cause of kernel fault.
|
||||
//
|
||||
// XXX Fixme
|
||||
// - today we restart from the beginning of the string instead
|
||||
// of trying to continue where we left off.
|
||||
//
|
||||
.recover:
|
||||
ld8 val=[base],8 // will fail if unrecoverable fault
|
||||
;;
|
||||
or val=val,mask // remask first bytes
|
||||
cmp.eq p0,p6=r0,r0 // nullify first ld8 in loop
|
||||
;;
|
||||
//
|
||||
// ar.ec is still zero here
|
||||
//
|
||||
2:
|
||||
(p6) ld8 val=[base],8 // will fail if unrecoverable fault
|
||||
;;
|
||||
czx1.r val1=val // search 0 byte from right
|
||||
;;
|
||||
cmp.eq p6,p0=8,val1 // val1==8 ?
|
||||
(p6) br.wtop.dptk 2b // loop until p6 == 0
|
||||
;; // (avoid WAW on p63)
|
||||
sub ret0=base,orig // distance from base
|
||||
sub tmp=8,val1
|
||||
mov pr=saved_pr,0xffffffffffff0000
|
||||
;;
|
||||
sub ret0=ret0,tmp // length=now - back -1
|
||||
mov ar.pfs=saved_pfs // because of ar.ec, restore no matter what
|
||||
br.ret.sptk.many rp // end of successful recovery code
|
||||
END(strlen)
|
||||
@@ -1,46 +0,0 @@
|
||||
INSTRUCTIONS FOR Running IPF/Xen with VT-enabled Tiger4 pltform
|
||||
|
||||
1. Install a Linux Disk, VT_Disk, to be used by VT
|
||||
2. Setup the target VT_Disk
|
||||
1. Boot VT_Disk
|
||||
2. modify following files of VT_Disk
|
||||
/boot/efi/efi/redhat/elilo.conf -
|
||||
modify "append=" line to have "root=/dev/hda3"
|
||||
** note /dev/hda3 must reflect VT_Disk /root partition
|
||||
|
||||
/etc/fstab -
|
||||
LABEL=/ / ext3 DEFAULTS 1 1
|
||||
to
|
||||
/dev/hda3 / ext3 DEFAULTS 1 1
|
||||
and other entries accordingly
|
||||
3. Install Xen and boot XenLinux on your standard Linux disk
|
||||
1. modify /boot/efi/efi/redhat/elilo.conf -
|
||||
"append=" entry to have "root=/dev/sda3"
|
||||
2. modify /etc/fstab -
|
||||
LABEL=/ / ext3 DEFAULTS 1 1
|
||||
to
|
||||
/dev/sda3 / ext3 DEFAULTS 1 1
|
||||
and other entries accordingly
|
||||
4. Reboot XenLinux with VT_Disk in /dev/sdb slot
|
||||
1. copy Guest_Firmware.bin into /usr/lib/xen/boot/guest_firmware.bin
|
||||
2. modify /etc/xen/xmexample.vti
|
||||
disk = [ 'phy:/dev/sdb,ioemu:hda,w' ]
|
||||
and make sure
|
||||
kernel=/usr/lib/xen/boot/guest_firmware.bin
|
||||
5. Make sure XenLinux has SDL installed by
|
||||
> rpm -q -a | grep SDL
|
||||
SDL-1.2.7-8 SDL-devel-1.2.7-8
|
||||
6. Start vncserver from XenLinux
|
||||
1. ifconfig to get XenLinux IP address
|
||||
2. vncserver
|
||||
7. Start VT Domain
|
||||
1. From a remote system connect to XenLinux through vnc viewer
|
||||
2. On vnc windows
|
||||
> xend start
|
||||
> xm create /etc/xen/xmexample.vti
|
||||
an EFI shell will popup
|
||||
> fs0:
|
||||
fs0:> cd efi\redhat
|
||||
fs0:> elilo linux
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
# Recipe of Booting up Xen/dom0/domU on IA64 system
|
||||
# 06/02/2006 Written by Xen-ia64-devel community.
|
||||
|
||||
My enviroment is;
|
||||
Machine : Tiger4
|
||||
Domain0 OS : RHEL4 U2
|
||||
DomainU OS : RHEL4 U2
|
||||
|
||||
----------------------------
|
||||
Build xen
|
||||
1. Download source
|
||||
# hg clone http://xenbits.xensource.com/ext/ia64/xen-unstable.hg
|
||||
# cd xen-unstable.hg
|
||||
# hg clone http://xenbits.xensource.com/ext/ia64/linux-2.6.18-xen.hg
|
||||
|
||||
2. # make world
|
||||
|
||||
3. # make install-tools
|
||||
|
||||
4. copy kernels and xen
|
||||
# cp xen/xen.gz /boot/efi/efi/redhat/
|
||||
# cp build-linux-2.6.18-xen_ia64/vmlinux.gz /boot/efi/efi/redhat/vmlinuz-2.6.18.8-xen
|
||||
|
||||
5. make initrd for Dom0/DomU
|
||||
# make -C linux-2.6.18-xen.hg ARCH=ia64 modules_install O=$(/bin/pwd)/build-linux-2.6.18-xen_ia64
|
||||
# mkinitrd -f /boot/efi/efi/redhat/initrd-2.6.18.8-xen.img 2.6.18.8-xen --builtin mptbase --builtin mptscsih
|
||||
|
||||
---------------------------------
|
||||
Make OSimg for DomU
|
||||
1. make file
|
||||
# dd if=/dev/zero of=/root/rhel4.img bs=1M seek=4096 count=0
|
||||
# mke2fs -F -j /root/rhel4.img
|
||||
# mount -o loop /root/rhel4.img /mnt
|
||||
# cp -ax /{dev,var,etc,usr,bin,sbin,lib} /mnt
|
||||
# mkdir /mnt/{root,proc,sys,home,tmp}
|
||||
|
||||
2. modify DomU's fstab
|
||||
# vi /mnt/etc/fstab
|
||||
/dev/xvda1 / ext3 defaults 1 1
|
||||
none /dev/pts devpts gid=5,mode=620 0 0
|
||||
none /dev/shm tmpfs defaults 0 0
|
||||
none /proc proc defaults 0 0
|
||||
none /sys sysfs defaults 0 0
|
||||
|
||||
3. modify inittab
|
||||
set runlevel to 3 to avoid X trying to start
|
||||
# vi /mnt/etc/inittab
|
||||
id:3:initdefault:
|
||||
Start a getty on the xvc0 console
|
||||
X0:2345:respawn:/sbin/mingetty xvc0
|
||||
tty1-6 mingetty can be commented out
|
||||
|
||||
4. add xvc0 into /etc/securetty
|
||||
# vi /mnt/etc/securetty (add xvc0)
|
||||
|
||||
5. umount
|
||||
# umount /mnt
|
||||
|
||||
-------------------------------------
|
||||
Boot Xen & Domain0
|
||||
1. replace elilo
|
||||
download from the below
|
||||
http://elilo.sourceforge.net/cgi-bin/blosxom
|
||||
and copy into /boot/efi/efi/redhat/
|
||||
# cp elilo-3.6-ia64.efi /boot/efi/efi/redhat/elilo.efi
|
||||
|
||||
|
||||
2. modify elilo.conf (like the below)
|
||||
# vi /boot/efi/efi/redhat/elilo.conf
|
||||
prompt
|
||||
timeout=20
|
||||
default=xen
|
||||
relocatable
|
||||
|
||||
image=vmlinuz-2.6.18.8-xen
|
||||
label=xen
|
||||
vmm=xen.gz
|
||||
initrd=initrd-2.6.18.8-xen.img
|
||||
read-only
|
||||
append=" -- rhgb root=/dev/sda2"
|
||||
|
||||
-------------------------------------
|
||||
Boot DomainU
|
||||
1. make config of DomU
|
||||
# vi /etc/xen/rhel4
|
||||
kernel = "/boot/efi/efi/redhat/vmlinuz-2.6.18.8-xen"
|
||||
ramdisk = "/boot/efi/efi/redhat/initrd-2.6.18.8-xen.img"
|
||||
memory = 384
|
||||
name = "rhel4"
|
||||
disk = [ 'file:/root/rhel4.img,xvda1,w' ]
|
||||
root = "/dev/xvda1 ro"
|
||||
|
||||
2. After boot xen and dom0, start xend
|
||||
# /etc/init.d/xend start
|
||||
( In the debugging case, # XEND_DEBUG=1 xend trace_start )
|
||||
|
||||
3. start domU
|
||||
# xm create -c rhel4
|
||||
@@ -1,154 +0,0 @@
|
||||
Xenoprof/IA64 HOWTO
|
||||
|
||||
Written by Isaku Yamahata <yamahata at valinux co jp>
|
||||
15th Jan, 2008
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
This documents describes how to use xenoprof/ia64.
|
||||
See oprofile site for the details of oprofile itself.
|
||||
|
||||
|
||||
Reference
|
||||
---------
|
||||
oprofile
|
||||
http://oprofile.sourceforge.net/news/
|
||||
|
||||
xenoprof
|
||||
http://xenoprof.sourceforge.net/
|
||||
|
||||
|
||||
Requirement
|
||||
-----------
|
||||
- xen VMM
|
||||
xen-ia64-devel.hg c/s 16632:2900e4dacaa7 or later
|
||||
Probably xen 3.2 or later would be OK.
|
||||
|
||||
- dom0/domU Linux
|
||||
linux kernel tree corresponding to xen VMM.
|
||||
|
||||
- Oprofile
|
||||
oprofile 0.9.3 or later
|
||||
get the patch for oprofile 0.9.3 from http://xenoprof.sourceforge.net/
|
||||
oprofile-0.9.3-xen-r2.patch or later
|
||||
NOTE:The xenoprof/IA64 specific part is already included in the development
|
||||
cvs tree.
|
||||
|
||||
- Documentation
|
||||
You can find the documentation from http://xenoprof.sourceforge.net/.
|
||||
It doesn't cover IA64 specific part, but it almost applies.
|
||||
|
||||
|
||||
Options to opcontrol
|
||||
--------------------
|
||||
You have to tell the oprofile daemon that the session is xenoprof one
|
||||
by "--xen" option.
|
||||
--xen=<xen_image_file>
|
||||
Specify the xen image.
|
||||
--active-domains<list>
|
||||
Specify active domains
|
||||
--passive-domains=<list>
|
||||
Specify passive domains
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
- dom0 active, dom1 passive case example
|
||||
on dom0
|
||||
# opctonrol --start-daemon --xen=<path to xen-syms> \
|
||||
--vmlinux=<path to vmlinux> \
|
||||
--active-domains=0 --passive-domains=1
|
||||
# opcontrol --start
|
||||
|
||||
<make activity you want>
|
||||
|
||||
# opcontrol --stop (or opcontrol --shutdown)
|
||||
# opreport -l or something to get the result
|
||||
|
||||
|
||||
- both dom0 and dom1 active example
|
||||
on dom0
|
||||
# opctonrol --start-daemon --xen=<path to xen-syms> \
|
||||
--vmlinux=<path to vmlinux> \
|
||||
--active-domains=0,1
|
||||
|
||||
on dom1
|
||||
# opctonrol --start-daemon --xen=<path to xen-syms> \
|
||||
--vmlinux=<path to vmlinux>
|
||||
domain1 isn't primary domain so that --active-domains/--passive-domains
|
||||
shouldn't be specified.
|
||||
|
||||
on dom0
|
||||
# opcontrol --start
|
||||
on dom1
|
||||
# opcontrol --start
|
||||
|
||||
<make activity you want>
|
||||
|
||||
on dom1
|
||||
# opcontrol --stop (or opcontrol --shutdown)
|
||||
on dom0
|
||||
# opcontrol --stop (or opcontrol --shutdown)
|
||||
|
||||
on dom0
|
||||
# opreport -l or something to get the result of dom0
|
||||
on dom1
|
||||
# opreport -l or something to get the result of dom1
|
||||
|
||||
|
||||
Result example
|
||||
--------------
|
||||
The app name of non-dom0 domain would be domain<N>-{xen, kernel, modules, app}
|
||||
where N is the domain id.
|
||||
You may want to create domain<N>-xen domain<N>-kernel to get the symbol names.
|
||||
|
||||
# opreport
|
||||
CPU: Itanium 2, speed 1595 MHz (estimated)
|
||||
Counted L2DTLB_MISSES events (L2DTLB Misses) with a unit mask of 0x00 (No unit mask) count 5000
|
||||
L2DTLB_MISSES:...|
|
||||
samples| %|
|
||||
------------------
|
||||
242 40.2662 domain1-kernel
|
||||
176 29.2845 domain1-xen
|
||||
128 21.2978 domain1-apps
|
||||
55 9.1514 xen-syms
|
||||
|
||||
# opreport -l
|
||||
CPU: Itanium 2, speed 1595 MHz (estimated)
|
||||
Counted L2DTLB_MISSES events (L2DTLB Misses) with a unit mask of 0x00 (No unit mask) count 5000
|
||||
warning: /boot/domain1-xen could not be found.
|
||||
warning: /domain1-apps could not be found.
|
||||
warning: /domain1-kernel could not be found.
|
||||
samples % app name symbol name
|
||||
242 40.2662 domain1-kernel (no symbols)
|
||||
176 29.2845 domain1-xen (no symbols)
|
||||
128 21.2978 domain1-apps (no symbols)
|
||||
16 2.6622 xen-syms context_switch
|
||||
16 2.6622 xen-syms lookup_domain_mpa
|
||||
7 1.1647 xen-syms vcpu_get_domain_bundle
|
||||
3 0.4992 xen-syms do_dom0vp_op
|
||||
3 0.4992 xen-syms lookup_noalloc_domain_pte
|
||||
3 0.4992 xen-syms xencomm_get_page
|
||||
2 0.3328 xen-syms __copy_user
|
||||
2 0.3328 xen-syms vcpu_translate
|
||||
1 0.1664 xen-syms ia64_frametable_probe
|
||||
1 0.1664 xen-syms vcpu_wake
|
||||
1 0.1664 xen-syms xencomm_ctxt_init
|
||||
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
- Don't create/destroy/save/restore/live migration during xenoprof session.
|
||||
Otherwise something would go wrong. (including xen VMM hang)
|
||||
This isn't ia64 specific.
|
||||
If you want to profile early boot phase, "xm create -p/unpause" is your
|
||||
friend.
|
||||
|
||||
- Currently only the generic PMC/PMD is supported.
|
||||
The CPU implementation specific PMC/PMD isn't supported.
|
||||
|
||||
- calling graph isn't supported yet.
|
||||
|
||||
- The active domain for HVM domain isn't supported
|
||||
Xen/IA64 VMM itself supports it, however the driver doesn't exist.
|
||||
@@ -1,25 +0,0 @@
|
||||
# generate a patch for all the files in linux-xen directories
|
||||
# (these are files that are identical to linux except for a few small changes)
|
||||
# run in the main xen directory
|
||||
LINUXPATH=/home/djm/linux-2.6.13
|
||||
OUTFILE=/tmp/linux-xen-diffs
|
||||
wd=$PWD
|
||||
for i in include/asm-ia64/linux-xen/asm include/asm-ia64/linux-xen/linux arch/ia64/linux-xen
|
||||
do
|
||||
echo '#############' $i '#############'
|
||||
cd $i
|
||||
ln -s $LINUXPATH xxx
|
||||
cat README.origin | grep -v '^#' | grep -v '^[ ]*$' | \
|
||||
sed -e 's/[ ]*-> linux/ xxx/' | \
|
||||
sed -e 's/^/diff -Naur /' | bash
|
||||
rm -f xxx
|
||||
cd $wd
|
||||
done > $OUTFILE
|
||||
echo 'Total lines in patch:' `cat $OUTFILE | grep -v '########' | wc -l`
|
||||
echo 'Approx. changes in patch:' `cat $OUTFILE | grep -v '########' | \
|
||||
grep -- '-#if' | wc -l`
|
||||
echo 'Total lines added by patch:' `cat $OUTFILE | grep -v '########' | \
|
||||
grep -- '^-' | wc -l`
|
||||
echo 'Total non-conditional-compile lines added by patch:' \
|
||||
`cat $OUTFILE | grep -v '########' | grep -- '^-' | \
|
||||
egrep -v '^-#if|^-#else|^-#endif' | wc -l`
|
||||
@@ -1,31 +0,0 @@
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
obj-m += expose_p2m.o
|
||||
else
|
||||
PWD := $(shell pwd)
|
||||
TOPDIR ?= $(abspath $(PWD)/../../../../..)
|
||||
KVER ?= $(shell awk '/^LINUX_VER\>/{print $$3}' $(TOPDIR)/buildconfigs/mk.linux-2.6-xen)
|
||||
KDIR ?= $(TOPDIR)/linux-$(KVER)-xen
|
||||
#CROSS_COMPILE ?= ia64-unknown-linux-
|
||||
#ARCH ?= ia64
|
||||
|
||||
ifneq ($(O),)
|
||||
OPT_O := O=$(realpath $(O))
|
||||
else
|
||||
OPT_O ?= O=$(TOPDIR)/build-linux-$(KVER)-xen_ia64
|
||||
endif
|
||||
|
||||
ifneq ($(V),)
|
||||
OPT_V := V=$(V)
|
||||
endif
|
||||
|
||||
ifneq ($(ARCH),)
|
||||
OPT_ARCH := ARCH=$(ARCH)
|
||||
endif
|
||||
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
OPT_CORSS_COMPILE := CROSS_COMPILE=$(CROSS_COMPILE)
|
||||
endif
|
||||
|
||||
default:
|
||||
$(MAKE) -C $(KDIR) $(OPT_O) $(OPT_V) $(OPT_CORSS_COMPILE) $(OPT_ARCH) M=$(PWD)
|
||||
endif
|
||||
@@ -1,12 +0,0 @@
|
||||
This directory contains Linux kernel module for p2m exposure test/benchmark.
|
||||
|
||||
1. build kernel module
|
||||
- At fist build, linux-xen as usual
|
||||
- then type just 'make' in this directory, then you'll have expose_p2m.ko.
|
||||
See Makefile for details.
|
||||
|
||||
2. test, benchmark.
|
||||
- type 'insmod expose_p2m.ko' on the system.
|
||||
Then the result is printed out to your console.
|
||||
insmod fails with EINVAL so that you don't have to execute rmmod.
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/******************************************************************************
|
||||
* arch/ia64/xen/expose_p2m.c
|
||||
*
|
||||
* Copyright (c) 2006 Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/hypercall.h>
|
||||
#include <asm/hypervisor.h>
|
||||
|
||||
#define printd(fmt, ...) printk("%s:%d " fmt, __func__, __LINE__, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
// copied from arch/ia64/mm/tlb.c. it isn't exported.
|
||||
void
|
||||
local_flush_tlb_all (void)
|
||||
{
|
||||
unsigned long i, j, flags, count0, count1, stride0, stride1, addr;
|
||||
|
||||
addr = local_cpu_data->ptce_base;
|
||||
count0 = local_cpu_data->ptce_count[0];
|
||||
count1 = local_cpu_data->ptce_count[1];
|
||||
stride0 = local_cpu_data->ptce_stride[0];
|
||||
stride1 = local_cpu_data->ptce_stride[1];
|
||||
|
||||
local_irq_save(flags);
|
||||
for (i = 0; i < count0; ++i) {
|
||||
for (j = 0; j < count1; ++j) {
|
||||
ia64_ptce(addr);
|
||||
addr += stride1;
|
||||
}
|
||||
addr += stride0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
ia64_srlz_i(); /* srlz.i implies srlz.d */
|
||||
}
|
||||
|
||||
static void
|
||||
do_p2m(unsigned long (*conv)(unsigned long),
|
||||
const char* msg, const char* prefix,
|
||||
unsigned long start_gpfn, unsigned end_gpfn, unsigned long stride)
|
||||
{
|
||||
struct timeval before_tv;
|
||||
struct timeval after_tv;
|
||||
unsigned long gpfn;
|
||||
unsigned long mfn;
|
||||
unsigned long count;
|
||||
s64 nsec;
|
||||
|
||||
count = 0;
|
||||
do_gettimeofday(&before_tv);
|
||||
for (gpfn = start_gpfn; gpfn < end_gpfn; gpfn += stride) {
|
||||
mfn = (*conv)(gpfn);
|
||||
count++;
|
||||
}
|
||||
do_gettimeofday(&after_tv);
|
||||
nsec = timeval_to_ns(&after_tv) - timeval_to_ns(&before_tv);
|
||||
printk("%s stride %4ld %s: %9ld / %6ld = %5ld nsec\n",
|
||||
msg, stride, prefix,
|
||||
nsec, count, nsec/count);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_with_hypercall(const char* msg,
|
||||
unsigned long start_gpfn, unsigned long end_gpfn,
|
||||
unsigned long stride)
|
||||
{
|
||||
do_p2m(&HYPERVISOR_phystomach, msg, "hypercall",
|
||||
start_gpfn, end_gpfn, stride);
|
||||
}
|
||||
|
||||
static void
|
||||
do_with_table(const char* msg,
|
||||
unsigned long start_gpfn, unsigned long end_gpfn,
|
||||
unsigned long stride)
|
||||
{
|
||||
do_p2m(&p2m_phystomach, msg, "p2m table",
|
||||
start_gpfn, end_gpfn, stride);
|
||||
}
|
||||
|
||||
static int __init
|
||||
expose_p2m_init(void)
|
||||
{
|
||||
unsigned long gpfn;
|
||||
unsigned long mfn;
|
||||
unsigned long p2m_mfn;
|
||||
|
||||
int error_count = 0;
|
||||
|
||||
const int strides[] = {
|
||||
PTRS_PER_PTE, PTRS_PER_PTE/2, PTRS_PER_PTE/3, PTRS_PER_PTE/4,
|
||||
L1_CACHE_BYTES/sizeof(pte_t), 1
|
||||
};
|
||||
int i;
|
||||
|
||||
|
||||
#if 0
|
||||
printd("about to call p2m_expose_init()\n");
|
||||
if (p2m_expose_init() < 0) {
|
||||
printd("p2m_expose_init() failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
printd("p2m_expose_init() success\n");
|
||||
#else
|
||||
if (!p2m_initialized) {
|
||||
printd("p2m exposure isn't initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
printd("p2m expose test begins\n");
|
||||
for (gpfn = p2m_min_low_pfn; gpfn < p2m_max_low_pfn; gpfn++) {
|
||||
mfn = HYPERVISOR_phystomach(gpfn);
|
||||
p2m_mfn = p2m_phystomach(gpfn);
|
||||
if (mfn != p2m_mfn) {
|
||||
printd("gpfn 0x%016lx "
|
||||
"mfn 0x%016lx p2m_mfn 0x%016lx\n",
|
||||
gpfn, mfn, p2m_mfn);
|
||||
printd("mpaddr 0x%016lx "
|
||||
"maddr 0x%016lx p2m_maddr 0x%016lx\n",
|
||||
gpfn << PAGE_SHIFT,
|
||||
mfn << PAGE_SHIFT, p2m_mfn << PAGE_SHIFT);
|
||||
|
||||
error_count++;
|
||||
if (error_count > 16) {
|
||||
printk("too many errors\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
printd("p2m expose test done!\n");
|
||||
|
||||
printk("type "
|
||||
"stride "
|
||||
"type : "
|
||||
" nsec / count = "
|
||||
"nsec per conv\n");
|
||||
for (i = 0; i < sizeof(strides)/sizeof(strides[0]); i++) {
|
||||
int stride = strides[i];
|
||||
local_flush_tlb_all();
|
||||
do_with_hypercall("cold tlb",
|
||||
p2m_min_low_pfn, p2m_max_low_pfn, stride);
|
||||
do_with_hypercall("warm tlb",
|
||||
p2m_min_low_pfn, p2m_max_low_pfn, stride);
|
||||
|
||||
local_flush_tlb_all();
|
||||
do_with_table("cold tlb",
|
||||
p2m_min_low_pfn, p2m_max_low_pfn, stride);
|
||||
do_with_table("warm tlb",
|
||||
p2m_min_low_pfn, p2m_max_low_pfn, stride);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
expose_p2m_cleanup(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(expose_p2m_init);
|
||||
module_exit(expose_p2m_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Isaku Yamahata <yamahata@valinux.co.jp>");
|
||||
@@ -1,9 +0,0 @@
|
||||
privify: privify_elf64.o privify.o
|
||||
gcc -g privify.o privify_elf64.o -o privify
|
||||
|
||||
|
||||
privify_elf64.o: privify_elf64.c
|
||||
gcc -g -D__KERNEL__ -c privify_elf64.c
|
||||
|
||||
privify.o: privify.c
|
||||
gcc -nostdinc -g -D__KERNEL__ -c privify.c
|
||||
@@ -1,8 +0,0 @@
|
||||
In this directory, just "make".
|
||||
|
||||
Run the resulting program on a vmlinux that has been adjusted
|
||||
to run on Xen (see arch/ia64/tools/README.xenia64linux):
|
||||
|
||||
./privify vmlinux xenlinux
|
||||
|
||||
Use the resulting xenlinux file as domain0
|
||||
@@ -1,360 +0,0 @@
|
||||
/*
|
||||
* Binary translate privilege-sensitive ops to privileged
|
||||
*
|
||||
* Copyright (C) 2004 Hewlett-Packard Co.
|
||||
* Dan Magenheimer (dan.magenheimer@hp.com)
|
||||
*
|
||||
*/
|
||||
|
||||
#include "privify.h"
|
||||
|
||||
typedef unsigned long long u64;
|
||||
typedef unsigned long long IA64_INST;
|
||||
|
||||
typedef union U_IA64_BUNDLE {
|
||||
u64 i64[2];
|
||||
struct { u64 template:5,slot0:41,slot1a:18,slot1b:23,slot2:41; };
|
||||
// NOTE: following doesn't work because bitfields can't cross natural
|
||||
// size boundaries
|
||||
//struct { u64 template:5, slot0:41, slot1:41, slot2:41; };
|
||||
} IA64_BUNDLE;
|
||||
|
||||
typedef enum E_IA64_SLOT_TYPE { I, M, F, B, L, ILLEGAL } IA64_SLOT_TYPE;
|
||||
|
||||
typedef union U_INST64_A5 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, imm7b:7, r3:2, imm5c:5, imm9d:9, s:1, major:4; };
|
||||
} INST64_A5;
|
||||
|
||||
typedef union U_INST64_B4 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, btype:3, un3:3, p:1, b2:3, un11:11, x6:6, wh:2, d:1, un1:1, major:4; };
|
||||
} INST64_B4;
|
||||
|
||||
typedef union U_INST64_B8 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, un21:21, x6:6, un4:4, major:4; };
|
||||
} INST64_B8;
|
||||
|
||||
typedef union U_INST64_B9 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, imm20:20, :1, x6:6, :3, i:1, major:4; };
|
||||
} INST64_B9;
|
||||
|
||||
typedef union U_INST64_I19 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, imm20:20, :1, x6:6, x3:3, i:1, major:4; };
|
||||
} INST64_I19;
|
||||
|
||||
typedef union U_INST64_I26 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, ar3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_I26;
|
||||
|
||||
typedef union U_INST64_I27 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, imm:7, ar3:7, x6:6, x3:3, s:1, major:4;};
|
||||
} INST64_I27;
|
||||
|
||||
typedef union U_INST64_I28 { // not privileged (mov from AR)
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, :7, ar3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_I28;
|
||||
|
||||
typedef union U_INST64_M28 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :14, r3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_M28;
|
||||
|
||||
typedef union U_INST64_M29 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, ar3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_M29;
|
||||
|
||||
typedef union U_INST64_M30 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, imm:7, ar3:7,x4:4,x2:2,x3:3,s:1,major:4;};
|
||||
} INST64_M30;
|
||||
|
||||
typedef union U_INST64_M31 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, :7, ar3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_M31;
|
||||
|
||||
typedef union U_INST64_M32 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, cr3:7, x6:6, x3:3, :1, major:4;};
|
||||
} INST64_M32;
|
||||
|
||||
typedef union U_INST64_M33 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, :7, cr3:7, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M33;
|
||||
|
||||
typedef union U_INST64_M35 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, :7, x6:6, x3:3, :1, major:4; };
|
||||
|
||||
} INST64_M35;
|
||||
|
||||
typedef union U_INST64_M36 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, :14, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M36;
|
||||
|
||||
typedef union U_INST64_M41 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, :7, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M41;
|
||||
|
||||
typedef union U_INST64_M42 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, r3:7, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M42;
|
||||
|
||||
typedef union U_INST64_M43 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, :7, r3:7, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M43;
|
||||
|
||||
typedef union U_INST64_M44 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, imm:21, x4:4, i2:2, x3:3, i:1, major:4; };
|
||||
} INST64_M44;
|
||||
|
||||
typedef union U_INST64_M45 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, :7, r2:7, r3:7, x6:6, x3:3, :1, major:4; };
|
||||
} INST64_M45;
|
||||
|
||||
typedef union U_INST64_M46 {
|
||||
IA64_INST inst;
|
||||
struct { u64 qp:6, r1:7, un7:7, r3:7, x6:6, x3:3, un1:1, major:4; };
|
||||
} INST64_M46;
|
||||
|
||||
typedef union U_INST64 {
|
||||
IA64_INST inst;
|
||||
struct { u64 :37, major:4; } generic;
|
||||
INST64_A5 A5; // used in build_hypercall_bundle only
|
||||
INST64_B4 B4; // used in build_hypercall_bundle only
|
||||
INST64_B8 B8; // rfi, bsw.[01]
|
||||
INST64_B9 B9; // break.b
|
||||
INST64_I19 I19; // used in build_hypercall_bundle only
|
||||
INST64_I26 I26; // mov register to ar (I unit)
|
||||
INST64_I27 I27; // mov immediate to ar (I unit)
|
||||
INST64_I28 I28; // mov from ar (I unit)
|
||||
INST64_M28 M28; // purge translation cache entry
|
||||
INST64_M29 M29; // mov register to ar (M unit)
|
||||
INST64_M30 M30; // mov immediate to ar (M unit)
|
||||
INST64_M31 M31; // mov from ar (M unit)
|
||||
INST64_M32 M32; // mov reg to cr
|
||||
INST64_M33 M33; // mov from cr
|
||||
INST64_M35 M35; // mov to psr
|
||||
INST64_M36 M36; // mov from psr
|
||||
INST64_M41 M41; // translation cache insert
|
||||
INST64_M42 M42; // mov to indirect reg/translation reg insert
|
||||
INST64_M43 M43; // mov from indirect reg
|
||||
INST64_M44 M44; // set/reset system mask
|
||||
INST64_M45 M45; // translation purge
|
||||
INST64_M46 M46; // translation access (tpa,tak)
|
||||
} INST64;
|
||||
|
||||
#define MASK_41 ((u64)0x1ffffffffff)
|
||||
|
||||
long priv_verbose = 0;
|
||||
#define verbose(a...) do { if (priv_verbose) printf(a); } while(0)
|
||||
|
||||
/*
|
||||
* privify_inst
|
||||
*
|
||||
* Replaces privilege-sensitive instructions (and reads from write-trapping
|
||||
* registers) with privileged/trapping instructions as follows:
|
||||
* mov rx=ar.cflg -> mov ar.cflg=r(x+64) [**]
|
||||
* mov rx=ar.ky -> mov ar.ky=r(x+64)
|
||||
* fc rx -> ptc r(x+64)
|
||||
* thash rx=ry -> tak rx=r(y+64)
|
||||
* ttag rx=ry -> tpa rx=r(y+64)
|
||||
* mov rx=cpuid[ry] -> mov r(x+64)=rr[ry]
|
||||
* mov rx=pmd[ry] -> mov r(x+64)=pmc[ry] [**]
|
||||
* cover -> break.b 0x1fffff
|
||||
*
|
||||
* [**] not currently implemented
|
||||
*/
|
||||
IA64_INST privify_inst(IA64_INST inst_val,
|
||||
IA64_SLOT_TYPE slot_type, IA64_BUNDLE *bp, char **msg)
|
||||
{
|
||||
INST64 inst = *(INST64 *)&inst_val;
|
||||
|
||||
*msg = 0;
|
||||
switch (slot_type) {
|
||||
case M:
|
||||
// FIXME: Also use for mov_to/from_ar.cflag (M29/M30) (IA32 only)
|
||||
if (inst.generic.major != 1) break;
|
||||
if (inst.M46.x3 != 0) break;
|
||||
if (inst.M31.x6 == 0x22 && inst.M31.ar3 < 8) {
|
||||
// mov r1=kr -> mov kr=r1+64
|
||||
verbose("privify_inst: privified mov r1=kr @%p\n",bp);
|
||||
if (inst.M31.r1 >= 64) *msg = "mov r1=kr w/r1>63";
|
||||
else privify_mov_from_kr_m(inst);
|
||||
break;
|
||||
}
|
||||
if (inst.M29.x6 == 0x2a && inst.M29.ar3 < 8) {// mov kr=r1
|
||||
if (inst.M29.r2 >= 64) *msg = "mov kr=r2 w/r2>63";
|
||||
break;
|
||||
}
|
||||
if (inst.M28.x6 == 0x30) {
|
||||
// fc r3-> ptc r3+64
|
||||
verbose("privify_inst: privified fc r3 @%p\n",bp);
|
||||
if (inst.M28.r3 >= 64) *msg = "fc r3 w/r3>63";
|
||||
else privify_fc(inst);
|
||||
break;
|
||||
}
|
||||
if (inst.M28.x6 == 0x34) {
|
||||
if (inst.M28.r3 >= 64) *msg = "ptc.e w/r3>63";
|
||||
break;
|
||||
}
|
||||
if (inst.M46.un7 != 0) break;
|
||||
if (inst.M46.un1 != 0) break;
|
||||
if (inst.M46.x6 == 0x1a) { // thash -> tak r1=r3+64
|
||||
verbose("privify_inst: privified thash @%p\n",bp);
|
||||
if (inst.M46.r3 >= 64) *msg = "thash w/r3>63";
|
||||
else privify_thash(inst);
|
||||
}
|
||||
else if (inst.M46.x6 == 0x1b) { // ttag -> tpa r1=r3+64
|
||||
verbose("privify_inst: privified ttag @%p\n",bp);
|
||||
if (inst.M46.r3 >= 64) *msg = "ttag w/r3>63";
|
||||
else privify_ttag(inst);
|
||||
}
|
||||
else if (inst.M43.x6 == 0x17) {
|
||||
verbose("privify_inst: privified mov_from_cpuid @%p\n",bp);
|
||||
if (inst.M43.r1 >= 64) *msg = "mov_from_cpuid w/r1>63";
|
||||
else privify_mov_from_cpuid(inst);
|
||||
}
|
||||
else if (inst.M46.x6 == 0x1e) { // tpa
|
||||
if (inst.M46.r3 >= 64) *msg = "tpa w/r3>63";
|
||||
}
|
||||
else if (inst.M46.x6 == 0x1f) { // tak
|
||||
if (inst.M46.r3 >= 64) *msg = "tak w/r3>63";
|
||||
}
|
||||
else if (inst.M43.x6 == 0x10) {
|
||||
if (inst.M43.r1 >= 64) *msg = "mov_to_rr w/r1>63";
|
||||
}
|
||||
break;
|
||||
case B:
|
||||
if (inst.generic.major != 0) break;
|
||||
if (inst.B8.x6 == 0x2) { // cover -> break.b 0x1fffff
|
||||
if (inst.B8.un21 != 0) break;
|
||||
if (inst.B8.un4 != 0) break;
|
||||
privify_cover(inst);
|
||||
verbose("privify_inst: privified cover @%p\n",bp);
|
||||
}
|
||||
if (inst.B9.x6 == 0x0) { // (p15) break.b 0x1fffff -> cover
|
||||
if (inst.B9.qp != 15) break;
|
||||
if (inst.B9.imm20 != 0xfffff) break;
|
||||
if (inst.B9.i != 1) break;
|
||||
inst.B8.x6 = 0x2;
|
||||
inst.B8.un21 = 0;
|
||||
inst.B8.un4 = 0;
|
||||
inst.B8.qp = 0;
|
||||
verbose("privify_inst: unprivified pseudo-cover @%p\n",
|
||||
bp);
|
||||
}
|
||||
break;
|
||||
case I: // only used for privifying mov_from_ar
|
||||
// FIXME: Also use for mov_to/from_ar.cflag (I26/I27) (IA32 only)
|
||||
if (inst.generic.major != 0) break;
|
||||
if (inst.I28.x6 == 0x32 && !inst.I28.x3 && inst.I28.ar3 < 8) {
|
||||
// mov r1=kr -> mov kr=r1+64
|
||||
verbose("privify_inst: privified mov r1=kr @%p\n",bp);
|
||||
if (inst.I28.r1 >= 64) *msg = "mov r1=kr w/r1>63";
|
||||
else privify_mov_from_kr_i(inst);
|
||||
}
|
||||
else if (inst.I26.x6 == 0x2a && !inst.I26.x3 &&
|
||||
inst.I26.ar3 < 8) {// mov kr=r1
|
||||
if (inst.I26.r2 >= 64) *msg = "mov kr=r2 w/r2>63";
|
||||
}
|
||||
break;
|
||||
case F: case L: case ILLEGAL:
|
||||
break;
|
||||
}
|
||||
return *(IA64_INST *)&inst;
|
||||
}
|
||||
|
||||
#define read_slot1(b) (((b.i64[0]>>46L) | (b.i64[1]<<18UL)) & MASK_41)
|
||||
// Not sure why, but this more obvious definition of read_slot1 doesn't work
|
||||
// because the compiler treats (b.slot1b<<18UL) as a signed 32-bit integer
|
||||
// so not enough bits get used and it gets sign extended to boot!
|
||||
//#define read_slot1(b) ((b.slot1a | (b.slot1b<<18UL)) & MASK_41)
|
||||
#define write_slot1(b,inst) do { b.slot1a=inst;b.slot1b=inst>>18UL;} while (0)
|
||||
|
||||
|
||||
void privify_memory(void *start, unsigned long len)
|
||||
{
|
||||
IA64_BUNDLE bundle, *bp = (IA64_BUNDLE *)start;
|
||||
IA64_INST tmp;
|
||||
char *msg;
|
||||
|
||||
printf("privifying %ld bytes of memory at %p\n",len,start);
|
||||
if ((unsigned long)start & 0xfL) {
|
||||
printf("unaligned memory block in privify_memory\n");
|
||||
}
|
||||
len &= ~0xf;
|
||||
for (bundle = *bp; len; len -= 16) {
|
||||
switch(bundle.template) {
|
||||
case 0x06: case 0x07: case 0x14: case 0x15:
|
||||
case 0x1a: case 0x1b: case 0x1e: case 0x1f:
|
||||
break;
|
||||
case 0x16: case 0x17:
|
||||
// may be B in slot0/1 but cover can only be slot2
|
||||
bundle.slot2 = privify_inst(bundle.slot2,B,bp,&msg);
|
||||
break;
|
||||
case 0x00: case 0x01: case 0x02: case 0x03:
|
||||
tmp = privify_inst(read_slot1(bundle),I,bp,&msg);
|
||||
write_slot1(bundle,tmp);
|
||||
case 0x0c: case 0x0d:
|
||||
bundle.slot2 = privify_inst(bundle.slot2,I,bp,&msg);
|
||||
case 0x04: case 0x05:
|
||||
// could a privified cover be in slot2 here?
|
||||
bundle.slot0 = privify_inst(bundle.slot0,M,bp,&msg);
|
||||
break;
|
||||
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
||||
bundle.slot2 = privify_inst(bundle.slot2,I,bp,&msg);
|
||||
case 0x0e: case 0x0f:
|
||||
bundle.slot0 = privify_inst(bundle.slot0,M,bp,&msg);
|
||||
if (msg) break;
|
||||
tmp = privify_inst(read_slot1(bundle),M,bp,&msg);
|
||||
write_slot1(bundle,tmp);
|
||||
break;
|
||||
case 0x10: case 0x11:
|
||||
tmp = privify_inst(read_slot1(bundle),I,bp,&msg);
|
||||
write_slot1(bundle,tmp);
|
||||
case 0x12: case 0x13:
|
||||
// may be B in slot1 but cover can only be slot2
|
||||
case 0x1c: case 0x1d:
|
||||
bundle.slot0 = privify_inst(bundle.slot0,M,bp,&msg);
|
||||
if (msg) break;
|
||||
bundle.slot2 = privify_inst(bundle.slot2,B,bp,&msg);
|
||||
break;
|
||||
case 0x18: case 0x19:
|
||||
bundle.slot0 = privify_inst(bundle.slot0,M,bp,&msg);
|
||||
if (msg) break;
|
||||
tmp = privify_inst(read_slot1(bundle),M,bp,&msg);
|
||||
write_slot1(bundle,tmp);
|
||||
if (msg) break;
|
||||
bundle.slot2 = privify_inst(bundle.slot2,B,bp,&msg);
|
||||
break;
|
||||
}
|
||||
if (msg) {
|
||||
if (bundle.slot2)
|
||||
printf("privify_memory: %s @%p\n",msg,bp);
|
||||
else
|
||||
printf("privify_memory: %s @%p probably not insts\n",
|
||||
msg,bp);
|
||||
printf("privify_memory: bundle=%p,%p\n",
|
||||
bundle.i64[1],bundle.i64[0]);
|
||||
}
|
||||
*bp = bundle;
|
||||
bundle = *++bp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Binary translate privilege-sensitive ops to privileged
|
||||
*
|
||||
* Copyright (C) 2004 Hewlett-Packard Co.
|
||||
* Dan Magenheimer (dan.magenheimer@hp.com)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Macros to replace privilege-sensitive instructions (and reads from
|
||||
* write-trapping registers) with privileged/trapping instructions as follows:
|
||||
* mov rx=ar.cflg -> mov ar.cflg=r(x+64) [**]
|
||||
* mov rx=ar.ky -> mov ar.ky=r(x+64)
|
||||
* fc rx -> ptc r(x+64)
|
||||
* thash rx=ry -> tak rx=r(y+64)
|
||||
* ttag rx=ry -> tpa rx=r(y+64)
|
||||
* mov rx=cpuid[ry] -> mov r(x+64)=rr[ry]
|
||||
* mov rx=pmd[ry] -> mov r(x+64)=pmc[ry] [**]
|
||||
* cover -> break.b 0x1fffff
|
||||
* [**] not implemented yet
|
||||
*/
|
||||
|
||||
#define notimpl(s) printk(s##" not implemented");
|
||||
#define privify_mov_from_cflg_m(i) do { notimpl("mov from ar.cflg"); } while(0)
|
||||
#define privify_mov_from_cflg_i(i) do { notimpl("mov from ar.cflg"); } while(0)
|
||||
#define privify_mov_from_kr_m(i) do { i.M31.x6 = 0x2a; i.M29.r2 = i.M31.r1 + 64; } while(0)
|
||||
#define privify_mov_from_kr_i(i) do { i.I28.x6 = 0x2a; i.I26.r2 = i.I28.r1 + 64; } while(0)
|
||||
#define privify_fc(i) do { i.M28.x6 = 0x34; i.M28.r3 = i.M28.r3 + 64; } while(0)
|
||||
#define privify_thash(i) do { i.M46.x6 = 0x1f; i.M46.r3 += 64; } while(0)
|
||||
#define privify_ttag(i) do { i.M46.x6 = 0x1f; i.M46.r3 += 64; } while(0)
|
||||
#define privify_mov_from_cpuid(i) do { i.M43.x6 = 0x10; i.M43.r1 += 64; } while(0)
|
||||
#define privify_mov_from_pmd(i) do { notimpl("mov from pmd"); } while(0)
|
||||
#define privify_cover(x) do { x.B8.x6 = 0x0; x.B9.imm20 = 0xfffff; x.B9.i = 0x1; } while(0)
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Binary translate privilege-sensitive ops to privileged
|
||||
*
|
||||
* Copyright (C) 2004 Hewlett-Packard Co.
|
||||
* Dan Magenheimer (dan.magenheimer@hp.com)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#define ELFSIZE 64
|
||||
#include <linux/elf.h>
|
||||
|
||||
#define MAX_FILSIZ (32*1024*1024)
|
||||
unsigned long buf[MAX_FILSIZ/sizeof(unsigned long)];
|
||||
|
||||
static void
|
||||
usage (FILE *fp)
|
||||
{
|
||||
fprintf(fp, "Usage: privify elf64filein elf64fileout\n");
|
||||
}
|
||||
|
||||
static void
|
||||
panic (char *s)
|
||||
{
|
||||
fprintf(stderr, "panic: %s\n",s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
read_file(const char *in_path, char *buf, int maxsize)
|
||||
{
|
||||
ssize_t nread, totread = 0, ssize_inc = 8192;
|
||||
int from;
|
||||
|
||||
if ((from = open (in_path, O_RDONLY)) < 0) return -1;
|
||||
maxsize -= ssize_inc; // create safety zone
|
||||
if (maxsize < 0) panic("input file exceeds max size");
|
||||
while ((nread = read(from, buf, ssize_inc)) > 0) {
|
||||
if (nread < 0) return -1; // problem
|
||||
totread += nread;
|
||||
if (nread < ssize_inc) return totread; // done
|
||||
buf += ssize_inc;
|
||||
if (totread > maxsize) // buffer too small
|
||||
panic("file exceeds max size\n");
|
||||
}
|
||||
return totread;
|
||||
}
|
||||
|
||||
static int
|
||||
write_file(const char *out_path, char *buf, int size)
|
||||
{
|
||||
int to;
|
||||
|
||||
if ((to = open(out_path, O_WRONLY|O_CREAT|O_EXCL,0644)) < 0)
|
||||
return -1;
|
||||
|
||||
if (write(to,buf,size) < 0) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
|
||||
(ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
|
||||
(ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
|
||||
(ehdr).e_ident[EI_MAG3] == ELFMAG3)
|
||||
|
||||
|
||||
static void
|
||||
privify_elf(char *elfbase)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfbase;
|
||||
Elf64_Phdr *phdr;
|
||||
Elf64_Shdr *shdr;
|
||||
char *elfaddr;
|
||||
unsigned long size;
|
||||
int h;
|
||||
|
||||
if ( !IS_ELF(*ehdr) )
|
||||
panic("Kernel image does not have an ELF header.\n");
|
||||
for ( h = 0; h < ehdr->e_phnum; h++ ) {
|
||||
phdr = (Elf64_Phdr *)(elfbase +
|
||||
ehdr->e_phoff + (h*ehdr->e_phentsize));
|
||||
printf("h=%d, phdr=%p,phdr->p_type=%lx",h,phdr,phdr->p_type);
|
||||
if ((phdr->p_type != PT_LOAD)) {
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
size = phdr->p_filesz;
|
||||
elfaddr = elfbase + phdr->p_offset;
|
||||
printf(",elfaddr=%p,size=%d,phdr->p_flags=%lx\n",
|
||||
elfaddr,size,phdr->p_flags);
|
||||
if (phdr->p_flags & PF_X) privify_memory(elfaddr,size);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *in_path, *out_path;
|
||||
int fsize;
|
||||
|
||||
if (argc != 3) {
|
||||
usage(stdout);
|
||||
exit(1);
|
||||
}
|
||||
in_path = argv[1];
|
||||
out_path = argv[2];
|
||||
if ((fsize = read_file(in_path,(char *)buf,MAX_FILSIZ)) < 0) {
|
||||
perror("read_file");
|
||||
panic("failed");
|
||||
}
|
||||
privify_elf((char *)buf);
|
||||
fflush(stdout);
|
||||
if (write_file(out_path,(char *)buf,fsize) < 0) {
|
||||
perror("write_file");
|
||||
panic("failed");
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
CC=gcc
|
||||
CFLAGS=-O -Wall
|
||||
|
||||
.PHONY: all
|
||||
all: postat
|
||||
|
||||
postat: postat.c pohcalls.o
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -f *.o postat *.s *~
|
||||
|
||||
|
||||
Loaded 100 of 488 files, more files were not shown because too many files have changed in this diff.
Show more