linux-kernel-test/arch/s390/kernel/smp.c
Gerald Schaefer c1821c2e97 [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>
2007-02-05 21:18:17 +01:00

809 lines
19 KiB
C

/*
* arch/s390/kernel/smp.c
*
* Copyright (C) IBM Corp. 1999,2006
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
* Heiko Carstens (heiko.carstens@de.ibm.com)
*
* based on other smp stuff by
* (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
* (c) 1998 Ingo Molnar
*
* We work with logical cpu numbering everywhere we can. The only
* functions using the real cpu address (got from STAP) are the sigp
* functions. For all other functions we use the identity mapping.
* That means that cpu_number_map[i] == i for every cpu. cpu_number_map is
* used e.g. to find the idle task belonging to a logical cpu. Every array
* in the kernel is sorted by the logical cpu number and not by the physical
* one which is causing all the confusion with __cpu_logical_map and
* cpu_number_map in other architectures.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/cache.h>
#include <linux/interrupt.h>
#include <linux/cpu.h>
#include <linux/timex.h>
#include <asm/setup.h>
#include <asm/sigp.h>
#include <asm/pgalloc.h>
#include <asm/irq.h>
#include <asm/s390_ext.h>
#include <asm/cpcmd.h>
#include <asm/tlbflush.h>
#include <asm/timer.h>
extern volatile int __cpu_logical_map[];
/*
* An array with a pointer the lowcore of every CPU.
*/
struct _lowcore *lowcore_ptr[NR_CPUS];
cpumask_t cpu_online_map = CPU_MASK_NONE;
cpumask_t cpu_possible_map = CPU_MASK_NONE;
static struct task_struct *current_set[NR_CPUS];
static void smp_ext_bitcall(int, ec_bit_sig);
static void smp_ext_bitcall_others(ec_bit_sig);
/*
5B * Structure and data for smp_call_function(). This is designed to minimise
* static memory requirements. It also looks cleaner.
*/
static DEFINE_SPINLOCK(call_lock);
struct call_data_struct {
void (*func) (void *info);
void *info;
atomic_t started;
atomic_t finished;
int wait;
};
static struct call_data_struct * call_data;
/*
* 'Call function' interrupt callback
*/
static void do_call_function(void)
{
void (*func) (void *info) = call_data->func;
void *info = call_data->info;
int wait = call_data->wait;
atomic_inc(&call_data->started);
(*func)(info);
if (wait)
atomic_inc(&call_data->finished);
}
/*
* this function sends a 'generic call function' IPI to all other CPUs
* in the system.
*/
int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
int wait)
/*
* [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.
* <nonatomic> currently unused.
* <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.
*/
{
struct call_data_struct data;
int cpus = num_online_cpus()-1;
if (cpus <= 0)
return 0;
/* Can deadlock when called with interrupts disabled */
WARN_ON(irqs_disabled());
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);
call_data = &data;
/* Send a message to all other CPUs and wait for them to respond */
smp_ext_bitcall_others(ec_call_function);
/* Wait for response */
while (atomic_read(&data.started) != cpus)
cpu_relax();
if (wait)
while (atomic_read(&data.finished) != cpus)
cpu_relax();
spin_unlock(&call_lock);
return 0;
}
/*
* Call a function on one CPU
* cpu : the CPU the function should be executed on
*
* You must not call this function with disabled interrupts or from a
* hardware interrupt handler. You may call it from a bottom half.
*
* It is guaranteed that the called function runs on the specified CPU,
* preemption is disabled.
*/
int smp_call_function_on(void (*func) (void *info), void *info,
int nonatomic, int wait, int cpu)
{
struct call_data_struct data;
int curr_cpu;
if (!cpu_online(cpu))
return -EINVAL;
/* disable preemption for local function call */
curr_cpu = get_cpu();
if (curr_cpu == cpu) {
/* direct call to function */
func(info);
put_cpu();
return 0;
}
data.func = func;
data.info = info;
atomic_set(&data.started, 0);
data.wait = wait;
if (wait)
atomic_set(&data.finished, 0);
spin_lock_bh(&call_lock);
call_data = &data;
smp_ext_bitcall(cpu, ec_call_function);
/* Wait for response */
while (atomic_read(&data.started) != 1)
cpu_relax();
if (wait)
while (atomic_read(&data.finished) != 1)
cpu_relax();
spin_unlock_bh(&call_lock);
put_cpu();
return 0;
}
EXPORT_SYMBOL(smp_call_function_on);
static inline void do_send_stop(void)
{
int cpu, rc;
/* stop all processors */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
do {
rc = signal_processor(cpu, sigp_stop);
} while (rc == sigp_busy);
}
}
static inline void do_store_status(void)
{
int cpu, rc;
/* store status of all processors in their lowcores (real 0) */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
do {
rc = signal_processor_p(
(__u32)(unsigned long) lowcore_ptr[cpu], cpu,
sigp_store_status_at_address);
} while(rc == sigp_busy);
}
}
static inline void do_wait_for_stop(void)
{
int cpu;
/* Wait for all other cpus to enter stopped state */
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
while(!smp_cpu_not_running(cpu))
cpu_relax();
}
}
/*
* this function sends a 'stop' sigp to all other CPUs in the system.
* it goes straight through.
*/
void smp_send_stop(void)
{
/* Disable all interrupts/machine checks */
__load_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK);
/* write magic number to zero page (absolute 0) */
lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC;
/* stop other processors. */
do_send_stop();
/* wait until other processors are stopped */
do_wait_for_stop();
/* store status of other processors. */
do_store_status();
}
/*
* Reboot, halt and power_off routines for SMP.
*/
void machine_restart_smp(char * __unused)
{
smp_send_stop();
do_reipl();
}
void machine_halt_smp(void)
{
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
__cpcmd(vmhalt_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
for (;;);
}
void machine_power_off_smp(void)
{
smp_send_stop();
if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
__cpcmd(vmpoff_cmd, NULL, 0, NULL);
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
for (;;);
}
/*
* This is the main routine where commands issued by other
* cpus are handled.
*/
static void do_ext_call_interrupt(__u16 code)
{
unsigned long bits;
/*
* handle bit signal external calls
*
* For the ec_schedule signal we have to do nothing. All the work
* is done automatically when we return from the interrupt.
*/
bits = xchg(&S390_lowcore.ext_call_fast, 0);
if (test_bit(ec_call_function, &bits))
do_call_function();
}
/*
* Send an external call sigp to another cpu and return without waiting
* for its completion.
*/
static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
{
/*
* Set signaling bit in lowcore of target cpu and kick it
*/
set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast);
while(signal_processor(cpu, sigp_emergency_signal) == sigp_busy)
udelay(10);
}
/*
* Send an external call sigp to every other cpu in the system and
* return without waiting for its completion.
*/
static void smp_ext_bitcall_others(ec_bit_sig sig)
{
int cpu;
for_each_online_cpu(cpu) {
if (cpu == smp_processor_id())
continue;
/*
* Set signaling bit in lowcore of target cpu and kick it
*/
set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast);
while (signal_processor(cpu, sigp_emergency_signal) == sigp_busy)
udelay(10);
}
}
#ifndef CONFIG_64BIT
/*
* this function sends a 'purge tlb' signal to another CPU.
*/
void smp_ptlb_callback(void *info)
{
local_flush_tlb();
}
void smp_ptlb_all(void)
{
on_each_cpu(smp_ptlb_callback, NULL, 0, 1);
}
EXPORT_SYMBOL(smp_ptlb_all);
#endif /* ! CONFIG_64BIT */
/*
* this function sends a 'reschedule' IPI to another CPU.
* it goes straight through and wastes no time serializing
* anything. Worst case is that we lose a reschedule ...
*/
void smp_send_reschedule(int cpu)
{
smp_ext_bitcall(cpu, ec_schedule);
}
/*
* parameter area for the set/clear control bit callbacks
*/
struct ec_creg_mask_parms {
unsigned long orvals[16];
unsigned long andvals[16];
};
/*
* callback for setting/clearing control bits
*/
static void smp_ctl_bit_callback(void *info) {
struct ec_creg_mask_parms *pp = info;
unsigned long cregs[16];
int i;
__ctl_store(cregs, 0, 15);
for (i = 0; i <= 15; i++)
cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i];
__ctl_load(cregs, 0, 15);
}
/*
* Set a bit in a control register of all cpus
*/
void smp_ctl_set_bit(int cr, int bit)
{
struct ec_creg_mask_parms parms;
memset(&parms.orvals, 0, sizeof(parms.orvals));
memset(&parms.andvals, 0xff, sizeof(parms.andvals));
parms.orvals[cr] = 1 << bit;
on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
}
/*
* Clear a bit in a control register of all cpus
*/
void smp_ctl_clear_bit(int cr, int bit)
{
struct ec_creg_mask_parms parms;
memset(&parms.orvals, 0, sizeof(parms.orvals));
memset(&parms.andvals, 0xff, sizeof(parms.andvals));
parms.andvals[cr] = ~(1L << bit);
on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
}
/*
* Lets check how many CPUs we have.
*/
static unsigned int
__init smp_count_cpus(void)
{
unsigned int cpu, num_cpus;
__u16 boot_cpu_addr;
/*
* cpu 0 is the boot cpu. See smp_prepare_boot_cpu.
*/
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
current_thread_info()->cpu = 0;
num_cpus = 1;
for (cpu = 0; cpu <= 65535; cpu++) {
if ((__u16) cpu == boot_cpu_addr)
continue;
__cpu_logical_map[1] = (__u16) cpu;
if (signal_processor(1, sigp_sense) ==
sigp_not_operational)
continue;
num_cpus++;
}
printk("Detected %d CPU's\n",(int) num_cpus);
printk("Boot cpu address %2X\n", boot_cpu_addr);
return num_cpus;
}
/*
* Activate a secondary processor.
*/
int __devinit start_secondary(void *cpuvoid)
{
/* Setup the cpu */
cpu_init();
preempt_disable();
/* init per CPU timer */
init_cpu_timer();
#ifdef CONFIG_VIRT_TIMER
init_cpu_vtimer();
#endif
/* Enable pfault pseudo page faults on this cpu. */
pfault_init();
/* Mark this cpu as online */
cpu_set(smp_processor_id(), cpu_online_map);
/* Switch on interrupts */
local_irq_enable();
/* Print info about this processor */
print_cpu_info(&S390_lowcore.cpu_data);
/* cpu_idle will call schedule for us */
cpu_idle();
return 0;
}
static void __init smp_create_idle(unsigned int cpu)
{
struct task_struct *p;
/*
* don't care about the psw and regs settings since we'll never
* reschedule the forked task.
*/
p = fork_idle(cpu);
if (IS_ERR(p))
panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
current_set[cpu] = p;
}
/* Reserving and releasing of CPUs */
static DEFINE_SPINLOCK(smp_reserve_lock);
static int smp_cpu_reserved[NR_CPUS];
int
smp_get_cpu(cpumask_t cpu_mask)
{
unsigned long flags;
int cpu;
spin_lock_irqsave(&smp_reserve_lock, flags);
/* Try to find an already reserved cpu. */
for_each_cpu_mask(cpu, cpu_mask) {
if (smp_cpu_reserved[cpu] != 0) {
smp_cpu_reserved[cpu]++;
/* Found one. */
goto out;
}
}
/* Reserve a new cpu from cpu_mask. */
for_each_cpu_mask(cpu, cpu_mask) {
if (cpu_online(cpu)) {
smp_cpu_reserved[cpu]++;
goto out;
}
}
cpu = -ENODEV;
out:
spin_unlock_irqrestore(&smp_reserve_lock, flags);
return cpu;
}
void
smp_put_cpu(int cpu)
{
unsigned long flags;
spin_lock_irqsave(&smp_reserve_lock, flags);
smp_cpu_reserved[cpu]--;
spin_unlock_irqrestore(&smp_reserve_lock, flags);
}
static inline int
cpu_stopped(int cpu)
{
__u32 status;
/* Check for stopped state */
if (signal_processor_ps(&status, 0, cpu, sigp_sense) == sigp_status_stored) {
if (status & 0x40)
return 1;
}
return 0;
}
/* Upping and downing of CPUs */
int
__cpu_up(unsigned int cpu)
{
struct task_struct *idle;
struct _lowcore *cpu_lowcore;
struct stack_frame *sf;
sigp_ccode ccode;
int curr_cpu;
for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) {
__cpu_logical_map[cpu] = (__u16) curr_cpu;
if (cpu_stopped(cpu))
break;
}
if (!cpu_stopped(cpu))
return -ENODEV;
ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
cpu, sigp_set_prefix);
if (ccode){
printk("sigp_set_prefix failed for cpu %d "
"with condition code %d\n",
(int) cpu, (int) ccode);
return -EIO;
}
idle = current_set[cpu];
cpu_lowcore = lowcore_ptr[cpu];
cpu_lowcore->kernel_stack = (unsigned long)
task_stack_page(idle) + (THREAD_SIZE);
sf = (struct stack_frame *) (cpu_lowcore->kernel_stack
- sizeof(struct pt_regs)
- sizeof(struct stack_frame));
memset(sf, 0, sizeof(struct stack_frame));
sf->gprs[9] = (unsigned long) sf;
cpu_lowcore->save_area[15] = (unsigned long) sf;
__ctl_store(cpu_lowcore->cregs_save_area[0], 0, 15);
asm volatile(
" stam 0,15,0(%0)"
: : "a" (&cpu_lowcore->access_regs_save_area) : "memory");
cpu_lowcore->percpu_offset = __per_cpu_offset[cpu];
cpu_lowcore->current_task = (unsigned long) idle;
cpu_lowcore->cpu_data.cpu_nr = cpu;
eieio();
while (signal_processor(cpu,sigp_restart) == sigp_busy)
udelay(10);
while (!cpu_online(cpu))
cpu_relax();
return 0;
}
static unsigned int __initdata additional_cpus;
static unsigned int __initdata possible_cpus;
void __init smp_setup_cpu_possible_map(void)
{
unsigned int phy_cpus, pos_cpus, cpu;
phy_cpus = smp_count_cpus();
pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
if (possible_cpus)
pos_cpus = min(possible_cpus, (unsigned int) NR_CPUS);
for (cpu = 0; cpu < pos_cpus; cpu++)
cpu_set(cpu, cpu_possible_map);
phy_cpus = min(phy_cpus, pos_cpus);
for (cpu = 0; cpu < phy_cpus; cpu++)
cpu_set(cpu, cpu_present_map);
}
#ifdef CONFIG_HOTPLUG_CPU
static int __init setup_additional_cpus(char *s)
{
additional_cpus = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("additional_cpus", setup_additional_cpus);
static int __init setup_possible_cpus(char *s)
{
possible_cpus = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("possible_cpus", setup_possible_cpus);
int
__cpu_disable(void)
{
unsigned long flags;
struct ec_creg_mask_parms cr_parms;
int cpu = smp_processor_id();
spin_lock_irqsave(&smp_reserve_lock, flags);
if (smp_cpu_reserved[cpu] != 0) {
spin_unlock_irqrestore(&smp_reserve_lock, flags);
return -EBUSY;
}
cpu_clear(cpu, cpu_online_map);
/* Disable pfault pseudo page faults on this cpu. */
pfault_fini();
memset(&cr_parms.orvals, 0, sizeof(cr_parms.orvals));
memset(&cr_parms.andvals, 0xff, sizeof(cr_parms.andvals));
/* disable all external interrupts */
cr_parms.orvals[0] = 0;
cr_parms.andvals[0] = ~(1<<15 | 1<<14 | 1<<13 | 1<<12 |
1<<11 | 1<<10 | 1<< 6 | 1<< 4);
/* disable all I/O interrupts */
cr_parms.orvals[6] = 0;
cr_parms.andvals[6] = ~(1<<31 | 1<<30 | 1<<29 | 1<<28 |
1<<27 | 1<<26 | 1<<25 | 1<<24);
/* disable most machine checks */
cr_parms.orvals[14] = 0;
cr_parms.andvals[14] = ~(1<<28 | 1<<27 | 1<<26 | 1<<25 | 1<<24);
smp_ctl_bit_callback(&cr_parms);
spin_unlock_irqrestore(&smp_reserve_lock, flags);
return 0;
}
void
__cpu_die(unsigned int cpu)
{
/* Wait until target cpu is down */
while (!smp_cpu_not_running(cpu))
cpu_relax();
printk("Processor %d spun down\n", cpu);
}
void
cpu_die(void)
{
idle_task_exit();
signal_processor(smp_processor_id(), sigp_stop);
BUG();
for(;;);
}
#endif /* CONFIG_HOTPLUG_CPU */
/*
* Cycle through the processors and setup structures.
*/
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned long stack;
unsigned int cpu;
int i;
/* request the 0x1201 emergency signal external interrupt */
if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
panic("Couldn't request external interrupt 0x1201");
memset(lowcore_ptr,0,sizeof(lowcore_ptr));
/*
* Initialize prefix pages and stacks for all possible cpus
*/
print_cpu_info(&S390_lowcore.cpu_data);
for_each_possible_cpu(i) {
lowcore_ptr[i] = (struct _lowcore *)
__get_free_pages(GFP_KERNEL|GFP_DMA,
sizeof(void*) == 8 ? 1 : 0);
stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER);
if (lowcore_ptr[i] == NULL || stack == 0ULL)
panic("smp_boot_cpus failed to allocate memory\n");
*(lowcore_ptr[i]) = S390_lowcore;
lowcore_ptr[i]->async_stack = stack + (ASYNC_SIZE);
stack = __get_free_pages(GFP_KERNEL,0);
if (stack == 0ULL)
panic("smp_boot_cpus failed to allocate memory\n");
lowcore_ptr[i]->panic_stack = stack + (PAGE_SIZE);
#ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE) {
lowcore_ptr[i]->extended_save_area_addr =
(__u32) __get_free_pages(GFP_KERNEL,0);
if (lowcore_ptr[i]->extended_save_area_addr == 0)
panic("smp_boot_cpus failed to "
"allocate memory\n");
}
#endif
}
#ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE)
ctl_set_bit(14, 29); /* enable extended save area */
#endif
set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]);
for_each_possible_cpu(cpu)
if (cpu != smp_processor_id())
smp_create_idle(cpu);
}
void __devinit smp_prepare_boot_cpu(void)
{
BUG_ON(smp_processor_id() != 0);
cpu_set(0, cpu_online_map);
S390_lowcore.percpu_offset = __per_cpu_offset[0];
current_set[0] = current;
}
void smp_cpus_done(unsigned int max_cpus)
{
cpu_present_map = cpu_possible_map;
}
/*
* the frequency of the profiling timer can be changed
* by writing a multiplier value into /proc/profile.
*
* usually you want to run this on all CPUs ;)
*/
int setup_profiling_timer(unsigned int multiplier)
{
return 0;
}
static DEFINE_PER_CPU(struct cpu, cpu_devices);
static int __init topology_init(void)
{
int cpu;
int ret;
for_each_possible_cpu(cpu) {
struct cpu *c = &per_cpu(cpu_devices, cpu);
c->hotpluggable = 1;
ret = register_cpu(c, cpu);
if (ret)
printk(KERN_WARNING "topology_init: register_cpu %d "
"failed (%d)\n", cpu, ret);
}
return 0;
}
subsys_initcall(topology_init);
EXPORT_SYMBOL(cpu_online_map);
EXPORT_SYMBOL(cpu_possible_map);
EXPORT_SYMBOL(lowcore_ptr);
EXPORT_SYMBOL(smp_ctl_set_bit);
EXPORT_SYMBOL(smp_ctl_clear_bit);
EXPORT_SYMBOL(smp_call_function);
EXPORT_SYMBOL(smp_get_cpu);
EXPORT_SYMBOL(smp_put_cpu);