aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/setup.c
diff options
context:
space:
mode:
authorGerald Schaefer <geraldsc@de.ibm.com>2007-02-05 15:18:17 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-02-05 15:18:17 -0500
commitc1821c2e9711adc3cd298a16b7237c92a2cee78d (patch)
tree9155b089db35a37d95863125ea4c5f918bd7801b /arch/s390/kernel/setup.c
parent86aa9fc2456d8a662f299a70bdb70987209170f0 (diff)
[S390] noexec protection
This provides a noexec protection on s390 hardware. Our hardware does not have any bits left in the pte for a hw noexec bit, so this is a different approach using shadow page tables and a special addressing mode that allows separate address spaces for code and data. As a special feature of our "secondary-space" addressing mode, separate page tables can be specified for the translation of data addresses (storage operands) and instruction addresses. The shadow page table is used for the instruction addresses and the standard page table for the data addresses. The shadow page table is linked to the standard page table by a pointer in page->lru.next of the struct page corresponding to the page that contains the standard page table (since page->private is not really private with the pte_lock and the page table pages are not in the LRU list). Depending on the software bits of a pte, it is either inserted into both page tables or just into the standard (data) page table. Pages of a vma that does not have the VM_EXEC bit set get mapped only in the data address space. Any try to execute code on such a page will cause a page translation exception. The standard reaction to this is a SIGSEGV with two exceptions: the two system call opcodes 0x0a77 (sys_sigreturn) and 0x0aad (sys_rt_sigreturn) are allowed. They are stored by the kernel to the signal stack frame. Unfortunately, the signal return mechanism cannot be modified to use an SA_RESTORER because the exception unwinding code depends on the system call opcode stored behind the signal stack frame. This feature requires that user space is executed in secondary-space mode and the kernel in home-space mode, which means that the addressing modes need to be switched and that the noexec protection only works for user space. After switching the addressing modes, we cannot use the mvcp/mvcs instructions anymore to copy between kernel and user space. A new mvcos instruction has been added to the z9 EC/BC hardware which allows to copy between arbitrary address spaces, but on older hardware the page tables need to be walked manually. Signed-off-by: Gerald Schaefer <geraldsc@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/setup.c')
-rw-r--r--arch/s390/kernel/setup.c98
1 files changed, 93 insertions, 5 deletions
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 25bf7277d311..b1b9a931237d 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -50,6 +50,13 @@
50#include <asm/page.h> 50#include <asm/page.h>
51#include <asm/ptrace.h> 51#include <asm/ptrace.h>
52#include <asm/sections.h> 52#include <asm/sections.h>
53#include <asm/compat.h>
54
55long psw_kernel_bits = (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY |
56 PSW_MASK_MCHECK | PSW_DEFAULT_KEY);
57long psw_user_bits = (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME |
58 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
59 PSW_MASK_PSTATE | PSW_DEFAULT_KEY);
53 60
54/* 61/*
55 * User copy operations. 62 * User copy operations.
@@ -383,6 +390,84 @@ static int __init early_parse_ipldelay(char *p)
383} 390}
384early_param("ipldelay", early_parse_ipldelay); 391early_param("ipldelay", early_parse_ipldelay);
385 392
393#ifdef CONFIG_S390_SWITCH_AMODE
394unsigned int switch_amode = 0;
395EXPORT_SYMBOL_GPL(switch_amode);
396
397static inline void set_amode_and_uaccess(unsigned long user_amode,
398 unsigned long user32_amode)
399{
400 psw_user_bits = PSW_BASE_BITS | PSW_MASK_DAT | user_amode |
401 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
402 PSW_MASK_PSTATE | PSW_DEFAULT_KEY;
403#ifdef CONFIG_COMPAT
404 psw_user32_bits = PSW_BASE32_BITS | PSW_MASK_DAT | user_amode |
405 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
406 PSW_MASK_PSTATE | PSW_DEFAULT_KEY;
407 psw32_user_bits = PSW32_BASE_BITS | PSW32_MASK_DAT | user32_amode |
408 PSW32_MASK_IO | PSW32_MASK_EXT | PSW32_MASK_MCHECK |
409 PSW32_MASK_PSTATE;
410#endif
411 psw_kernel_bits = PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME |
412 PSW_MASK_MCHECK | PSW_DEFAULT_KEY;
413
414 if (MACHINE_HAS_MVCOS) {
415 printk("mvcos available.\n");
416 memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess));
417 } else {
418 printk("mvcos not available.\n");
419 memcpy(&uaccess, &uaccess_pt, sizeof(uaccess));
420 }
421}
422
423/*
424 * Switch kernel/user addressing modes?
425 */
426static int __init early_parse_switch_amode(char *p)
427{
428 switch_amode = 1;
429 return 0;
430}
431early_param("switch_amode", early_parse_switch_amode);
432
433#else /* CONFIG_S390_SWITCH_AMODE */
434static inline void set_amode_and_uaccess(unsigned long user_amode,
435 unsigned long user32_amode)
436{
437}
438#endif /* CONFIG_S390_SWITCH_AMODE */
439
440#ifdef CONFIG_S390_EXEC_PROTECT
441unsigned int s390_noexec = 0;
442EXPORT_SYMBOL_GPL(s390_noexec);
443
444/*
445 * Enable execute protection?
446 */
447static int __init early_parse_noexec(char *p)
448{
449 if (!strncmp(p, "off", 3))
450 return 0;
451 switch_amode = 1;
452 s390_noexec = 1;
453 return 0;
454}
455early_param("noexec", early_parse_noexec);
456#endif /* CONFIG_S390_EXEC_PROTECT */
457
458static void setup_addressing_mode(void)
459{
460 if (s390_noexec) {
461 printk("S390 execute protection active, ");
462 set_amode_and_uaccess(PSW_ASC_SECONDARY, PSW32_ASC_SECONDARY);
463 return;
464 }
465 if (switch_amode) {
466 printk("S390 address spaces switched, ");
467 set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY);
468 }
469}
470
386static void __init 471static void __init
387setup_lowcore(void) 472setup_lowcore(void)
388{ 473{
@@ -399,19 +484,21 @@ setup_lowcore(void)
399 lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; 484 lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
400 lc->restart_psw.addr = 485 lc->restart_psw.addr =
401 PSW_ADDR_AMODE | (unsigned long) restart_int_handler; 486 PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
402 lc->external_new_psw.mask = PSW_KERNEL_BITS; 487 if (switch_amode)
488 lc->restart_psw.mask |= PSW_ASC_HOME;
489 lc->external_new_psw.mask = psw_kernel_bits;
403 lc->external_new_psw.addr = 490 lc->external_new_psw.addr =
404 PSW_ADDR_AMODE | (unsigned long) ext_int_handler; 491 PSW_ADDR_AMODE | (unsigned long) ext_int_handler;
405 lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT; 492 lc->svc_new_psw.mask = psw_kernel_bits | PSW_MASK_IO | PSW_MASK_EXT;
406 lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call; 493 lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call;
407 lc->program_new_psw.mask = PSW_KERNEL_BITS; 494 lc->program_new_psw.mask = psw_kernel_bits;
408 lc->program_new_psw.addr = 495 lc->program_new_psw.addr =
409 PSW_ADDR_AMODE | (unsigned long)pgm_check_handler; 496 PSW_ADDR_AMODE | (unsigned long)pgm_check_handler;
410 lc->mcck_new_psw.mask = 497 lc->mcck_new_psw.mask =
411 PSW_KERNEL_BITS & ~PSW_MASK_MCHECK & ~PSW_MASK_DAT; 498 psw_kernel_bits & ~PSW_MASK_MCHECK & ~PSW_MASK_DAT;
412 lc->mcck_new_psw.addr = 499 lc->mcck_new_psw.addr =
413 PSW_ADDR_AMODE | (unsigned long) mcck_int_handler; 500 PSW_ADDR_AMODE | (unsigned long) mcck_int_handler;
414 lc->io_new_psw.mask = PSW_KERNEL_BITS; 501 lc->io_new_psw.mask = psw_kernel_bits;
415 lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler; 502 lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler;
416 lc->ipl_device = S390_lowcore.ipl_device; 503 lc->ipl_device = S390_lowcore.ipl_device;
417 lc->jiffy_timer = -1LL; 504 lc->jiffy_timer = -1LL;
@@ -645,6 +732,7 @@ setup_arch(char **cmdline_p)
645 parse_early_param(); 732 parse_early_param();
646 733
647 setup_memory_end(); 734 setup_memory_end();
735 setup_addressing_mode();
648 setup_memory(); 736 setup_memory();
649 setup_resources(); 737 setup_resources();
650 setup_lowcore(); 738 setup_lowcore();