Commit 6e439927 authored by David Mosberger's avatar David Mosberger

ia64: Build the gate page(s) as an ELF DSO.

      Remove CONFIG_FSYS option (it's the default now).
      Consolidate the various instruction patching routines into
      a single file (patch.c).
parent 16ec7a76
...@@ -778,9 +778,6 @@ source "arch/ia64/hp/sim/Kconfig" ...@@ -778,9 +778,6 @@ source "arch/ia64/hp/sim/Kconfig"
menu "Kernel hacking" menu "Kernel hacking"
config FSYS
bool "Light-weight system-call support (via epc)"
choice choice
prompt "Physical memory granularity" prompt "Physical memory granularity"
default IA64_GRANULE_64MB default IA64_GRANULE_64MB
......
...@@ -4,12 +4,11 @@ ...@@ -4,12 +4,11 @@
extra-y := head.o init_task.o extra-y := head.o init_task.o
obj-y := acpi.o entry.o efi.o efi_stub.o gate.o ia64_ksyms.o irq.o irq_ia64.o irq_lsapic.o \ obj-y := acpi.o entry.o efi.o efi_stub.o gate-data.o fsys.o ia64_ksyms.o irq.o irq_ia64.o \
ivt.o machvec.o pal.o process.o perfmon.o ptrace.o sal.o semaphore.o setup.o signal.o \ irq_lsapic.o ivt.o machvec.o pal.o patch.o process.o perfmon.o ptrace.o sal.o \
sys_ia64.o time.o traps.o unaligned.o unwind.o semaphore.o setup.o signal.o sys_ia64.o time.o traps.o unaligned.o unwind.o
obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_FSYS) += fsys.o
obj-$(CONFIG_IA64_BRL_EMU) += brl_emu.o obj-$(CONFIG_IA64_BRL_EMU) += brl_emu.o
obj-$(CONFIG_IA64_GENERIC) += acpi-ext.o obj-$(CONFIG_IA64_GENERIC) += acpi-ext.o
obj-$(CONFIG_IA64_HP_ZX1) += acpi-ext.o obj-$(CONFIG_IA64_HP_ZX1) += acpi-ext.o
...@@ -20,3 +19,18 @@ obj-$(CONFIG_MODULES) += module.o ...@@ -20,3 +19,18 @@ obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_SMP) += smp.o smpboot.o obj-$(CONFIG_SMP) += smp.o smpboot.o
obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o
AFLAGS_gate.lds.o += -P -C -U$(ARCH)
arch/ia64/kernel/gate.lds.s: %.s: %.S scripts FORCE
$(call if_changed_dep,as_s_S)
$(obj)/gate.so: $(src)/gate.lds.s $(obj)/gate.o
$(CC) -nostdlib -shared -Wl,-soname=linux-gate.so.1 \
-o $@ -Wl,-T,$^
extra-y += gate.so
$(obj)/built-in.o: $(obj)/gate-syms.o
$(obj)/built-in.o: ld_flags += -R $(obj)/gate-syms.o
$(obj)/gate-syms.o: $(src)/gate.lds.s $(obj)/gate.o
$(CC) -nostdlib -r -o $@ -Wl,-T,$^
extra-y += gate-syms.o
$(obj)/gate-data.o: $(obj)/gate.so
...@@ -392,7 +392,7 @@ GLOBAL_ENTRY(fsys_bubble_down) ...@@ -392,7 +392,7 @@ GLOBAL_ENTRY(fsys_bubble_down)
movl r9=PSR_PRESERVED_BITS movl r9=PSR_PRESERVED_BITS
mov ar.rsc=0 // set enforced lazy mode, pl 0, little-endian, loadrs=0 mov ar.rsc=0 // set enforced lazy mode, pl 0, little-endian, loadrs=0
movl r28=GATE_ADDR // cr.iip XXX fix me!! Should be: GATE_ADDR(syscall_via_break) movl r28=__kernel_syscall_via_break
;; ;;
mov r23=ar.bspstore // save ar.bspstore (12 cyc) mov r23=ar.bspstore // save ar.bspstore (12 cyc)
mov r31=pr // save pr (2 cyc) mov r31=pr // save pr (2 cyc)
......
.section .data.gate, "ax"
.incbin "arch/ia64/kernel/gate.so"
...@@ -6,20 +6,34 @@ ...@@ -6,20 +6,34 @@
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
*/ */
#include <linux/config.h>
#include <asm/asmmacro.h> #include <asm/asmmacro.h>
#include <asm/errno.h>
#include <asm/offsets.h> #include <asm/offsets.h>
#include <asm/sigcontext.h> #include <asm/sigcontext.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/unistd.h> #include <asm/unistd.h>
.section .text.gate, "ax" /*
.start_gate: * We can't easily refer to symbols inside the kernel. To avoid full runtime relocation,
* complications with the linker (which likes to create PLT stubs for branches
* to targets outside the shared object) and to avoid multi-phase kernel builds, we
* simply create minimalistic "patch lists" in special ELF sections.
*/
.section ".data.patch.fsyscall_table", "a"
.previous
#define LOAD_FSYSCALL_TABLE(reg) \
[1:] movl reg=0; \
.xdata4 ".data.patch.fsyscall_table", 1b-.
#if CONFIG_FSYS .section ".data.patch.brl_fsys_bubble_down", "a"
.previous
#define BRL_COND_FSYS_BUBBLE_DOWN(pr) \
[1:](pr)brl.cond.sptk 0; \
.xdata4 ".data.patch.brl_fsys_bubble_down", 1b-.
#include <asm/errno.h> GLOBAL_ENTRY(__kernel_syscall_via_break)
GLOBAL_ENTRY(syscall_via_break)
.prologue .prologue
.altrp b6 .altrp b6
.body .body
...@@ -32,7 +46,7 @@ GLOBAL_ENTRY(syscall_via_break) ...@@ -32,7 +46,7 @@ GLOBAL_ENTRY(syscall_via_break)
nop.i 0 nop.i 0
br.ret.sptk.many b6 br.ret.sptk.many b6
} }
END(syscall_via_break) END(__kernel_syscall_via_break)
/* /*
* On entry: * On entry:
...@@ -48,7 +62,7 @@ END(syscall_via_break) ...@@ -48,7 +62,7 @@ END(syscall_via_break)
* all "preserved" registers: same as on entry * all "preserved" registers: same as on entry
*/ */
GLOBAL_ENTRY(syscall_via_epc) GLOBAL_ENTRY(__kernel_syscall_via_epc)
.prologue .prologue
.altrp b6 .altrp b6
.body .body
...@@ -63,15 +77,15 @@ GLOBAL_ENTRY(syscall_via_epc) ...@@ -63,15 +77,15 @@ GLOBAL_ENTRY(syscall_via_epc)
epc epc
} }
;; ;;
rsm psr.be rsm psr.be // note: on McKinley "rsm psr.be/srlz.d" is slightly faster than "rum psr.be"
movl r14=fsyscall_table LOAD_FSYSCALL_TABLE(r14)
mov r16=IA64_KR(CURRENT) // 12 cycle read latency mov r16=IA64_KR(CURRENT) // 12 cycle read latency
mov r19=NR_syscalls-1 mov r19=NR_syscalls-1
;; ;;
shladd r18=r17,3,r14 shladd r18=r17,3,r14
srlz.d // ensure little-endian byteorder is in effect srlz.d
cmp.ne p8,p0=r0,r0 // p8 <- FALSE cmp.ne p8,p0=r0,r0 // p8 <- FALSE
/* Note: if r17 is a NaT, p6 will be set to zero. */ /* Note: if r17 is a NaT, p6 will be set to zero. */
cmp.geu p6,p7=r19,r17 // (syscall > 0 && syscall < 1024+NR_syscalls)? cmp.geu p6,p7=r19,r17 // (syscall > 0 && syscall < 1024+NR_syscalls)?
...@@ -87,23 +101,26 @@ GLOBAL_ENTRY(syscall_via_epc) ...@@ -87,23 +101,26 @@ GLOBAL_ENTRY(syscall_via_epc)
mov r27=ar.rsc mov r27=ar.rsc
mov r21=ar.fpsr mov r21=ar.fpsr
mov r26=ar.pfs mov r26=ar.pfs
#if 1/*def CONFIG_ITANIUM*/ /*
* brl.cond doesn't work as intended because the linker would convert this branch
* into a branch to a PLT. Perhaps there will be a way to avoid this with some
* future version of the linker. In the meantime, we just use an indirect branch
* instead.
*/
#ifdef CONFIG_ITANIUM
(p6) ld8 r14=[r14] // r14 <- fsys_bubble_down (p6) ld8 r14=[r14] // r14 <- fsys_bubble_down
;; ;;
(p6) mov b7=r14 (p6) mov b7=r14
(p6) br.sptk.many b7 (p6) br.sptk.many b7
#else #else
/* We can't do this until gate is a proper ELF DSO. */ BRL_COND_FSYS_BUBBLE_DOWN(p6)
(p6) brl.cond.sptk fsys_bubble_down
#endif #endif
mov r10=-1 mov r10=-1
mov r8=ENOSYS mov r8=ENOSYS
MCKINLEY_E9_WORKAROUND MCKINLEY_E9_WORKAROUND
br.ret.sptk.many b6 br.ret.sptk.many b6
END(syscall_via_epc) END(__kernel_syscall_via_epc)
#endif /* CONFIG_FSYS */
# define ARG0_OFF (16 + IA64_SIGFRAME_ARG0_OFFSET) # define ARG0_OFF (16 + IA64_SIGFRAME_ARG0_OFFSET)
# define ARG1_OFF (16 + IA64_SIGFRAME_ARG1_OFFSET) # define ARG1_OFF (16 + IA64_SIGFRAME_ARG1_OFFSET)
...@@ -162,7 +179,7 @@ END(syscall_via_epc) ...@@ -162,7 +179,7 @@ END(syscall_via_epc)
.savesp ar.pfs, CFM_OFF+SIGCONTEXT_OFF; \ .savesp ar.pfs, CFM_OFF+SIGCONTEXT_OFF; \
.vframesp SP_OFF+SIGCONTEXT_OFF .vframesp SP_OFF+SIGCONTEXT_OFF
GLOBAL_ENTRY(ia64_sigtramp) GLOBAL_ENTRY(__kernel_sigtramp)
// describe the state that is active when we get here: // describe the state that is active when we get here:
.prologue .prologue
SIGTRAMP_SAVES SIGTRAMP_SAVES
...@@ -344,4 +361,4 @@ restore_rbs: ...@@ -344,4 +361,4 @@ restore_rbs:
mov ar.rsc=0xf // (will be restored later on from sc_ar_rsc) mov ar.rsc=0xf // (will be restored later on from sc_ar_rsc)
// invala not necessary as that will happen when returning to user-mode // invala not necessary as that will happen when returning to user-mode
br.cond.sptk back_from_restore_rbs br.cond.sptk back_from_restore_rbs
END(ia64_sigtramp) END(__kernel_sigtramp)
/*
* Linker script for gate DSO. The gate pages are an ELF shared
* object prelinked to its virtual address, with only one read-only
* segment and one execute-only segment (both fit in one page).
* This script controls its layout.
*/
#include <linux/config.h>
#include <asm/system.h>
SECTIONS
{
. = GATE_ADDR + SIZEOF_HEADERS;
.hash : { *(.hash) } :rodata
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.dyn : { *(.rela*) }
.dynamic : { *(.dynamic) } :rodata :dynamic
.data.patch.mckinley_e9 : { *(.data.patch.mckinley_e9) } :rodata
.data.patch.fsyscall_table : { *(.data.patch.fsyscall_table) }
.data.patch.vtop : { *(.data.patch.vtop) }
.data.patch.brl_fsys_bubble_down : { *(.data.patch.brl_fsys_bubble_down) }
.IA_64.unwind_info : { *(.IA_64.unwind_info*) }
.IA_64.unwind : { *(.IA_64.unwind*) } :rodata :unwind
. = GATE_ADDR + PAGE_SIZE;
.text : { *(.text) } :rodata
.useless : {
*(.got.plt) *(.got)
*(.data .data.* .gnu.linkonce.d.*)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(__ex_table)
}
}
/*
* We must supply the ELF program headers explicitly to get just one
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
*/
PHDRS
{
rodata PT_LOAD FILEHDR PHDRS FLAGS(4); /* PF_R */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
unwind 0x70000001; /* PT_IA_64_UNWIND */
}
/*
* This controls what symbols we export from the DSO.
*/
VERSION
{
LINUX_2.5 {
global:
__kernel_syscall_via_break;
__kernel_syscall_via_epc;
__kernel_sigtramp;
local: *;
};
}
/* The ELF entry point can be used to set the AT_SYSINFO value. */
ENTRY(syscall_via_epc)
...@@ -128,7 +128,8 @@ ENTRY(vhpt_miss) ...@@ -128,7 +128,8 @@ ENTRY(vhpt_miss)
;; ;;
(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place (p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place
LOAD_PHYSICAL(srlz.d, p6, r19, swapper_pg_dir) // region 5 is rooted at swapper_pg_dir srlz.d
LOAD_PHYSICAL(p6, r19, swapper_pg_dir) // region 5 is rooted at swapper_pg_dir
.pred.rel "mutex", p6, p7 .pred.rel "mutex", p6, p7
(p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT (p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT
...@@ -423,7 +424,8 @@ ENTRY(nested_dtlb_miss) ...@@ -423,7 +424,8 @@ ENTRY(nested_dtlb_miss)
;; ;;
(p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place (p7) dep r17=r17,r19,(PAGE_SHIFT-3),3 // put region number bits in place
LOAD_PHYSICAL(srlz.d, p6, r19, swapper_pg_dir) // region 5 is rooted at swapper_pg_dir srlz.d
LOAD_PHYSICAL(p6, r19, swapper_pg_dir) // region 5 is rooted at swapper_pg_dir
.pred.rel "mutex", p6, p7 .pred.rel "mutex", p6, p7
(p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT (p6) shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
* 6. GR12 = Return address to location within SAL_CHECK * 6. GR12 = Return address to location within SAL_CHECK
*/ */
#define SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(_tmp) \ #define SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(_tmp) \
LOAD_PHYSICAL(nop 0, p0, _tmp, ia64_sal_to_os_handoff_state);; \ LOAD_PHYSICAL(p0, _tmp, ia64_sal_to_os_handoff_state);; \
st8 [_tmp]=r1,0x08;; \ st8 [_tmp]=r1,0x08;; \
st8 [_tmp]=r8,0x08;; \ st8 [_tmp]=r8,0x08;; \
st8 [_tmp]=r9,0x08;; \ st8 [_tmp]=r9,0x08;; \
...@@ -71,8 +71,8 @@ ...@@ -71,8 +71,8 @@
* returns ptr to SAL rtn save loc in _tmp * returns ptr to SAL rtn save loc in _tmp
*/ */
#define OS_MCA_TO_SAL_HANDOFF_STATE_RESTORE(_tmp) \ #define OS_MCA_TO_SAL_HANDOFF_STATE_RESTORE(_tmp) \
LOAD_PHYSICAL(nop 0, p6, _tmp, ia64_sal_to_os_handoff_state);; \ LOAD_PHYSICAL(p6, _tmp, ia64_sal_to_os_handoff_state);; \
LOAD_PHYSICAL(nop 0, p7, _tmp, ia64_os_to_sal_handoff_state);; \ LOAD_PHYSICAL(p7, _tmp, ia64_os_to_sal_handoff_state);; \
(p6) movl r8=IA64_MCA_COLD_BOOT; \ (p6) movl r8=IA64_MCA_COLD_BOOT; \
(p6) movl r10=IA64_MCA_SAME_CONTEXT; \ (p6) movl r10=IA64_MCA_SAME_CONTEXT; \
(p6) add _tmp=0x18,_tmp;; \ (p6) add _tmp=0x18,_tmp;; \
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <asm/patch.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define ARCH_MODULE_DEBUG 0 #define ARCH_MODULE_DEBUG 0
...@@ -158,27 +159,6 @@ slot (const struct insn *insn) ...@@ -158,27 +159,6 @@ slot (const struct insn *insn)
return (uint64_t) insn & 0x3; return (uint64_t) insn & 0x3;
} }
/* Patch instruction with "val" where "mask" has 1 bits. */
static void
apply (struct insn *insn, uint64_t mask, uint64_t val)
{
uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) bundle(insn);
# define insn_mask ((1UL << 41) - 1)
unsigned long shift;
b0 = b[0]; b1 = b[1];
shift = 5 + 41 * slot(insn); /* 5 bits of template, then 3 x 41-bit instructions */
if (shift >= 64) {
m1 = mask << (shift - 64);
v1 = val << (shift - 64);
} else {
m0 = mask << shift; m1 = mask >> (64 - shift);
v0 = val << shift; v1 = val >> (64 - shift);
b[0] = (b0 & ~m0) | (v0 & m0);
}
b[1] = (b1 & ~m1) | (v1 & m1);
}
static int static int
apply_imm64 (struct module *mod, struct insn *insn, uint64_t val) apply_imm64 (struct module *mod, struct insn *insn, uint64_t val)
{ {
...@@ -187,12 +167,7 @@ apply_imm64 (struct module *mod, struct insn *insn, uint64_t val) ...@@ -187,12 +167,7 @@ apply_imm64 (struct module *mod, struct insn *insn, uint64_t val)
mod->name, slot(insn)); mod->name, slot(insn));
return 0; return 0;
} }
apply(insn, 0x01fffefe000, ( ((val & 0x8000000000000000) >> 27) /* bit 63 -> 36 */ ia64_patch_imm64((u64) insn, val);
| ((val & 0x0000000000200000) << 0) /* bit 21 -> 21 */
| ((val & 0x00000000001f0000) << 6) /* bit 16 -> 22 */
| ((val & 0x000000000000ff80) << 20) /* bit 7 -> 27 */
| ((val & 0x000000000000007f) << 13) /* bit 0 -> 13 */));
apply((void *) insn - 1, 0x1ffffffffff, val >> 22);
return 1; return 1;
} }
...@@ -208,9 +183,7 @@ apply_imm60 (struct module *mod, struct insn *insn, uint64_t val) ...@@ -208,9 +183,7 @@ apply_imm60 (struct module *mod, struct insn *insn, uint64_t val)
printk(KERN_ERR "%s: value %ld out of IMM60 range\n", mod->name, (int64_t) val); printk(KERN_ERR "%s: value %ld out of IMM60 range\n", mod->name, (int64_t) val);
return 0; return 0;
} }
apply(insn, 0x011ffffe000, ( ((val & 0x1000000000000000) >> 24) /* bit 60 -> 36 */ ia64_patch_imm60((u64) insn, val);
| ((val & 0x00000000000fffff) << 13) /* bit 0 -> 13 */));
apply((void *) insn - 1, 0x1fffffffffc, val >> 18);
return 1; return 1;
} }
...@@ -221,10 +194,10 @@ apply_imm22 (struct module *mod, struct insn *insn, uint64_t val) ...@@ -221,10 +194,10 @@ apply_imm22 (struct module *mod, struct insn *insn, uint64_t val)
printk(KERN_ERR "%s: value %li out of IMM22 range\n", mod->name, (int64_t)val); printk(KERN_ERR "%s: value %li out of IMM22 range\n", mod->name, (int64_t)val);
return 0; return 0;
} }
apply(insn, 0x01fffcfe000, ( ((val & 0x200000) << 15) /* bit 21 -> 36 */ ia64_patch((u64) insn, 0x01fffcfe000, ( ((val & 0x200000) << 15) /* bit 21 -> 36 */
| ((val & 0x1f0000) << 6) /* bit 16 -> 22 */ | ((val & 0x1f0000) << 6) /* bit 16 -> 22 */
| ((val & 0x00ff80) << 20) /* bit 7 -> 27 */ | ((val & 0x00ff80) << 20) /* bit 7 -> 27 */
| ((val & 0x00007f) << 13) /* bit 0 -> 13 */)); | ((val & 0x00007f) << 13) /* bit 0 -> 13 */));
return 1; return 1;
} }
...@@ -235,8 +208,8 @@ apply_imm21b (struct module *mod, struct insn *insn, uint64_t val) ...@@ -235,8 +208,8 @@ apply_imm21b (struct module *mod, struct insn *insn, uint64_t val)
printk(KERN_ERR "%s: value %li out of IMM21b range\n", mod->name, (int64_t)val); printk(KERN_ERR "%s: value %li out of IMM21b range\n", mod->name, (int64_t)val);
return 0; return 0;
} }
apply(insn, 0x11ffffe000, ( ((val & 0x100000) << 16) /* bit 20 -> 36 */ ia64_patch((u64) insn, 0x11ffffe000, ( ((val & 0x100000) << 16) /* bit 20 -> 36 */
| ((val & 0x0fffff) << 13) /* bit 0 -> 13 */)); | ((val & 0x0fffff) << 13) /* bit 0 -> 13 */));
return 1; return 1;
} }
...@@ -751,7 +724,7 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend, ...@@ -751,7 +724,7 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
if (gp_addressable(mod, val)) { if (gp_addressable(mod, val)) {
/* turn "ld8" into "mov": */ /* turn "ld8" into "mov": */
DEBUGP("%s: patching ld8 at %p to mov\n", __FUNCTION__, location); DEBUGP("%s: patching ld8 at %p to mov\n", __FUNCTION__, location);
apply(location, 0x1fff80fe000, 0x10000000000); ia64_patch((u64) location, 0x1fff80fe000, 0x10000000000);
} }
return 0; return 0;
......
/*
* Instruction-patching support.
*
* Copyright (C) 2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com>
*/
#include <linux/init.h>
#include <linux/string.h>
#include <asm/patch.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/unistd.h>
/*
* This was adapted from code written by Tony Luck:
*
* The 64-bit value in a "movl reg=value" is scattered between the two words of the bundle
* like this:
*
* 6 6 5 4 3 2 1
* 3210987654321098765432109876543210987654321098765432109876543210
* ABBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCDEEEEEFFFFFFFFFGGGGGGG
*
* CCCCCCCCCCCCCCCCCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* xxxxAFFFFFFFFFEEEEEDxGGGGGGGxxxxxxxxxxxxxBBBBBBBBBBBBBBBBBBBBBBB
*/
static u64
get_imm64 (u64 insn_addr)
{
u64 *p = (u64 *) (insn_addr & -16); /* mask out slot number */
return ( (p[1] & 0x0800000000000000UL) << 4) | /*A*/
((p[1] & 0x00000000007fffffUL) << 40) | /*B*/
((p[0] & 0xffffc00000000000UL) >> 24) | /*C*/
((p[1] & 0x0000100000000000UL) >> 23) | /*D*/
((p[1] & 0x0003e00000000000UL) >> 29) | /*E*/
((p[1] & 0x07fc000000000000UL) >> 43) | /*F*/
((p[1] & 0x000007f000000000UL) >> 36); /*G*/
}
/* Patch instruction with "val" where "mask" has 1 bits. */
void
ia64_patch (u64 insn_addr, u64 mask, u64 val)
{
u64 m0, m1, v0, v1, b0, b1, *b = (u64 *) (insn_addr & -16);
# define insn_mask ((1UL << 41) - 1)
unsigned long shift;
b0 = b[0]; b1 = b[1];
shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */
if (shift >= 64) {
m1 = mask << (shift - 64);
v1 = val << (shift - 64);
} else {
m0 = mask << shift; m1 = mask >> (64 - shift);
v0 = val << shift; v1 = val >> (64 - shift);
b[0] = (b0 & ~m0) | (v0 & m0);
}
b[1] = (b1 & ~m1) | (v1 & m1);
}
void
ia64_patch_imm64 (u64 insn_addr, u64 val)
{
ia64_patch(insn_addr,
0x01fffefe000, ( ((val & 0x8000000000000000) >> 27) /* bit 63 -> 36 */
| ((val & 0x0000000000200000) << 0) /* bit 21 -> 21 */
| ((val & 0x00000000001f0000) << 6) /* bit 16 -> 22 */
| ((val & 0x000000000000ff80) << 20) /* bit 7 -> 27 */
| ((val & 0x000000000000007f) << 13) /* bit 0 -> 13 */));
ia64_patch(insn_addr - 1, 0x1ffffffffff, val >> 22);
}
void
ia64_patch_imm60 (u64 insn_addr, u64 val)
{
ia64_patch(insn_addr,
0x011ffffe000, ( ((val & 0x1000000000000000) >> 24) /* bit 60 -> 36 */
| ((val & 0x00000000000fffff) << 13) /* bit 0 -> 13 */));
ia64_patch(insn_addr - 1, 0x1fffffffffc, val >> 18);
}
/*
* We need sometimes to load the physical address of a kernel
* object. Often we can convert the virtual address to physical
* at execution time, but sometimes (either for performance reasons
* or during error recovery) we cannot to this. Patch the marked
* bundles to load the physical address.
* The 64-bit value in a "movl reg=value" is scattered between the
* two words of the bundle like this:
*
* 6 6 5 4 3 2 1
* 3210987654321098765432109876543210987654321098765432109876543210
* ABBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCDEEEEEFFFFFFFFFGGGGGGG
*
* CCCCCCCCCCCCCCCCCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* xxxxAFFFFFFFFFEEEEEDxGGGGGGGxxxxxxxxxxxxxBBBBBBBBBBBBBBBBBBBBBBB
*/
void __init
ia64_patch_vtop (unsigned long start, unsigned long end)
{
s32 *offp = (s32 *) start;
u64 ip;
while (offp < (s32 *) end) {
ip = (u64) offp + *offp;
/* replace virtual address with corresponding physical address: */
ia64_patch_imm64(ip, ia64_tpa(get_imm64(ip)));
++offp;
}
}
void
ia64_patch_mckinley_e9 (unsigned long start, unsigned long end)
{
static int first_time = 1;
int need_workaround;
s32 *offp = (s32 *) start;
u64 *wp;
need_workaround = (local_cpu_data->family == 0x1f && local_cpu_data->model == 0);
if (first_time) {
first_time = 0;
if (need_workaround)
printk(KERN_INFO "Leaving McKinley Errata 9 workaround enabled\n");
else
printk(KERN_INFO "McKinley Errata 9 workaround not needed; "
"disabling it\n");
}
if (need_workaround)
return;
while (offp < (s32 *) end) {
wp = (u64 *) ia64_imva((char *) offp + *offp);
wp[0] = 0x0000000100000000;
wp[1] = 0x0004000000000200;
ia64_fc(wp);
++offp;
}
ia64_insn_group_barrier();
ia64_sync_i();
ia64_insn_group_barrier();
ia64_srlz_i();
ia64_insn_group_barrier();
}
static void
patch_fsyscall_table (unsigned long start, unsigned long end)
{
extern unsigned long fsyscall_table[NR_syscalls];
s32 *offp = (s32 *) start;
while (offp < (s32 *) end) {
ia64_patch_imm64((u64) ia64_imva((char *) offp + *offp), (u64) fsyscall_table);
++offp;
}
}
static void
patch_brl_fsys_bubble_down (unsigned long start, unsigned long end)
{
extern char fsys_bubble_down[];
s32 *offp = (s32 *) start;
u64 ip;
while (offp < (s32 *) end) {
ip = (u64) offp + *offp;
ia64_patch_imm60((u64) ia64_imva((void *) ip),
(u64) (fsys_bubble_down - (ip & -16)) / 16);
++offp;
}
}
void
ia64_patch_gate (Elf64_Ehdr *ehdr)
{
Elf64_Shdr *shdr, *strsec, *sec;
Elf64_Half i;
unsigned long start, end;
char *strtab, *name;
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0 || ehdr->e_type != ET_DYN
|| !elf_check_arch(ehdr))
/*
* Without the gate shared library, we can't do signal return or fast
* system calls. In other words, the kernel will crash quickly anyhow, so
* we might just as well do it now...
*/
panic("%s: gate shared library at %p not a valid ELF object!\n",
__FUNCTION__, ehdr);
shdr = (void *) ((char *) ehdr + ehdr->e_shoff);
strsec = (void *) ((char *) shdr + ehdr->e_shstrndx * ehdr->e_shentsize);
strtab = (void *) ((char *) ehdr + strsec->sh_offset);
for (i = 0; i < ehdr->e_shnum; ++i) {
sec = (void *) ((char *) shdr + i * ehdr->e_shentsize);
if (strncmp(strtab + sec->sh_name, ".data.patch.", 12) != 0)
continue;
if (sec->sh_size == 0)
continue;
name = strtab + sec->sh_name + 12;
start = sec->sh_addr;
end = start + sec->sh_size;
if (strcmp(name, "fsyscall_table") == 0)
patch_fsyscall_table(start, end);
else if (strcmp(name, "brl_fsys_bubble_down") == 0)
patch_brl_fsys_bubble_down(start, end);
else if (strcmp(name, "top") == 0)
ia64_patch_vtop(start, end);
else if (strcmp(name, "mckinley_e9") == 0)
ia64_patch_mckinley_e9(start, end);
else
panic("%s: found unknown patch-list `%s'\n", __FUNCTION__, name);
}
}
...@@ -138,14 +138,12 @@ show_regs (struct pt_regs *regs) ...@@ -138,14 +138,12 @@ show_regs (struct pt_regs *regs)
void void
do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall) do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall)
{ {
#ifdef CONFIG_FSYS
if (fsys_mode(current, &scr->pt)) { if (fsys_mode(current, &scr->pt)) {
/* defer signal-handling etc. until we return to privilege-level 0. */ /* defer signal-handling etc. until we return to privilege-level 0. */
if (!ia64_psr(&scr->pt)->lp) if (!ia64_psr(&scr->pt)->lp)
ia64_psr(&scr->pt)->lp = 1; ia64_psr(&scr->pt)->lp = 1;
return; return;
} }
#endif
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
if (current->thread.pfm_needs_checking) if (current->thread.pfm_needs_checking)
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <asm/machvec.h> #include <asm/machvec.h>
#include <asm/mca.h> #include <asm/mca.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/patch.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/sal.h> #include <asm/sal.h>
...@@ -358,64 +359,16 @@ find_memory (void) ...@@ -358,64 +359,16 @@ find_memory (void)
#endif #endif
} }
/*
* We need sometimes to load the physical address of a kernel
* object. Often we can convert the virtual address to physical
* at execution time, but sometimes (either for performance reasons
* or during error recovery) we cannot to this. Patch the marked
* bundles to load the physical address.
* The 64-bit value in a "movl reg=value" is scattered between the
* two words of the bundle like this:
*
* 6 6 5 4 3 2 1
* 3210987654321098765432109876543210987654321098765432109876543210
* ABBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCDEEEEEFFFFFFFFFGGGGGGG
*
* CCCCCCCCCCCCCCCCCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* xxxxAFFFFFFFFFEEEEEDxGGGGGGGxxxxxxxxxxxxxBBBBBBBBBBBBBBBBBBBBBBB
*/
static void __init
patch_physical (void)
{
extern unsigned long *__start___vtop_patchlist[], *__end____vtop_patchlist[];
unsigned long **e, *p, paddr, vaddr;
for (e = __start___vtop_patchlist; e < __end____vtop_patchlist; e++) {
p = *e;
vaddr = ((p[1] & 0x0800000000000000UL) << 4) | /*A*/
((p[1] & 0x00000000007fffffUL) << 40) | /*B*/
((p[0] & 0xffffc00000000000UL) >> 24) | /*C*/
((p[1] & 0x0000100000000000UL) >> 23) | /*D*/
((p[1] & 0x0003e00000000000UL) >> 29) | /*E*/
((p[1] & 0x07fc000000000000UL) >> 43) | /*F*/
((p[1] & 0x000007f000000000UL) >> 36); /*G*/
paddr = ia64_tpa(vaddr);
*p = (*p & 0x3fffffffffffUL) |
((paddr & 0x000000ffffc00000UL)<<24); /*C*/
p++;
*p = (*p & 0xf000080fff800000UL) |
((paddr & 0x8000000000000000UL) >> 4) | /*A*/
((paddr & 0x7fffff0000000000UL) >> 40) | /*B*/
((paddr & 0x0000000000200000UL) << 23) | /*D*/
((paddr & 0x00000000001f0000UL) << 29) | /*E*/
((paddr & 0x000000000000ff80UL) << 43) | /*F*/
((paddr & 0x000000000000007fUL) << 36); /*G*/
}
}
void __init void __init
setup_arch (char **cmdline_p) setup_arch (char **cmdline_p)
{ {
extern unsigned long *__start___vtop_patchlist[], *__end____vtop_patchlist[];
extern unsigned long ia64_iobase; extern unsigned long ia64_iobase;
unsigned long phys_iobase; unsigned long phys_iobase;
unw_init(); unw_init();
patch_physical(); ia64_patch_vtop((u64) __start___vtop_patchlist, (u64) __end____vtop_patchlist);
*cmdline_p = __va(ia64_boot_param->command_line); *cmdline_p = __va(ia64_boot_param->command_line);
strncpy(saved_command_line, *cmdline_p, sizeof(saved_command_line)); strncpy(saved_command_line, *cmdline_p, sizeof(saved_command_line));
...@@ -522,8 +475,6 @@ setup_arch (char **cmdline_p) ...@@ -522,8 +475,6 @@ setup_arch (char **cmdline_p)
platform_setup(cmdline_p); platform_setup(cmdline_p);
paging_init(); paging_init();
unw_create_gate_table();
} }
/* /*
...@@ -860,27 +811,9 @@ cpu_init (void) ...@@ -860,27 +811,9 @@ cpu_init (void)
void void
check_bugs (void) check_bugs (void)
{ {
extern int __start___mckinley_e9_bundles[]; extern char __start___mckinley_e9_bundles[];
extern int __end___mckinley_e9_bundles[]; extern char __end___mckinley_e9_bundles[];
u64 *bundle;
int *wp;
if (local_cpu_data->family == 0x1f && local_cpu_data->model == 0) ia64_patch_mckinley_e9((unsigned long) __start___mckinley_e9_bundles,
printk(KERN_INFO "check_bugs: leaving McKinley Errata 9 workaround enabled\n"); (unsigned long) __end___mckinley_e9_bundles);
else {
printk(KERN_INFO "check_bugs: McKinley Errata 9 workaround not needed; "
"disabling it\n");
for (wp = __start___mckinley_e9_bundles; wp < __end___mckinley_e9_bundles; ++wp) {
bundle = (u64 *) ((char *) wp + *wp);
/* install a bundle of NOPs: */
bundle[0] = 0x0000000100000000;
bundle[1] = 0x0004000000000200;
ia64_fc(bundle);
}
ia64_insn_group_barrier();
ia64_sync_i();
ia64_insn_group_barrier();
ia64_srlz_i();
ia64_insn_group_barrier();
}
} }
...@@ -395,14 +395,14 @@ static long ...@@ -395,14 +395,14 @@ static long
setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set,
struct sigscratch *scr) struct sigscratch *scr)
{ {
extern char ia64_sigtramp[], __start_gate_section[]; extern char __kernel_sigtramp[];
unsigned long tramp_addr, new_rbs = 0; unsigned long tramp_addr, new_rbs = 0;
struct sigframe *frame; struct sigframe *frame;
struct siginfo si; struct siginfo si;
long err; long err;
frame = (void *) scr->pt.r12; frame = (void *) scr->pt.r12;
tramp_addr = GATE_ADDR + (ia64_sigtramp - __start_gate_section); tramp_addr = (unsigned long) __kernel_sigtramp;
if ((ka->sa.sa_flags & SA_ONSTACK) && sas_ss_flags((unsigned long) frame) == 0) { if ((ka->sa.sa_flags & SA_ONSTACK) && sas_ss_flags((unsigned long) frame) == 0) {
frame = (void *) ((current->sas_ss_sp + current->sas_ss_size) frame = (void *) ((current->sas_ss_sp + current->sas_ss_size)
& ~(STACK_ALIGN - 1)); & ~(STACK_ALIGN - 1));
......
...@@ -528,9 +528,8 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, ...@@ -528,9 +528,8 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
case 29: /* Debug */ case 29: /* Debug */
case 35: /* Taken Branch Trap */ case 35: /* Taken Branch Trap */
case 36: /* Single Step Trap */ case 36: /* Single Step Trap */
#ifdef CONFIG_FSYS
if (fsys_mode(current, regs)) { if (fsys_mode(current, regs)) {
extern char syscall_via_break[], __start_gate_section[]; extern char __kernel_syscall_via_break[];
/* /*
* Got a trap in fsys-mode: Taken Branch Trap and Single Step trap * Got a trap in fsys-mode: Taken Branch Trap and Single Step trap
* need special handling; Debug trap is not supposed to happen. * need special handling; Debug trap is not supposed to happen.
...@@ -541,12 +540,11 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, ...@@ -541,12 +540,11 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
return; return;
} }
/* re-do the system call via break 0x100000: */ /* re-do the system call via break 0x100000: */
regs->cr_iip = GATE_ADDR + (syscall_via_break - __start_gate_section); regs->cr_iip = (unsigned long) __kernel_syscall_via_break;
ia64_psr(regs)->ri = 0; ia64_psr(regs)->ri = 0;
ia64_psr(regs)->cpl = 3; ia64_psr(regs)->cpl = 3;
return; return;
} }
#endif
switch (vector) { switch (vector) {
case 29: case 29:
siginfo.si_code = TRAP_HWBKPT; siginfo.si_code = TRAP_HWBKPT;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
* acquired, then the read-write lock must be acquired first. * acquired, then the read-write lock must be acquired first.
*/ */
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/elf.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -1840,11 +1841,7 @@ unw_unwind (struct unw_frame_info *info) ...@@ -1840,11 +1841,7 @@ unw_unwind (struct unw_frame_info *info)
return -1; return -1;
} }
ip = info->ip = *info->rp_loc; ip = info->ip = *info->rp_loc;
if (ip < GATE_ADDR + PAGE_SIZE) { if (ip < GATE_ADDR) {
/*
* We don't have unwind info for the gate page, so we consider that part
* of user-space for the purpose of unwinding.
*/
UNW_DPRINT(2, "unwind.%s: reached user-space (ip=0x%lx)\n", __FUNCTION__, ip); UNW_DPRINT(2, "unwind.%s: reached user-space (ip=0x%lx)\n", __FUNCTION__, ip);
STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags)); STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
return -1; return -1;
...@@ -1917,11 +1914,7 @@ unw_unwind_to_user (struct unw_frame_info *info) ...@@ -1917,11 +1914,7 @@ unw_unwind_to_user (struct unw_frame_info *info)
__FUNCTION__, ip); __FUNCTION__, ip);
return -1; return -1;
} }
/* if (ip < GATE_ADDR)
* We don't have unwind info for the gate page, so we consider that part
* of user-space for the purpose of unwinding.
*/
if (ip < GATE_ADDR + PAGE_SIZE)
return 0; return 0;
} }
unw_get_ip(info, &ip); unw_get_ip(info, &ip);
...@@ -2131,30 +2124,41 @@ unw_remove_unwind_table (void *handle) ...@@ -2131,30 +2124,41 @@ unw_remove_unwind_table (void *handle)
kfree(table); kfree(table);
} }
void static void __init
unw_create_gate_table (void) create_gate_table (void)
{ {
extern char __start_gate_section[], __stop_gate_section[]; const struct unw_table_entry *entry, *start, *end;
unsigned long *lp, start, end, segbase = unw.kernel_table.segment_base; unsigned long *lp, segbase = GATE_ADDR;
const struct unw_table_entry *entry, *first, *unw_table_end;
extern int ia64_unw_end;
size_t info_size, size; size_t info_size, size;
char *info; char *info;
Elf64_Phdr *punw = NULL, *phdr = (Elf64_Phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
int i;
for (i = 0; i < GATE_EHDR->e_phnum; ++i, ++phdr)
if (phdr->p_type == PT_IA_64_UNWIND) {
punw = phdr;
break;
}
start = (unsigned long) __start_gate_section - segbase; if (!punw) {
end = (unsigned long) __stop_gate_section - segbase; printk("%s: failed to find gate DSO's unwind table!\n", __FUNCTION__);
unw_table_end = (struct unw_table_entry *) &ia64_unw_end; return;
}
start = (const struct unw_table_entry *) punw->p_vaddr;
end = (struct unw_table_entry *) ((char *) start + punw->p_memsz);
size = 0; size = 0;
first = lookup(&unw.kernel_table, start);
for (entry = first; entry < unw_table_end && entry->start_offset < end; ++entry) unw_add_unwind_table("linux-gate.so", segbase, 0, start, end);
for (entry = start; entry < end; ++entry)
size += 3*8 + 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset)); size += 3*8 + 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset));
size += 8; /* reserve space for "end of table" marker */ size += 8; /* reserve space for "end of table" marker */
unw.gate_table = alloc_bootmem(size); unw.gate_table = kmalloc(size, GFP_KERNEL);
if (!unw.gate_table) { if (!unw.gate_table) {
unw.gate_table_size = 0; unw.gate_table_size = 0;
printk(KERN_ERR "unwind: unable to create unwind data for gate page!\n"); printk(KERN_ERR "%s: unable to create unwind data for gate page!\n", __FUNCTION__);
return; return;
} }
unw.gate_table_size = size; unw.gate_table_size = size;
...@@ -2162,19 +2166,21 @@ unw_create_gate_table (void) ...@@ -2162,19 +2166,21 @@ unw_create_gate_table (void)
lp = unw.gate_table; lp = unw.gate_table;
info = (char *) unw.gate_table + size; info = (char *) unw.gate_table + size;
for (entry = first; entry < unw_table_end && entry->start_offset < end; ++entry, lp += 3) { for (entry = start; entry < end; ++entry, lp += 3) {
info_size = 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset)); info_size = 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset));
info -= info_size; info -= info_size;
memcpy(info, (char *) segbase + entry->info_offset, info_size); memcpy(info, (char *) segbase + entry->info_offset, info_size);
lp[0] = entry->start_offset - start + GATE_ADDR; /* start */ lp[0] = segbase + entry->start_offset; /* start */
lp[1] = entry->end_offset - start + GATE_ADDR; /* end */ lp[1] = segbase + entry->end_offset; /* end */
lp[2] = info - (char *) unw.gate_table; /* info */ lp[2] = info - (char *) unw.gate_table; /* info */
} }
*lp = 0; /* end-of-table marker */ *lp = 0; /* end-of-table marker */
} }
void __initcall(create_gate_table);
void __init
unw_init (void) unw_init (void)
{ {
extern int ia64_unw_start, ia64_unw_end, __gp; extern int ia64_unw_start, ia64_unw_end, __gp;
...@@ -2215,6 +2221,14 @@ unw_init (void) ...@@ -2215,6 +2221,14 @@ unw_init (void)
} }
/* /*
* DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
*
* This system call has been deprecated. The new and improved way to get
* at the kernel's unwind info is via the gate DSO. The address of the
* ELF header for this DSO is passed to user-level via AT_SYSINFO_EHDR.
*
* DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
*
* This system call copies the unwind data into the buffer pointed to by BUF and returns * This system call copies the unwind data into the buffer pointed to by BUF and returns
* the size of the unwind data. If BUF_SIZE is smaller than the size of the unwind data * the size of the unwind data. If BUF_SIZE is smaller than the size of the unwind data
* or if BUF is NULL, nothing is copied, but the system call still returns the size of the * or if BUF is NULL, nothing is copied, but the system call still returns the size of the
......
/* /*
* Initialize MMU support. * Initialize MMU support.
* *
* Copyright (C) 1998-2002 Hewlett-Packard Co * Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/efi.h>
#include <linux/elf.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/efi.h>
#include <linux/mmzone.h>
#include <asm/a.out.h> #include <asm/a.out.h>
#include <asm/bitops.h> #include <asm/bitops.h>
...@@ -23,12 +24,13 @@ ...@@ -23,12 +24,13 @@
#include <asm/ia32.h> #include <asm/ia32.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/machvec.h> #include <asm/machvec.h>
#include <asm/patch.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/sal.h> #include <asm/sal.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/tlb.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <asm/tlb.h>
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
...@@ -247,18 +249,17 @@ show_mem(void) ...@@ -247,18 +249,17 @@ show_mem(void)
} }
/* /*
* This is like put_dirty_page() but installs a clean page with PAGE_GATE protection * This is like put_dirty_page() but installs a clean page in the kernel's page table.
* (execute-only, typically).
*/ */
struct page * struct page *
put_gate_page (struct page *page, unsigned long address) put_kernel_page (struct page *page, unsigned long address, pgprot_t pgprot)
{ {
pgd_t *pgd; pgd_t *pgd;
pmd_t *pmd; pmd_t *pmd;
pte_t *pte; pte_t *pte;
if (!PageReserved(page)) if (!PageReserved(page))
printk(KERN_ERR "put_gate_page: gate page at 0x%p not in reserved memory\n", printk(KERN_ERR "put_kernel_page: page at 0x%p not in reserved memory\n",
page_address(page)); page_address(page));
pgd = pgd_offset_k(address); /* note: this is NOT pgd_offset()! */ pgd = pgd_offset_k(address); /* note: this is NOT pgd_offset()! */
...@@ -275,7 +276,7 @@ put_gate_page (struct page *page, unsigned long address) ...@@ -275,7 +276,7 @@ put_gate_page (struct page *page, unsigned long address)
pte_unmap(pte); pte_unmap(pte);
goto out; goto out;
} }
set_pte(pte, mk_pte(page, PAGE_GATE)); set_pte(pte, mk_pte(page, pgprot));
pte_unmap(pte); pte_unmap(pte);
} }
out: spin_unlock(&init_mm.page_table_lock); out: spin_unlock(&init_mm.page_table_lock);
...@@ -283,6 +284,19 @@ put_gate_page (struct page *page, unsigned long address) ...@@ -283,6 +284,19 @@ put_gate_page (struct page *page, unsigned long address)
return page; return page;
} }
static void
setup_gate (void)
{
extern char __start_gate_section[];
/* install the read-only and privilege-promote pages in the global page table: */
put_kernel_page(virt_to_page(ia64_imva(__start_gate_section)), GATE_ADDR, PAGE_READONLY);
put_kernel_page(virt_to_page(ia64_imva(__start_gate_section + PAGE_SIZE)),
GATE_ADDR + PAGE_SIZE, PAGE_GATE);
ia64_patch_gate((Elf64_Ehdr *) __start_gate_section);
}
void __init void __init
ia64_mmu_init (void *my_cpu_data) ia64_mmu_init (void *my_cpu_data)
{ {
...@@ -582,8 +596,6 @@ count_reserved_pages (u64 start, u64 end, void *arg) ...@@ -582,8 +596,6 @@ count_reserved_pages (u64 start, u64 end, void *arg)
return 0; return 0;
} }
#ifdef CONFIG_FSYS
/* /*
* Boot command-line option "nolwsys" can be used to disable the use of any light-weight * Boot command-line option "nolwsys" can be used to disable the use of any light-weight
* system call handler. When this option is in effect, all fsyscalls will end up bubbling * system call handler. When this option is in effect, all fsyscalls will end up bubbling
...@@ -603,15 +615,13 @@ nolwsys_setup (char *s) ...@@ -603,15 +615,13 @@ nolwsys_setup (char *s)
__setup("nolwsys", nolwsys_setup); __setup("nolwsys", nolwsys_setup);
#endif /* CONFIG_FSYS */
void void
mem_init (void) mem_init (void)
{ {
extern char __start_gate_section[];
long reserved_pages, codesize, datasize, initsize; long reserved_pages, codesize, datasize, initsize;
unsigned long num_pgt_pages; unsigned long num_pgt_pages;
pg_data_t *pgdat; pg_data_t *pgdat;
int i;
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
/* /*
...@@ -658,27 +668,19 @@ mem_init (void) ...@@ -658,27 +668,19 @@ mem_init (void)
if (num_pgt_pages > (u64) pgt_cache_water[1]) if (num_pgt_pages > (u64) pgt_cache_water[1])
pgt_cache_water[1] = num_pgt_pages; pgt_cache_water[1] = num_pgt_pages;
#ifdef CONFIG_FSYS /*
{ * For fsyscall entrpoints with no light-weight handler, use the ordinary
int i; * (heavy-weight) handler, but mark it by setting bit 0, so the fsyscall entry
* code can tell them apart.
/* */
* For fsyscall entrpoints with no light-weight handler, use the ordinary for (i = 0; i < NR_syscalls; ++i) {
* (heavy-weight) handler, but mark it by setting bit 0, so the fsyscall entry extern unsigned long fsyscall_table[NR_syscalls];
* code can tell them apart. extern unsigned long sys_call_table[NR_syscalls];
*/
for (i = 0; i < NR_syscalls; ++i) {
extern unsigned long fsyscall_table[NR_syscalls];
extern unsigned long sys_call_table[NR_syscalls];
if (!fsyscall_table[i] || nolwsys)
fsyscall_table[i] = sys_call_table[i] | 1;
}
}
#endif
/* install the gate page in the global page table: */ if (!fsyscall_table[i] || nolwsys)
put_gate_page(virt_to_page(ia64_imva(__start_gate_section)), GATE_ADDR); fsyscall_table[i] = sys_call_table[i] | 1;
}
setup_gate(); /* setup gate pages before we free up boot memory... */
#ifdef CONFIG_IA32_SUPPORT #ifdef CONFIG_IA32_SUPPORT
ia32_gdt_init(); ia32_gdt_init();
......
...@@ -55,17 +55,17 @@ SECTIONS ...@@ -55,17 +55,17 @@ SECTIONS
__stop___ex_table = .; __stop___ex_table = .;
} }
__vtop_patchlist : AT(ADDR(__vtop_patchlist) - LOAD_OFFSET) .data.patch.vtop : AT(ADDR(.data.patch.vtop) - LOAD_OFFSET)
{ {
__start___vtop_patchlist = .; __start___vtop_patchlist = .;
*(__vtop_patchlist) *(.data.patch.vtop)
__end____vtop_patchlist = .; __end____vtop_patchlist = .;
} }
__mckinley_e9_bundles : AT(ADDR(__mckinley_e9_bundles) - LOAD_OFFSET) .data.patch.mckinley_e9 : AT(ADDR(.data.patch.mckinley_e9) - LOAD_OFFSET)
{ {
__start___mckinley_e9_bundles = .; __start___mckinley_e9_bundles = .;
*(__mckinley_e9_bundles) *(.data.patch.mckinley_e9)
__end___mckinley_e9_bundles = .; __end___mckinley_e9_bundles = .;
} }
...@@ -159,7 +159,7 @@ SECTIONS ...@@ -159,7 +159,7 @@ SECTIONS
.data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET)
{ *(__special_page_section) { *(__special_page_section)
__start_gate_section = .; __start_gate_section = .;
*(.text.gate) *(.data.gate)
__stop_gate_section = .; __stop_gate_section = .;
} }
......
...@@ -56,15 +56,12 @@ ...@@ -56,15 +56,12 @@
* path (ivt.S - TLB miss processing) or in places where it might not be * path (ivt.S - TLB miss processing) or in places where it might not be
* safe to use a "tpa" instruction (mca_asm.S - error recovery). * safe to use a "tpa" instruction (mca_asm.S - error recovery).
*/ */
.section "__vtop_patchlist", "a" // declare section & section attributes .section ".data.patch.vtop", "a" // declare section & section attributes
.previous .previous
#define LOAD_PHYSICAL(op, preg, reg, obj) \ #define LOAD_PHYSICAL(pr, reg, obj) \
1: { .mlx; \ [1:](pr)movl reg = obj; \
op; \ .xdata4 ".data.patch.vtop", 1b-.
(preg) movl reg = obj; \
}; \
.xdata8 "__vtop_patchlist", 1b
/* /*
* For now, we always put in the McKinley E9 workaround. On CPUs that don't need it, * For now, we always put in the McKinley E9 workaround. On CPUs that don't need it,
...@@ -72,11 +69,11 @@ ...@@ -72,11 +69,11 @@
*/ */
#define DO_MCKINLEY_E9_WORKAROUND #define DO_MCKINLEY_E9_WORKAROUND
#ifdef DO_MCKINLEY_E9_WORKAROUND #ifdef DO_MCKINLEY_E9_WORKAROUND
.section "__mckinley_e9_bundles", "a" .section ".data.patch.mckinley_e9", "a"
.previous .previous
/* workaround for Itanium 2 Errata 9: */ /* workaround for Itanium 2 Errata 9: */
# define MCKINLEY_E9_WORKAROUND \ # define MCKINLEY_E9_WORKAROUND \
.xdata4 "__mckinley_e9_bundles", 1f-.; \ .xdata4 ".data.patch.mckinley_e9", 1f-.;\
1:{ .mib; \ 1:{ .mib; \
nop.m 0; \ nop.m 0; \
nop.i 0; \ nop.i 0; \
......
...@@ -42,6 +42,8 @@ ...@@ -42,6 +42,8 @@
*/ */
#define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x800000000) #define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x800000000)
#define PT_IA_64_UNWIND 0x70000001
/* IA-64 relocations: */ /* IA-64 relocations: */
#define R_IA64_NONE 0x00 /* none */ #define R_IA64_NONE 0x00 /* none */
#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ #define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
...@@ -176,28 +178,73 @@ extern void ia64_elf_core_copy_regs (struct pt_regs *src, elf_gregset_t dst); ...@@ -176,28 +178,73 @@ extern void ia64_elf_core_copy_regs (struct pt_regs *src, elf_gregset_t dst);
#define ELF_PLATFORM 0 #define ELF_PLATFORM 0
/* /*
* This should go into linux/elf.h... * Architecture-neutral AT_ values are in the range 0-17. Leave some room for more of
* them, start the architecture-specific ones at 32.
*/ */
#define AT_SYSINFO 32 #define AT_SYSINFO 32
#define AT_SYSINFO_EHDR 33
#ifdef __KERNEL__ #ifdef __KERNEL__
struct elf64_hdr; struct elf64_hdr;
extern void ia64_set_personality (struct elf64_hdr *elf_ex, int ibcs2_interpreter); extern void ia64_set_personality (struct elf64_hdr *elf_ex, int ibcs2_interpreter);
#define SET_PERSONALITY(ex, ibcs2) ia64_set_personality(&(ex), ibcs2) #define SET_PERSONALITY(ex, ibcs2) ia64_set_personality(&(ex), ibcs2)
struct task_struct;
extern int dump_task_regs(struct task_struct *, elf_gregset_t *); extern int dump_task_regs(struct task_struct *, elf_gregset_t *);
extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *); extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *);
#define ELF_CORE_COPY_TASK_REGS(tsk, elf_gregs) dump_task_regs(tsk, elf_gregs) #define ELF_CORE_COPY_TASK_REGS(tsk, elf_gregs) dump_task_regs(tsk, elf_gregs)
#define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs)
#ifdef CONFIG_FSYS #define GATE_EHDR ((const struct elfhdr *) GATE_ADDR)
#define ARCH_DLINFO \
do { \ #define ARCH_DLINFO \
extern char syscall_via_epc[], __start_gate_section[]; \ do { \
NEW_AUX_ENT(AT_SYSINFO, GATE_ADDR + (syscall_via_epc - __start_gate_section)); \ extern char __kernel_syscall_via_epc[]; \
NEW_AUX_ENT(AT_SYSINFO, __kernel_syscall_via_epc); \
NEW_AUX_ENT(AT_SYSINFO_EHDR, (unsigned long) GATE_EHDR); \
} while (0)
/*
* These macros parameterize elf_core_dump in fs/binfmt_elf.c to write out extra segments
* containing the gate DSO contents. Dumping its contents makes post-mortem fully
* interpretable later without matching up the same kernel and hardware config to see what
* IP values meant. Dumping its extra ELF program headers includes all the other
* information a debugger needs to easily find how the gate DSO was being used.
*/
#define ELF_CORE_EXTRA_PHDRS (GATE_EHDR->e_phnum)
#define ELF_CORE_WRITE_EXTRA_PHDRS \
do { \
const struct elf_phdr *const gate_phdrs = \
(const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff); \
int i; \
Elf64_Off ofs = 0; \
for (i = 0; i < GATE_EHDR->e_phnum; ++i) { \
struct elf_phdr phdr = gate_phdrs[i]; \
if (phdr.p_type == PT_LOAD) { \
ofs = phdr.p_offset = offset; \
phdr.p_filesz = PAGE_SIZE; /* just cover RO-data */ \
offset += phdr.p_filesz; \
} else \
phdr.p_offset += ofs; \
phdr.p_paddr = 0; /* match other core phdrs */ \
DUMP_WRITE(&phdr, sizeof(phdr)); \
} \
} while (0)
#define ELF_CORE_WRITE_EXTRA_DATA \
do { \
const struct elf_phdr *const gate_phdrs = \
(const struct elf_phdr *) (GATE_ADDR \
+ GATE_EHDR->e_phoff); \
int i; \
for (i = 0; i < GATE_EHDR->e_phnum; ++i) { \
if (gate_phdrs[i].p_type == PT_LOAD) \
DUMP_WRITE((void *) gate_phdrs[i].p_vaddr, \
gate_phdrs[i].p_filesz); \
} \
} while (0) } while (0)
#endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
#ifndef _ASM_IA64_PATCH_H
#define _ASM_IA64_PATCH_H
/*
* Copyright (C) 2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com>
*
* There are a number of reasons for patching instructions. Rather than duplicating code
* all over the place, we put the common stuff here. Reasons for patching: in-kernel
* module-loader, virtual-to-physical patch-list, McKinley Errata 9 workaround, and gate
* shared library. Undoubtedly, some of these reasons will disappear and others will
* be added over time.
*/
#include <linux/elf.h>
#include <linux/types.h>
extern void ia64_patch (u64 insn_addr, u64 mask, u64 val); /* patch any insn slot */
extern void ia64_patch_imm64 (u64 insn_addr, u64 val); /* patch "movl" w/abs. value*/
extern void ia64_patch_imm60 (u64 insn_addr, u64 val); /* patch "brl" w/ip-rel value */
extern void ia64_patch_mckinley_e9 (unsigned long start, unsigned long end);
extern void ia64_patch_vtop (unsigned long start, unsigned long end);
extern void ia64_patch_gate (Elf64_Ehdr *ehdr);
#endif /* _ASM_IA64_PATCH_H */
...@@ -258,6 +258,7 @@ ia64_phys_addr_valid (unsigned long addr) ...@@ -258,6 +258,7 @@ ia64_phys_addr_valid (unsigned long addr)
/* /*
* The following have defined behavior only work if pte_present() is true. * The following have defined behavior only work if pte_present() is true.
*/ */
#define pte_user(pte) ((pte_val(pte) & _PAGE_PL_MASK) == _PAGE_PL_3)
#define pte_read(pte) (((pte_val(pte) & _PAGE_AR_MASK) >> _PAGE_AR_SHIFT) < 6) #define pte_read(pte) (((pte_val(pte) & _PAGE_AR_MASK) >> _PAGE_AR_SHIFT) < 6)
#define pte_write(pte) ((unsigned) (((pte_val(pte) & _PAGE_AR_MASK) >> _PAGE_AR_SHIFT) - 2) <= 4) #define pte_write(pte) ((unsigned) (((pte_val(pte) & _PAGE_AR_MASK) >> _PAGE_AR_SHIFT) - 2) <= 4)
#define pte_exec(pte) ((pte_val(pte) & _PAGE_AR_RX) != 0) #define pte_exec(pte) ((pte_val(pte) & _PAGE_AR_RX) != 0)
...@@ -486,4 +487,8 @@ typedef pte_t *pte_addr_t; ...@@ -486,4 +487,8 @@ typedef pte_t *pte_addr_t;
*/ */
#define pgtable_cache_init() do { } while (0) #define pgtable_cache_init() do { } while (0)
/* These tell get_user_pages() that the first gate page is accessible from user-level. */
#define FIXADDR_START GATE_ADDR
#define FIXADDR_TOP (GATE_ADDR + PAGE_SIZE)
#endif /* _ASM_IA64_PGTABLE_H */ #endif /* _ASM_IA64_PGTABLE_H */
...@@ -19,11 +19,10 @@ ...@@ -19,11 +19,10 @@
#include <asm/pal.h> #include <asm/pal.h>
#include <asm/percpu.h> #include <asm/percpu.h>
#define KERNEL_START (0xa000000100000000) /* 0xa000000000000000 - 0xa000000000000000+PERCPU_PAGE_SIZE remain unmapped */
/* 0xa000000000000000 - 0xa000000000000000+PERCPU_MAX_SIZE remain unmapped */
#define PERCPU_ADDR (0xa000000000000000 + PERCPU_PAGE_SIZE) #define PERCPU_ADDR (0xa000000000000000 + PERCPU_PAGE_SIZE)
#define GATE_ADDR (0xa000000000000000 + 2*PERCPU_PAGE_SIZE) #define GATE_ADDR (0xa000000000000000 + 2*PERCPU_PAGE_SIZE)
#define KERNEL_START 0xa000000100000000
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
......
...@@ -97,7 +97,6 @@ struct unw_frame_info { ...@@ -97,7 +97,6 @@ struct unw_frame_info {
* Initialize unwind support. * Initialize unwind support.
*/ */
extern void unw_init (void); extern void unw_init (void);
extern void unw_create_gate_table (void);
extern void *unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp, extern void *unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp,
const void *table_start, const void *table_end); const void *table_start, const void *table_end);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment