diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-06 14:45:32 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-06 14:45:32 -0800 |
commit | 02aedd69e2ef31b0fca1e8960cb1e7fd0c343110 (patch) | |
tree | c096ab87e0832e8ddda45241b422c0064cfe0cbb /include | |
parent | 9ad0830f307bcd8dc285cfae58998d43b21727f4 (diff) | |
parent | 4d284cac76d0bfebc42d76b428c4e44d921200a9 (diff) |
Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6: (37 commits)
[S390] Avoid excessive inlining.
[S390] Mark kernel text section read-only.
[S390] Convert memory detection into C code.
[S390] Calibrate delay and bogomips.
[S390] Hypervisor filesystem (s390_hypfs) for z/VM
[S390] Add crypto support for 3592 tape devices
[S390] boot from NSS support
[S390] Support for s390 Pseudo Random Number Generator
[S390] ETR support.
[S390] noexec protection
[S390] move crypto options and some cleanup.
[S390] cio: Don't spam debug feature.
[S390] Cleanup of CHSC event handling.
[S390] cio: declare hardware structures packed.
[S390] Add set_fs(USER_DS) to start_thread().
[S390] cio: Catch operand exceptions on stsch.
[S390] Fix register usage description.
[S390] kretprobe_trampoline_holder() in wrong section.
[S390] Fix kprobes breakpoint handling.
[S390] Update maintainers file.
...
Diffstat (limited to 'include')
-rw-r--r-- | include/asm-s390/compat.h | 28 | ||||
-rw-r--r-- | include/asm-s390/etr.h | 219 | ||||
-rw-r--r-- | include/asm-s390/hardirq.h | 2 | ||||
-rw-r--r-- | include/asm-s390/io.h | 4 | ||||
-rw-r--r-- | include/asm-s390/kdebug.h | 3 | ||||
-rw-r--r-- | include/asm-s390/lowcore.h | 6 | ||||
-rw-r--r-- | include/asm-s390/mmu_context.h | 50 | ||||
-rw-r--r-- | include/asm-s390/pgalloc.h | 85 | ||||
-rw-r--r-- | include/asm-s390/pgtable.h | 147 | ||||
-rw-r--r-- | include/asm-s390/processor.h | 27 | ||||
-rw-r--r-- | include/asm-s390/ptrace.h | 11 | ||||
-rw-r--r-- | include/asm-s390/reset.h | 3 | ||||
-rw-r--r-- | include/asm-s390/sclp.h | 39 | ||||
-rw-r--r-- | include/asm-s390/sections.h | 2 | ||||
-rw-r--r-- | include/asm-s390/setup.h | 23 | ||||
-rw-r--r-- | include/asm-s390/sfp-util.h | 66 | ||||
-rw-r--r-- | include/asm-s390/smp.h | 6 | ||||
-rw-r--r-- | include/asm-s390/system.h | 4 | ||||
-rw-r--r-- | include/asm-s390/tape390.h | 72 | ||||
-rw-r--r-- | include/asm-s390/timer.h | 3 | ||||
-rw-r--r-- | include/asm-s390/timex.h | 50 | ||||
-rw-r--r-- | include/asm-s390/tlbflush.h | 9 | ||||
-rw-r--r-- | include/asm-s390/uaccess.h | 2 |
23 files changed, 790 insertions, 71 deletions
diff --git a/include/asm-s390/compat.h b/include/asm-s390/compat.h index 356a0b18353..296f4f1a20e 100644 --- a/include/asm-s390/compat.h +++ b/include/asm-s390/compat.h @@ -6,6 +6,34 @@ #include <linux/types.h> #include <linux/sched.h> +#define PSW32_MASK_PER 0x40000000UL +#define PSW32_MASK_DAT 0x04000000UL +#define PSW32_MASK_IO 0x02000000UL +#define PSW32_MASK_EXT 0x01000000UL +#define PSW32_MASK_KEY 0x00F00000UL +#define PSW32_MASK_MCHECK 0x00040000UL +#define PSW32_MASK_WAIT 0x00020000UL +#define PSW32_MASK_PSTATE 0x00010000UL +#define PSW32_MASK_ASC 0x0000C000UL +#define PSW32_MASK_CC 0x00003000UL +#define PSW32_MASK_PM 0x00000f00UL + +#define PSW32_ADDR_AMODE31 0x80000000UL +#define PSW32_ADDR_INSN 0x7FFFFFFFUL + +#define PSW32_BASE_BITS 0x00080000UL + +#define PSW32_ASC_PRIMARY 0x00000000UL +#define PSW32_ASC_ACCREG 0x00004000UL +#define PSW32_ASC_SECONDARY 0x00008000UL +#define PSW32_ASC_HOME 0x0000C000UL + +#define PSW32_MASK_MERGE(CURRENT,NEW) \ + (((CURRENT) & ~(PSW32_MASK_CC|PSW32_MASK_PM)) | \ + ((NEW) & (PSW32_MASK_CC|PSW32_MASK_PM))) + +extern long psw32_user_bits; + #define COMPAT_USER_HZ 100 typedef u32 compat_size_t; diff --git a/include/asm-s390/etr.h b/include/asm-s390/etr.h new file mode 100644 index 00000000000..b498f19bb9a --- /dev/null +++ b/include/asm-s390/etr.h @@ -0,0 +1,219 @@ +/* + * include/asm-s390/etr.h + * + * Copyright IBM Corp. 2006 + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + */ +#ifndef __S390_ETR_H +#define __S390_ETR_H + +/* ETR attachment control register */ +struct etr_eacr { + unsigned int e0 : 1; /* port 0 stepping control */ + unsigned int e1 : 1; /* port 1 stepping control */ + unsigned int _pad0 : 5; /* must be 00100 */ + unsigned int dp : 1; /* data port control */ + unsigned int p0 : 1; /* port 0 change recognition control */ + unsigned int p1 : 1; /* port 1 change recognition control */ + unsigned int _pad1 : 3; /* must be 000 */ + unsigned int ea : 1; /* ETR alert control */ + unsigned int es : 1; /* ETR sync check control */ + unsigned int sl : 1; /* switch to local control */ +} __attribute__ ((packed)); + +/* Port state returned by steai */ +enum etr_psc { + etr_psc_operational = 0, + etr_psc_semi_operational = 1, + etr_psc_protocol_error = 4, + etr_psc_no_symbols = 8, + etr_psc_no_signal = 12, + etr_psc_pps_mode = 13 +}; + +/* Logical port state returned by stetr */ +enum etr_lpsc { + etr_lpsc_operational_step = 0, + etr_lpsc_operational_alt = 1, + etr_lpsc_semi_operational = 2, + etr_lpsc_protocol_error = 4, + etr_lpsc_no_symbol_sync = 8, + etr_lpsc_no_signal = 12, + etr_lpsc_pps_mode = 13 +}; + +/* ETR status words */ +struct etr_esw { + struct etr_eacr eacr; /* attachment control register */ + unsigned int y : 1; /* stepping mode */ + unsigned int _pad0 : 5; /* must be 00000 */ + unsigned int p : 1; /* stepping port number */ + unsigned int q : 1; /* data port number */ + unsigned int psc0 : 4; /* port 0 state code */ + unsigned int psc1 : 4; /* port 1 state code */ +} __attribute__ ((packed)); + +/* Second level data register status word */ +struct etr_slsw { + unsigned int vv1 : 1; /* copy of validity bit data frame 1 */ + unsigned int vv2 : 1; /* copy of validity bit data frame 2 */ + unsigned int vv3 : 1; /* copy of validity bit data frame 3 */ + unsigned int vv4 : 1; /* copy of validity bit data frame 4 */ + unsigned int _pad0 : 19; /* must by all zeroes */ + unsigned int n : 1; /* EAF port number */ + unsigned int v1 : 1; /* validity bit ETR data frame 1 */ + unsigned int v2 : 1; /* validity bit ETR data frame 2 */ + unsigned int v3 : 1; /* validity bit ETR data frame 3 */ + unsigned int v4 : 1; /* validity bit ETR data frame 4 */ + unsigned int _pad1 : 4; /* must be 0000 */ +} __attribute__ ((packed)); + +/* ETR data frames */ +struct etr_edf1 { + unsigned int u : 1; /* untuned bit */ + unsigned int _pad0 : 1; /* must be 0 */ + unsigned int r : 1; /* service request bit */ + unsigned int _pad1 : 4; /* must be 0000 */ + unsigned int a : 1; /* time adjustment bit */ + unsigned int net_id : 8; /* ETR network id */ + unsigned int etr_id : 8; /* id of ETR which sends data frames */ + unsigned int etr_pn : 8; /* port number of ETR output port */ +} __attribute__ ((packed)); + +struct etr_edf2 { + unsigned int etv : 32; /* Upper 32 bits of TOD. */ +} __attribute__ ((packed)); + +struct etr_edf3 { + unsigned int rc : 8; /* failure reason code */ + unsigned int _pad0 : 3; /* must be 000 */ + unsigned int c : 1; /* ETR coupled bit */ + unsigned int tc : 4; /* ETR type code */ + unsigned int blto : 8; /* biased local time offset */ + /* (blto - 128) * 15 = minutes */ + unsigned int buo : 8; /* biased utc offset */ + /* (buo - 128) = leap seconds */ +} __attribute__ ((packed)); + +struct etr_edf4 { + unsigned int ed : 8; /* ETS device dependent data */ + unsigned int _pad0 : 1; /* must be 0 */ + unsigned int buc : 5; /* biased ut1 correction */ + /* (buc - 16) * 0.1 seconds */ + unsigned int em : 6; /* ETS error magnitude */ + unsigned int dc : 6; /* ETS drift code */ + unsigned int sc : 6; /* ETS steering code */ +} __attribute__ ((packed)); + +/* + * ETR attachment information block, two formats + * format 1 has 4 reserved words with a size of 64 bytes + * format 2 has 16 reserved words with a size of 96 bytes + */ +struct etr_aib { + struct etr_esw esw; + struct etr_slsw slsw; + unsigned long long tsp; + struct etr_edf1 edf1; + struct etr_edf2 edf2; + struct etr_edf3 edf3; + struct etr_edf4 edf4; + unsigned int reserved[16]; +} __attribute__ ((packed,aligned(8))); + +/* ETR interruption parameter */ +struct etr_interruption_parameter { + unsigned int _pad0 : 8; + unsigned int pc0 : 1; /* port 0 state change */ + unsigned int pc1 : 1; /* port 1 state change */ + unsigned int _pad1 : 3; + unsigned int eai : 1; /* ETR alert indication */ + unsigned int _pad2 : 18; +} __attribute__ ((packed)); + +/* Query TOD offset result */ +struct etr_ptff_qto { + unsigned long long physical_clock; + unsigned long long tod_offset; + unsigned long long logical_tod_offset; + unsigned long long tod_epoch_difference; +} __attribute__ ((packed)); + +/* Inline assembly helper functions */ +static inline int etr_setr(struct etr_eacr *ctrl) +{ + int rc = -ENOSYS; + + asm volatile( + " .insn s,0xb2160000,0(%2)\n" + "0: la %0,0\n" + "1:\n" + EX_TABLE(0b,1b) + : "+d" (rc) : "m" (*ctrl), "a" (ctrl)); + return rc; +} + +/* Stores a format 1 aib with 64 bytes */ +static inline int etr_stetr(struct etr_aib *aib) +{ + int rc = -ENOSYS; + + asm volatile( + " .insn s,0xb2170000,0(%2)\n" + "0: la %0,0\n" + "1:\n" + EX_TABLE(0b,1b) + : "+d" (rc) : "m" (*aib), "a" (aib)); + return rc; +} + +/* Stores a format 2 aib with 96 bytes for specified port */ +static inline int etr_steai(struct etr_aib *aib, unsigned int func) +{ + register unsigned int reg0 asm("0") = func; + int rc = -ENOSYS; + + asm volatile( + " .insn s,0xb2b30000,0(%2)\n" + "0: la %0,0\n" + "1:\n" + EX_TABLE(0b,1b) + : "+d" (rc) : "m" (*aib), "a" (aib), "d" (reg0)); + return rc; +} + +/* Function codes for the steai instruction. */ +#define ETR_STEAI_STEPPING_PORT 0x10 +#define ETR_STEAI_ALTERNATE_PORT 0x11 +#define ETR_STEAI_PORT_0 0x12 +#define ETR_STEAI_PORT_1 0x13 + +static inline int etr_ptff(void *ptff_block, unsigned int func) +{ + register unsigned int reg0 asm("0") = func; + register unsigned long reg1 asm("1") = (unsigned long) ptff_block; + int rc = -ENOSYS; + + asm volatile( + " .word 0x0104\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (rc), "=m" (ptff_block) + : "d" (reg0), "d" (reg1), "m" (ptff_block) : "cc"); + return rc; +} + +/* Function codes for the ptff instruction. */ +#define ETR_PTFF_QAF 0x00 /* query available functions */ +#define ETR_PTFF_QTO 0x01 /* query tod offset */ +#define ETR_PTFF_QSI 0x02 /* query steering information */ +#define ETR_PTFF_ATO 0x40 /* adjust tod offset */ +#define ETR_PTFF_STO 0x41 /* set tod offset */ +#define ETR_PTFF_SFS 0x42 /* set fine steering rate */ +#define ETR_PTFF_SGS 0x43 /* set gross steering rate */ + +/* Functions needed by the machine check handler */ +extern void etr_switch_to_local(void); +extern void etr_sync_check(void); + +#endif /* __S390_ETR_H */ diff --git a/include/asm-s390/hardirq.h b/include/asm-s390/hardirq.h index c2f6a8782d3..31beb18cb3d 100644 --- a/include/asm-s390/hardirq.h +++ b/include/asm-s390/hardirq.h @@ -32,6 +32,6 @@ typedef struct { #define HARDIRQ_BITS 8 -extern void account_ticks(void); +extern void account_ticks(u64 time); #endif /* __ASM_HARDIRQ_H */ diff --git a/include/asm-s390/io.h b/include/asm-s390/io.h index efb7de9c1c6..a4c2d550dad 100644 --- a/include/asm-s390/io.h +++ b/include/asm-s390/io.h @@ -28,11 +28,7 @@ static inline unsigned long virt_to_phys(volatile void * address) { unsigned long real_address; asm volatile( -#ifndef __s390x__ " lra %0,0(%1)\n" -#else /* __s390x__ */ - " lrag %0,0(%1)\n" -#endif /* __s390x__ */ " jz 0f\n" " la %0,0\n" "0:" diff --git a/include/asm-s390/kdebug.h b/include/asm-s390/kdebug.h index 40cc68025e0..1b50f89819a 100644 --- a/include/asm-s390/kdebug.h +++ b/include/asm-s390/kdebug.h @@ -26,7 +26,6 @@ extern int register_page_fault_notifier(struct notifier_block *); extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head s390die_chain; - enum die_val { DIE_OOPS = 1, DIE_BPT, @@ -56,4 +55,6 @@ static inline int notify_die(enum die_val val, const char *str, return atomic_notifier_call_chain(&s390die_chain, val, &args); } +extern void die(const char *, struct pt_regs *, long); + #endif diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index 74f7389bd3e..4a31d0a7ee8 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -220,7 +220,8 @@ struct _lowcore __u32 kernel_asce; /* 0xc4c */ __u32 user_asce; /* 0xc50 */ __u32 panic_stack; /* 0xc54 */ - __u8 pad10[0xc60-0xc58]; /* 0xc58 */ + __u32 user_exec_asce; /* 0xc58 */ + __u8 pad10[0xc60-0xc5c]; /* 0xc5c */ /* entry.S sensitive area start */ struct cpuinfo_S390 cpu_data; /* 0xc60 */ __u32 ipl_device; /* 0xc7c */ @@ -310,7 +311,8 @@ struct _lowcore __u64 kernel_asce; /* 0xd58 */ __u64 user_asce; /* 0xd60 */ __u64 panic_stack; /* 0xd68 */ - __u8 pad10[0xd80-0xd70]; /* 0xd70 */ + __u64 user_exec_asce; /* 0xd70 */ + __u8 pad10[0xd80-0xd78]; /* 0xd78 */ /* entry.S sensitive area start */ struct cpuinfo_S390 cpu_data; /* 0xd80 */ __u32 ipl_device; /* 0xdb8 */ diff --git a/include/asm-s390/mmu_context.h b/include/asm-s390/mmu_context.h index bcf24a87387..1d21da220d4 100644 --- a/include/asm-s390/mmu_context.h +++ b/include/asm-s390/mmu_context.h @@ -9,6 +9,7 @@ #ifndef __S390_MMU_CONTEXT_H #define __S390_MMU_CONTEXT_H +#include <asm/pgalloc.h> /* * get a new mmu context.. S390 don't know about contexts. */ @@ -16,29 +17,44 @@ #define destroy_context(mm) do { } while (0) +#ifndef __s390x__ +#define LCTL_OPCODE "lctl" +#define PGTABLE_BITS (_SEGMENT_TABLE|USER_STD_MASK) +#else +#define LCTL_OPCODE "lctlg" +#define PGTABLE_BITS (_REGION_TABLE|USER_STD_MASK) +#endif + static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) { } static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk) + struct task_struct *tsk) { - if (prev != next) { -#ifndef __s390x__ - S390_lowcore.user_asce = (__pa(next->pgd)&PAGE_MASK) | - (_SEGMENT_TABLE|USER_STD_MASK); - /* Load home space page table origin. */ - asm volatile("lctl 13,13,%0" - : : "m" (S390_lowcore.user_asce) ); -#else /* __s390x__ */ - S390_lowcore.user_asce = (__pa(next->pgd) & PAGE_MASK) | - (_REGION_TABLE|USER_STD_MASK); - /* Load home space page table origin. */ - asm volatile("lctlg 13,13,%0" - : : "m" (S390_lowcore.user_asce) ); -#endif /* __s390x__ */ - } + pgd_t *shadow_pgd = get_shadow_pgd(next->pgd); + + if (prev != next) { + S390_lowcore.user_asce = (__pa(next->pgd) & PAGE_MASK) | + PGTABLE_BITS; + if (shadow_pgd) { + /* Load primary/secondary space page table origin. */ + S390_lowcore.user_exec_asce = + (__pa(shadow_pgd) & PAGE_MASK) | PGTABLE_BITS; + asm volatile(LCTL_OPCODE" 1,1,%0\n" + LCTL_OPCODE" 7,7,%1" + : : "m" (S390_lowcore.user_exec_asce), + "m" (S390_lowcore.user_asce) ); + } else if (switch_amode) { + /* Load primary space page table origin. */ + asm volatile(LCTL_OPCODE" 1,1,%0" + : : "m" (S390_lowcore.user_asce) ); + } else + /* Load home space page table origin. */ + asm volatile(LCTL_OPCODE" 13,13,%0" + : : "m" (S390_lowcore.user_asce) ); + } cpu_set(smp_processor_id(), next->cpu_vm_mask); } @@ -51,4 +67,4 @@ static inline void activate_mm(struct mm_struct *prev, set_fs(current->thread.mm_segment); } -#endif +#endif /* __S390_MMU_CONTEXT_H */ diff --git a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h index 0707a7e2fc1..56c8a6c80e2 100644 --- a/include/asm-s390/pgalloc.h +++ b/include/asm-s390/pgalloc.h @@ -47,6 +47,17 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) if (!pgd) return NULL; + if (s390_noexec) { + pgd_t *shadow_pgd = (pgd_t *) + __get_free_pages(GFP_KERNEL, PGD_ALLOC_ORDER); + struct page *page = virt_to_page(pgd); + + if (!shadow_pgd) { + free_pages((unsigned long) pgd, PGD_ALLOC_ORDER); + return NULL; + } + page->lru.next = (void *) shadow_pgd; + } for (i = 0; i < PTRS_PER_PGD; i++) #ifndef __s390x__ pmd_clear(pmd_offset(pgd + i, i*PGDIR_SIZE)); @@ -58,6 +69,10 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) static inline void pgd_free(pgd_t *pgd) { + pgd_t *shadow_pgd = get_shadow_pgd(pgd); + + if (shadow_pgd) + free_pages((unsigned long) shadow_pgd, PGD_ALLOC_ORDER); free_pages((unsigned long) pgd, PGD_ALLOC_ORDER); } @@ -71,6 +86,7 @@ static inline void pgd_free(pgd_t *pgd) #define pmd_free(x) do { } while (0) #define __pmd_free_tlb(tlb,x) do { } while (0) #define pgd_populate(mm, pmd, pte) BUG() +#define pgd_populate_kernel(mm, pmd, pte) BUG() #else /* __s390x__ */ static inline pmd_t * pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr) { @@ -79,6 +95,17 @@ static inline pmd_t * pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr) if (!pmd) return NULL; + if (s390_noexec) { + pmd_t *shadow_pmd = (pmd_t *) + __get_free_pages(GFP_KERNEL, PMD_ALLOC_ORDER); + struct page *page = virt_to_page(pmd); + + if (!shadow_pmd) { + free_pages((unsigned long) pmd, PMD_ALLOC_ORDER); + return NULL; + } + page->lru.next = (void *) shadow_pmd; + } for (i=0; i < PTRS_PER_PMD; i++) pmd_clear(pmd + i); return pmd; @@ -86,6 +113,10 @@ static inline pmd_t * pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr) static inline void pmd_free (pmd_t *pmd) { + pmd_t *shadow_pmd = get_shadow_pmd(pmd); + + if (shadow_pmd) + free_pages((unsigned long) shadow_pmd, PMD_ALLOC_ORDER); free_pages((unsigned long) pmd, PMD_ALLOC_ORDER); } @@ -95,11 +126,22 @@ static inline void pmd_free (pmd_t *pmd) pmd_free(pmd); \ } while (0) -static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) +static inline void +pgd_populate_kernel(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) { pgd_val(*pgd) = _PGD_ENTRY | __pa(pmd); } +static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pmd_t *pmd) +{ + pgd_t *shadow_pgd = get_shadow_pgd(pgd); + pmd_t *shadow_pmd = get_shadow_pmd(pmd); + + if (shadow_pgd && shadow_pmd) + pgd_populate_kernel(mm, shadow_pgd, shadow_pmd); + pgd_populate_kernel(mm, pgd, pmd); +} + #endif /* __s390x__ */ static inline void @@ -119,7 +161,13 @@ pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *page) { - pmd_populate_kernel(mm, pmd, (pte_t *)page_to_phys(page)); + pte_t *pte = (pte_t *)page_to_phys(page); + pmd_t *shadow_pmd = get_shadow_pmd(pmd); + pte_t *shadow_pte = get_shadow_pte(pte); + + pmd_populate_kernel(mm, pmd, pte); + if (shadow_pmd && shadow_pte) + pmd_populate_kernel(mm, shadow_pmd, shadow_pte); } /* @@ -133,6 +181,17 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long vmaddr) if (!pte) return NULL; + if (s390_noexec) { + pte_t *shadow_pte = (pte_t *) + __get_free_page(GFP_KERNEL|__GFP_REPEAT); + struct page *page = virt_to_page(pte); + + if (!shadow_pte) { + free_page((unsigned long) pte); + return NULL; + } + page->lru.next = (void *) shadow_pte; + } for (i=0; i < PTRS_PER_PTE; i++) { pte_clear(mm, vmaddr, pte + i); vmaddr += PAGE_SIZE; @@ -151,14 +210,30 @@ pte_alloc_one(struct mm_struct *mm, unsigned long vmaddr) static inline void pte_free_kernel(pte_t *pte) { - free_page((unsigned long) pte); + pte_t *shadow_pte = get_shadow_pte(pte); + + if (shadow_pte) + free_page((unsigned long) shadow_pte); + free_page((unsigned long) pte); } static inline void pte_free(struct page *pte) { - __free_page(pte); + struct page *shadow_page = get_shadow_page(pte); + + if (shadow_page) + __free_page(shadow_page); + __free_page(pte); } -#define __pte_free_tlb(tlb,pte) tlb_remove_page(tlb,pte) +#define __pte_free_tlb(tlb, pte) \ +({ \ + struct mmu_gather *__tlb = (tlb); \ + struct page *__pte = (pte); \ + struct page *shadow_page = get_shadow_page(__pte); \ + if (shadow_page) \ + tlb_remove_page(__tlb, shadow_page); \ + tlb_remove_page(__tlb, __pte); \ +}) #endif /* _S390_PGALLOC_H */ diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index ae61aca5d48..13c16546eff 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -40,6 +40,7 @@ struct mm_struct; extern pgd_t swapper_pg_dir[] __attribute__ ((aligned (4096))); extern void paging_init(void); +extern void vmem_map_init(void); /* * The S390 doesn't have any external MMU info: the kernel page @@ -223,6 +224,8 @@ extern unsigned long vmalloc_end; #define _PAGE_TYPE_FILE 0x601 /* bit 0x002 is used for offset !! */ #define _PAGE_TYPE_RO 0x200 #define _PAGE_TYPE_RW 0x000 +#define _PAGE_TYPE_EX_RO 0x202 +#define _PAGE_TYPE_EX_RW 0x002 /* * PTE type bits are rather complicated. handle_pte_fault uses pte_present, @@ -243,11 +246,13 @@ extern unsigned long vmalloc_end; * _PAGE_TYPE_FILE 11?1 -> 11?1 * _PAGE_TYPE_RO 0100 -> 1100 * _PAGE_TYPE_RW 0000 -> 1000 + * _PAGE_TYPE_EX_RO 0110 -> 1110 + * _PAGE_TYPE_EX_RW 0010 -> 1010 * - * pte_none is true for bits combinations 1000, 1100 + * pte_none is true for bits combinations 1000, 1010, 1100, 1110 * pte_present is true for bits combinations 0000, 0010, 0100, 0110, 1001 * pte_file is true for bits combinations 1101, 1111 - * swap pte is 1011 and 0001, 0011, 0101, 0111, 1010 and 1110 are invalid. + * swap pte is 1011 and 0001, 0011, 0101, 0111 are invalid. */ #ifndef __s390x__ @@ -312,33 +317,100 @@ extern unsigned long vmalloc_end; #define PAGE_NONE __pgprot(_PAGE_TYPE_NONE) #define PAGE_RO __pgprot(_PAGE_TYPE_RO) #define PAGE_RW __pgprot(_PAGE_TYPE_RW) +#define PAGE_EX_RO __pgprot(_PAGE_TYPE_EX_RO) +#define PAGE_EX_RW __pgprot(_PAGE_TYPE_EX_RW) #define PAGE_KERNEL PAGE_RW #define PAGE_COPY PAGE_RO /* - * The S390 can't do page protection for execute, and considers that the - * same are read. Also, write permissions imply read permissions. This is - * the closest we can get.. + * Dependent on the EXEC_PROTECT option s390 can do execute protection. + * Write permission always implies read permission. In theory with a + * primary/secondary page table execute only can be implemented but + * it would cost an additional bit in the pte to distinguish all the + * different pte types. To avoid that execute permission currently + * implies read permission as well. */ /*xwr*/ #define __P000 PAGE_NONE #define __P001 PAGE_RO #define __P010 PAGE_RO #define __P011 PAGE_RO -#define __P100 PAGE_RO -#define __P101 PAGE_RO -#define __P110 PAGE_RO -#define __P111 PAGE_RO +#define __P100 PAGE_EX_RO +#define __P101 PAGE_EX_RO +#define __P110 PAGE_EX_RO +#define __P111 PAGE_EX_RO #define __S000 PAGE_NONE #define __S001 PAGE_RO #define __S010 PAGE_RW #define __S011 PAGE_RW -#define __S100 PAGE_RO -#define __S101 PAGE_RO -#define __S110 PAGE_RW -#define __S111 PAGE_RW +#define __S100 PAGE_EX_RO +#define __S101 PAGE_EX_RO +#define __S110 PAGE_EX_RW +#define __S111 PAGE_EX_RW + +#ifndef __s390x__ +# define PMD_SHADOW_SHIFT 1 +# define PGD_SHADOW_SHIFT 1 +#else /* __s390x__ */ +# define PMD_SHADOW_SHIFT 2 +# define PGD_SHADOW_SHIFT 2 +#endif /* __s390x__ */ + +static inline struct page *get_shadow_page(struct page *page) +{ + if (s390_noexec && !list_empty(&page->lru)) + return virt_to_page(page->lru.next); + return NULL; +} + +static inline pte_t *get_shadow_pte(pte_t *ptep) +{ + unsigned long pteptr = (unsigned long) (ptep); + + if (s390_noexec) { + unsigned long offset = pteptr & (PAGE_SIZE - 1); + void *addr = (void *) (pteptr ^ offset); + struct page *page = virt_to_page(addr); + if (!list_empty(&page->lru)) + return (pte_t *) ((unsigned long) page->lru.next | + offset); + } + return NULL; +} + +static inline pmd_t *get_shadow_pmd(pmd_t *pmdp) +{ + unsigned long pmdptr = (unsigned long) (pmdp); + + if (s390_noexec) { + unsigned long offset = pmdptr & + ((PAGE_SIZE << PMD_SHADOW_SHIFT) - 1); + void *addr = (void *) (pmdptr ^ offset); + struct page *page = virt_to_page(addr); + if (!list_empty(&page->lru)) + return (pmd_t *) ((unsigned long) page->lru.next | + offset); + } + return NULL; +} + +static inline pgd_t *get_shadow_pgd(pgd_t *pgdp) +{ + unsigned long pgdptr = (unsigned long) (pgdp); + + if (s390_noexec) { + unsigned long offset = pgdptr & + ((PAGE_SIZE << PGD_SHADOW_SHIFT) - 1); + void *addr = (void *) (pgdptr ^ offset); + struct page *page = virt_to_page(addr); + if (!list_empty(&page->lru)) + return (pgd_t *) ((unsigned long) page->lru.next | + offset); + } + return NULL; +} /* * Certain architectures need to do special things when PTEs @@ -347,7 +419,16 @@ extern unsigned long vmalloc_end; */ static inline void set_pte(pte_t *pteptr, pte_t pteval) { + pte_t *shadow_pte = get_shadow_pte(pteptr); + *pteptr = pteval; + if (shadow_pte) { + if (!(pte_val(pteval) & _PAGE_INVALID) && + (pte_val(pteval) & _PAGE_SWX)) + pte_val(*shadow_pte) = pte_val(pteval) | _PAGE_RO; + else + pte_val(*shadow_pte) = _PAGE_TYPE_EMPTY; + } } #define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval) @@ -465,7 +546,7 @@ static inline int pte_read(pte_t pte) static inline void pgd_clear(pgd_t * pgdp) { } -static inline void pmd_clear(pmd_t * pmdp) +static inline void pmd_clear_kernel(pmd_t * pmdp) { pmd_val(pmdp[0]) = _PAGE_TABLE_INV; pmd_val(pmdp[1]) = _PAGE_TABLE_INV; @@ -473,24 +554,55 @@ static inline void pmd_clear(pmd_t * pmdp) pmd_val(pmdp[3]) = _PAGE_TABLE_INV; } +static inline void pmd_clear(pmd_t * pmdp) +{ + pmd_t *shadow_pmd = get_shadow_pmd(pmdp); + + pmd_clear_kernel(pmdp); + if (shadow_pmd) + pmd_clear_kernel(shadow_pmd); +} + #else /* __s390x__ */ -static inline void pgd_clear(pgd_t * pgdp) +static inline void pgd_clear_kernel(pgd_t * pgdp) { pgd_val(*pgdp) = _PGD_ENTRY_INV | _PGD_ENTRY; } -static inline void pmd_clear(pmd_t * pmdp) +static inline void pgd_clear(pgd_t * pgdp) +{ + pgd_t *shadow_pgd = get_shadow_pgd(pgdp); + + pgd_clear_kernel(pgdp); + if (shadow_pgd) + pgd_clear_kernel(shadow_pgd); +} + +static inline void pmd_clear_kernel(pmd_t * pmdp) { pmd_val(*pmdp) = _PMD_ENTRY_INV | _PMD_ENTRY; pmd_val1(*pmdp) = _PMD_ENTRY_INV | _PMD_ENTRY; } +static inline void pmd_clear(pmd_t * pmdp) +{ + pmd_t *shadow_pmd = get_shadow_pmd(pmdp); + + pmd_clear_kernel(pmdp); + if (shadow_pmd) + pmd_clear_kernel(shadow_pmd); +} + #endif /* __s390x__ */ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { + pte_t *shadow_pte = get_shadow_pte(ptep); + pte_val(*ptep) = _PAGE_TYPE_EMPTY; + if (shadow_pte) + pte_val(*shadow_pte) = _PAGE_TYPE_EMPTY; } /* @@ -608,8 +720,11 @@ ptep_clear_flush(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) { pte_t pte = *ptep; + pte_t *shadow_pte = get_shadow_pte(ptep); __ptep_ipte(address, ptep); + if (shadow_pte) + __ptep_ipte(address, shadow_pte); return pte; } diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index cbbedc63ba2..4c1b7394035 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -50,6 +50,7 @@ struct cpuinfo_S390 unsigned long pgtable_cache_sz; }; +extern void s390_adjust_jiffies(void); extern void print_cpu_info(struct cpuinfo_S390 *); /* Lazy FPU handling on uni-processor */ @@ -144,7 +145,8 @@ struct stack_frame { #ifndef __s390x__ #define start_thread(regs, new_psw, new_stackp) do { \ - regs->psw.mask = PSW_USER_BITS; \ + set_fs(USER_DS); \ + regs->psw.mask = psw_user_bits; \ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ regs->gprs[15] = new_stackp ; \ } while (0) @@ -152,13 +154,15 @@ struct stack_frame { #else /* __s390x__ */ #define start_thread(regs, new_psw, new_stackp) do { \ - regs->psw.mask = PSW_USER_BITS; \ + set_fs(USER_DS); \ + regs->psw.mask = psw_user_bits; \ regs->psw.addr = new_psw; \ regs->gprs[15] = new_stackp; \ } while (0) #define start_thread31(regs, new_psw, new_stackp) do { \ - regs->psw.mask = PSW_USER32_BITS; \ + set_fs(USER_DS); \ + regs->psw.mask = psw_user32_bits; \ regs->psw.addr = new_psw; \ regs->gprs[15] = new_stackp; \ } while (0) @@ -201,9 +205,8 @@ unsigned long get_wchan(struct task_struct *p); static inline void cpu_relax(void) { if (MACHINE_HAS_DIAG44) - asm volatile("diag 0,0,68" : : : "memory"); - else - barrier(); + asm volatile("diag 0,0,68"); + barrier(); } /* @@ -328,6 +331,18 @@ static inline void disabled_wait(unsigned long code) } /* + * Basic Machine Check/Program Check Handler. + */ + +extern void s390_base_mcck_handler(void); +extern void s390_base_pgm_handler(void); +extern void s390_base_ext_handler(void); + +extern void (*s390_base_mcck_handler_fn)(void); +extern void (*s390_base_pgm_handler_fn)(void); +extern void (*s390_base_ext_handler_fn)(void); |