Commit a489d159 authored by Linus Torvalds's avatar Linus Torvalds

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: (44 commits)
  [S390] hypfs crashes with invalid mount option.
  [S390] cio: subchannel evaluation function operates without lock
  [S390] cio: always query all paths on path verification.
  [S390] cio: update path groups on logical CHPID changes.
  [S390] cio: subchannels in no-path state.
  [S390] Replace nopav-message on VM.
  [S390] set modalias for ccw bus uevents.
  [S390] Get rid of DBG macro.
  [S390] Use alternative user-copy operations for new hardware.
  [S390] Make user-copy operations run-time configurable.
  [S390] Cleanup in signal handling code.
  [S390] Cleanup in page table related code.
  [S390] Linux API for writing z/VM APPLDATA Monitor records.
  [S390] xpram off by one error.
  [S390] Remove kexec experimental flag.
  [S390] cleanup appldata.
  [S390] fix typo in vmcp.
  [S390] Kernel stack overflow handling.
  [S390] qdio slsb processing state.
  [S390] Missing initialization in common i/o layer.
  ...
parents a48178a2 388c571c
...@@ -2452,6 +2452,8 @@ S: Maintained ...@@ -2452,6 +2452,8 @@ S: Maintained
S390 S390
P: Martin Schwidefsky P: Martin Schwidefsky
M: schwidefsky@de.ibm.com M: schwidefsky@de.ibm.com
P: Heiko Carstens
M: heiko.carstens@de.ibm.com
M: linux390@de.ibm.com M: linux390@de.ibm.com
L: linux-390@vm.marist.edu L: linux-390@vm.marist.edu
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
......
...@@ -460,8 +460,7 @@ config S390_HYPFS_FS ...@@ -460,8 +460,7 @@ config S390_HYPFS_FS
information in an s390 hypervisor environment. information in an s390 hypervisor environment.
config KEXEC config KEXEC
bool "kexec system call (EXPERIMENTAL)" bool "kexec system call"
depends on EXPERIMENTAL
help help
kexec is a system call that implements the ability to shutdown your kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot current kernel, and to start another kernel. It is like a reboot
...@@ -487,8 +486,22 @@ source "drivers/net/Kconfig" ...@@ -487,8 +486,22 @@ source "drivers/net/Kconfig"
source "fs/Kconfig" source "fs/Kconfig"
menu "Instrumentation Support"
source "arch/s390/oprofile/Kconfig" source "arch/s390/oprofile/Kconfig"
config KPROBES
bool "Kprobes (EXPERIMENTAL)"
depends on EXPERIMENTAL && MODULES
help
Kprobes allows you to trap at almost any kernel address and
execute a callback function. register_kprobe() establishes
a probepoint and specifies the callback. Kprobes is useful
for kernel debugging, non-intrusive instrumentation and testing.
If in doubt, say "N".
endmenu
source "arch/s390/Kconfig.debug" source "arch/s390/Kconfig.debug"
source "security/Kconfig" source "security/Kconfig"
......
...@@ -29,22 +29,6 @@ ...@@ -29,22 +29,6 @@
#define CTL_APPLDATA_NET_SUM 2125 #define CTL_APPLDATA_NET_SUM 2125
#define CTL_APPLDATA_PROC 2126 #define CTL_APPLDATA_PROC 2126
#ifndef CONFIG_64BIT
#define APPLDATA_START_INTERVAL_REC 0x00 /* Function codes for */
#define APPLDATA_STOP_REC 0x01 /* DIAG 0xDC */
#define APPLDATA_GEN_EVENT_RECORD 0x02
#define APPLDATA_START_CONFIG_REC 0x03
#else
#define APPLDATA_START_INTERVAL_REC 0x80
#define APPLDATA_STOP_REC 0x81
#define APPLDATA_GEN_EVENT_RECORD 0x82
#define APPLDATA_START_CONFIG_REC 0x83
#endif /* CONFIG_64BIT */
#define P_INFO(x...) printk(KERN_INFO MY_PRINT_NAME " info: " x) #define P_INFO(x...) printk(KERN_INFO MY_PRINT_NAME " info: " x)
#define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x) #define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x)
#define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x) #define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x)
......
...@@ -14,20 +14,20 @@ ...@@ -14,20 +14,20 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/page-flags.h> #include <linux/page-flags.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <asm/timer.h>
//#include <linux/kernel_stat.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <asm/appldata.h>
#include <asm/timer.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/smp.h>
#include "appldata.h" #include "appldata.h"
...@@ -39,34 +39,6 @@ ...@@ -39,34 +39,6 @@
#define TOD_MICRO 0x01000 /* nr. of TOD clock units #define TOD_MICRO 0x01000 /* nr. of TOD clock units
for 1 microsecond */ for 1 microsecond */
/*
* Parameter list for DIAGNOSE X'DC'
*/
#ifndef CONFIG_64BIT
struct appldata_parameter_list {
u16 diag; /* The DIAGNOSE code X'00DC' */
u8 function; /* The function code for the DIAGNOSE */
u8 parlist_length; /* Length of the parameter list */
u32 product_id_addr; /* Address of the 16-byte product ID */
u16 reserved;
u16 buffer_length; /* Length of the application data buffer */
u32 buffer_addr; /* Address of the application data buffer */
};
#else
struct appldata_parameter_list {
u16 diag;
u8 function;
u8 parlist_length;
u32 unused01;
u16 reserved;
u16 buffer_length;
u32 unused02;
u64 product_id_addr;
u64 buffer_addr;
};
#endif /* CONFIG_64BIT */
/* /*
* /proc entries (sysctl) * /proc entries (sysctl)
*/ */
...@@ -181,46 +153,17 @@ static void appldata_work_fn(void *data) ...@@ -181,46 +153,17 @@ static void appldata_work_fn(void *data)
int appldata_diag(char record_nr, u16 function, unsigned long buffer, int appldata_diag(char record_nr, u16 function, unsigned long buffer,
u16 length, char *mod_lvl) u16 length, char *mod_lvl)
{ {
unsigned long ry; struct appldata_product_id id = {
struct appldata_product_id {
char prod_nr[7]; /* product nr. */
char prod_fn[2]; /* product function */
char record_nr; /* record nr. */
char version_nr[2]; /* version */
char release_nr[2]; /* release */
char mod_lvl[2]; /* modification lvl. */
} appldata_product_id = {
/* all strings are EBCDIC, record_nr is byte */
.prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */
.prod_fn = {0xD5, 0xD3}, /* "NL" */ .prod_fn = 0xD5D3, /* "NL" */
.record_nr = record_nr, .record_nr = record_nr,
.version_nr = {0xF2, 0xF6}, /* "26" */ .version_nr = 0xF2F6, /* "26" */
.release_nr = {0xF0, 0xF1}, /* "01" */ .release_nr = 0xF0F1, /* "01" */
.mod_lvl = {mod_lvl[0], mod_lvl[1]}, .mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1],
};
struct appldata_parameter_list appldata_parameter_list = {
.diag = 0xDC,
.function = function,
.parlist_length =
sizeof(appldata_parameter_list),
.buffer_length = length,
.product_id_addr =
(unsigned long) &appldata_product_id,
.buffer_addr = virt_to_phys((void *) buffer)
}; };
if (!MACHINE_IS_VM) return appldata_asm(&id, function, (void *) buffer, length);
return -ENOSYS;
ry = -1;
asm volatile(
"diag %1,%0,0xDC\n\t"
: "=d" (ry)
: "d" (&appldata_parameter_list),
"m" (appldata_parameter_list),
"m" (appldata_product_id)
: "cc");
return (int) ry;
} }
/************************ timer, work, DIAG <END> ****************************/ /************************ timer, work, DIAG <END> ****************************/
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/appldata.h>
#include <asm/smp.h> #include <asm/smp.h>
#include "appldata.h" #include "appldata.h"
......
...@@ -428,6 +428,7 @@ CONFIG_S390_TAPE_34XX=m ...@@ -428,6 +428,7 @@ CONFIG_S390_TAPE_34XX=m
# CONFIG_VMLOGRDR is not set # CONFIG_VMLOGRDR is not set
# CONFIG_VMCP is not set # CONFIG_VMCP is not set
# CONFIG_MONREADER is not set # CONFIG_MONREADER is not set
CONFIG_MONWRITER=m
# #
# Cryptographic devices # Cryptographic devices
......
/* /*
* fs/hypfs/hypfs.h * arch/s390/hypfs/hypfs.h
* Hypervisor filesystem for Linux on s390. * Hypervisor filesystem for Linux on s390.
* *
* Copyright (C) IBM Corp. 2006 * Copyright (C) IBM Corp. 2006
......
/* /*
* fs/hypfs/hypfs_diag.c * arch/s390/hypfs/hypfs_diag.c
* Hypervisor filesystem for Linux on s390. Diag 204 and 224 * Hypervisor filesystem for Linux on s390. Diag 204 and 224
* implementation. * implementation.
* *
...@@ -432,12 +432,14 @@ static int diag204_probe(void) ...@@ -432,12 +432,14 @@ static int diag204_probe(void)
buf = diag204_get_buffer(INFO_EXT, &pages); buf = diag204_get_buffer(INFO_EXT, &pages);
if (!IS_ERR(buf)) { if (!IS_ERR(buf)) {
if (diag204(SUBC_STIB7 | INFO_EXT, pages, buf) >= 0) { if (diag204((unsigned long)SUBC_STIB7 |
(unsigned long)INFO_EXT, pages, buf) >= 0) {
diag204_store_sc = SUBC_STIB7; diag204_store_sc = SUBC_STIB7;
diag204_info_type = INFO_EXT; diag204_info_type = INFO_EXT;
goto out; goto out;
} }
if (diag204(SUBC_STIB6 | INFO_EXT, pages, buf) >= 0) { if (diag204((unsigned long)SUBC_STIB6 |
(unsigned long)INFO_EXT, pages, buf) >= 0) {
diag204_store_sc = SUBC_STIB7; diag204_store_sc = SUBC_STIB7;
diag204_info_type = INFO_EXT; diag204_info_type = INFO_EXT;
goto out; goto out;
...@@ -452,7 +454,8 @@ static int diag204_probe(void) ...@@ -452,7 +454,8 @@ static int diag204_probe(void)
rc = PTR_ERR(buf); rc = PTR_ERR(buf);
goto fail_alloc; goto fail_alloc;
} }
if (diag204(SUBC_STIB4 | INFO_SIMPLE, pages, buf) >= 0) { if (diag204((unsigned long)SUBC_STIB4 |
(unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
diag204_store_sc = SUBC_STIB4; diag204_store_sc = SUBC_STIB4;
diag204_info_type = INFO_SIMPLE; diag204_info_type = INFO_SIMPLE;
goto out; goto out;
...@@ -476,7 +479,8 @@ static void *diag204_store(void) ...@@ -476,7 +479,8 @@ static void *diag204_store(void)
buf = diag204_get_buffer(diag204_info_type, &pages); buf = diag204_get_buffer(diag204_info_type, &pages);
if (IS_ERR(buf)) if (IS_ERR(buf))
goto out; goto out;
if (diag204(diag204_store_sc | diag204_info_type, pages, buf) < 0) if (diag204((unsigned long)diag204_store_sc |
(unsigned long)diag204_info_type, pages, buf) < 0)
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOSYS);
out: out:
return buf; return buf;
...@@ -531,7 +535,7 @@ __init int hypfs_diag_init(void) ...@@ -531,7 +535,7 @@ __init int hypfs_diag_init(void)
return rc; return rc;
} }
__exit void hypfs_diag_exit(void) void hypfs_diag_exit(void)
{ {
diag224_delete_name_table(); diag224_delete_name_table();
diag204_free_buffer(); diag204_free_buffer();
......
/* /*
* fs/hypfs/hypfs_diag.h * arch/s390/hypfs_diag.h
* Hypervisor filesystem for Linux on s390. * Hypervisor filesystem for Linux on s390.
* *
* Copyright (C) IBM Corp. 2006 * Copyright (C) IBM Corp. 2006
......
/* /*
* fs/hypfs/inode.c * arch/s390/hypfs/inode.c
* Hypervisor filesystem for Linux on s390. * Hypervisor filesystem for Linux on s390.
* *
* Copyright (C) IBM Corp. 2006 * Copyright (C) IBM Corp. 2006
...@@ -312,10 +312,12 @@ static void hypfs_kill_super(struct super_block *sb) ...@@ -312,10 +312,12 @@ static void hypfs_kill_super(struct super_block *sb)
{ {
struct hypfs_sb_info *sb_info = sb->s_fs_info; struct hypfs_sb_info *sb_info = sb->s_fs_info;
if (sb->s_root) {
hypfs_delete_tree(sb->s_root); hypfs_delete_tree(sb->s_root);
hypfs_remove(sb_info->update_file); hypfs_remove(sb_info->update_file);
kfree(sb->s_fs_info); kfree(sb->s_fs_info);
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
}
kill_litter_super(sb); kill_litter_super(sb);
} }
......
...@@ -6,7 +6,7 @@ EXTRA_AFLAGS := -traditional ...@@ -6,7 +6,7 @@ EXTRA_AFLAGS := -traditional
obj-y := bitmap.o traps.o time.o process.o \ obj-y := bitmap.o traps.o time.o process.o \
setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
semaphore.o s390_ext.o debug.o profile.o irq.o reipl_diag.o semaphore.o s390_ext.o debug.o profile.o irq.o ipl.o
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
...@@ -24,6 +24,7 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ ...@@ -24,6 +24,7 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
obj-$(CONFIG_VIRT_TIMER) += vtime.o obj-$(CONFIG_VIRT_TIMER) += vtime.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_KPROBES) += kprobes.o
# Kexec part # Kexec part
S390_KEXEC_OBJS := machine_kexec.o crash.o S390_KEXEC_OBJS := machine_kexec.o crash.o
......
...@@ -505,6 +505,8 @@ pgm_no_vtime2: ...@@ -505,6 +505,8 @@ pgm_no_vtime2:
mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
tm SP_PSW+1(%r15),0x01 # kernel per event ?
bz BASED(kernel_per)
l %r3,__LC_PGM_ILC # load program interruption code l %r3,__LC_PGM_ILC # load program interruption code
la %r8,0x7f la %r8,0x7f
nr %r8,%r3 # clear per-event-bit and ilc nr %r8,%r3 # clear per-event-bit and ilc
...@@ -536,6 +538,16 @@ pgm_no_vtime3: ...@@ -536,6 +538,16 @@ pgm_no_vtime3:
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
b BASED(sysc_do_svc) b BASED(sysc_do_svc)
#
# per was called from kernel, must be kprobes
#
kernel_per:
mvi SP_TRAP+1(%r15),0x28 # set trap indication to pgm check
la %r2,SP_PTREGS(%r15) # address of register-save area
l %r1,BASED(.Lhandle_per) # load adr. of per handler
la %r14,BASED(sysc_leave) # load adr. of system return
br %r1 # branch to do_single_step
/* /*
* IO interrupt handler routine * IO interrupt handler routine
*/ */
......
...@@ -518,6 +518,8 @@ pgm_no_vtime2: ...@@ -518,6 +518,8 @@ pgm_no_vtime2:
#endif #endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
lg %r1,__TI_task(%r9) lg %r1,__TI_task(%r9)
tm SP_PSW+1(%r15),0x01 # kernel per event ?
jz kernel_per
mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID
mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
...@@ -553,6 +555,16 @@ pgm_no_vtime3: ...@@ -553,6 +555,16 @@ pgm_no_vtime3:
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
j sysc_do_svc j sysc_do_svc
#
# per was called from kernel, must be kprobes
#
kernel_per:
lhi %r0,__LC_PGM_OLD_PSW
sth %r0,SP_TRAP(%r15) # set trap indication to pgm check
la %r2,SP_PTREGS(%r15) # address of register-save area
larl %r14,sysc_leave # load adr. of system ret, no work
jg do_single_step # branch to do_single_step
/* /*
* IO interrupt handler routine * IO interrupt handler routine
*/ */
...@@ -815,7 +827,7 @@ restart_go: ...@@ -815,7 +827,7 @@ restart_go:
*/ */
stack_overflow: stack_overflow:
lg %r15,__LC_PANIC_STACK # change to panic stack lg %r15,__LC_PANIC_STACK # change to panic stack
aghi %r1,-SP_SIZE aghi %r15,-SP_SIZE
mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack
stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack
la %r1,__LC_SAVE_AREA la %r1,__LC_SAVE_AREA
...@@ -823,7 +835,7 @@ stack_overflow: ...@@ -823,7 +835,7 @@ stack_overflow:
je 0f je 0f
chi %r12,__LC_PGM_OLD_PSW chi %r12,__LC_PGM_OLD_PSW
je 0f je 0f
la %r1,__LC_SAVE_AREA+16 la %r1,__LC_SAVE_AREA+32
0: mvc SP_R12(32,%r15),0(%r1) # move %r12-%r15 to stack 0: mvc SP_R12(32,%r15),0(%r1) # move %r12-%r15 to stack
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # clear back chain xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # clear back chain
la %r2,SP_PTREGS(%r15) # load pt_regs la %r2,SP_PTREGS(%r15) # load pt_regs
......
...@@ -272,7 +272,7 @@ iplstart: ...@@ -272,7 +272,7 @@ iplstart:
# load parameter file from ipl device # load parameter file from ipl device
# #
.Lagain1: .Lagain1:
l %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # ramdisk loc. is temp l %r2,.Linitrd # ramdisk loc. is temp
bas %r14,.Lloader # load parameter file bas %r14,.Lloader # load parameter file
ltr %r2,%r2 # got anything ? ltr %r2,%r2 # got anything ?
bz .Lnopf bz .Lnopf
...@@ -280,7 +280,7 @@ iplstart: ...@@ -280,7 +280,7 @@ iplstart:
bnh .Lnotrunc bnh .Lnotrunc
la %r2,895 la %r2,895
.Lnotrunc: .Lnotrunc:
l %r4,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) l %r4,.Linitrd
clc 0(3,%r4),.L_hdr # if it is HDRx clc 0(3,%r4),.L_hdr # if it is HDRx
bz .Lagain1 # skip dataset header bz .Lagain1 # skip dataset header
clc 0(3,%r4),.L_eof # if it is EOFx clc 0(3,%r4),.L_eof # if it is EOFx
...@@ -323,14 +323,15 @@ iplstart: ...@@ -323,14 +323,15 @@ iplstart:
# load ramdisk from ipl device # load ramdisk from ipl device
# #
.Lagain2: .Lagain2:
l %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # addr of ramdisk l %r2,.Linitrd # addr of ramdisk
st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
bas %r14,.Lloader # load ramdisk bas %r14,.Lloader # load ramdisk
st %r2,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r12) # store size of ramdisk st %r2,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r12) # store size of ramdisk
ltr %r2,%r2 ltr %r2,%r2
bnz .Lrdcont bnz .Lrdcont
st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # no ramdisk found st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # no ramdisk found
.Lrdcont: .Lrdcont:
l %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) l %r2,.Linitrd
clc 0(3,%r2),.L_hdr # skip HDRx and EOFx clc 0(3,%r2),.L_hdr # skip HDRx and EOFx
bz .Lagain2 bz .Lagain2
...@@ -379,6 +380,7 @@ iplstart: ...@@ -379,6 +380,7 @@ iplstart:
l %r1,.Lstartup l %r1,.Lstartup
br %r1 br %r1
.Linitrd:.long _end + 0x400000 # default address of initrd
.Lparm: .long PARMAREA .Lparm: .long PARMAREA
.Lstartup: .long startup .Lstartup: .long startup
.Lcvtab:.long _ebcasc # ebcdic to ascii table .Lcvtab:.long _ebcasc # ebcdic to ascii table
...@@ -479,65 +481,6 @@ start: ...@@ -479,65 +481,6 @@ start:
.byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7 .byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7
.byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff .byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
.macro GET_IPL_DEVICE
.Lget_ipl_device:
l %r1,0xb8 # get sid
sll %r1,15 # test if subchannel is enabled
srl %r1,31
ltr %r1,%r1
bz 2f-.LPG1(%r13) # subchannel disabled
l %r1,0xb8
la %r5,.Lipl_schib-.LPG1(%r13)
stsch 0(%r5) # get schib of subchannel
bnz 2f-.LPG1(%r13) # schib not available
tm 5(%r5),0x01 # devno valid?
bno 2f-.LPG1(%r13)
la %r6,ipl_parameter_flags-.LPG1(%r13)
oi 3(%r6),0x01 # set flag
la %r2,ipl_devno-.LPG1(%r13)
mvc 0(2,%r2),6(%r5) # store devno
tm 4(%r5),0x80 # qdio capable device?
bno 2f-.LPG1(%r13)
oi 3(%r6),0x02 # set flag
# copy ipl parameters
lhi %r0,4096
l %r2,20(%r0) # get address of parameter list
lhi %r3,IPL_PARMBLOCK_ORIGIN
st %r3,20(%r0)
lhi %r4,1
cr %r2,%r3 # start parameters < destination ?
jl 0f
lhi %r1,1 # copy direction is upwards
j 1f
0: lhi %r1,-1 # copy direction is downwards
ar %r2,%r0
ar %r3,%r0
ar %r2,%r1
ar %r3,%r1
1: mvc 0(1,%r3),0(%r2) # finally copy ipl parameters
ar %r3,%r1
ar %r2,%r1
sr %r0,%r4
jne 1b
b 2f-.LPG1(%r13)
.align 4
.Lipl_schib:
.rept 13
.long 0
.endr
.globl ipl_parameter_flags
ipl_parameter_flags:
.long 0
.globl ipl_devno
ipl_devno:
.word 0
2:
.endm
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#include "head64.S" #include "head64.S"
#else #else
......
...@@ -26,8 +26,8 @@ startup:basr %r13,0 # get base ...@@ -26,8 +26,8 @@ startup:basr %r13,0 # get base
# #
.org PARMAREA .org PARMAREA
.long 0,0 # IPL_DEVICE .long 0,0 # IPL_DEVICE
.long 0,RAMDISK_ORIGIN # INITRD_START .long 0,0 # INITRD_START
.long 0,RAMDISK_SIZE # INITRD_SIZE .long 0,0 # INITRD_SIZE
.org COMMAND_LINE .org COMMAND_LINE
.byte "root=/dev/ram0 ro" .byte "root=/dev/ram0 ro"
...@@ -37,12 +37,23 @@ startup:basr %r13,0 # get base ...@@ -37,12 +37,23 @@ startup:basr %r13,0 # get base
startup_continue: startup_continue:
basr %r13,0 # get base basr %r13,0 # get base
.LPG1: GET_IPL_DEVICE .LPG1: mvi __LC_AR_MODE_ID,0 # set ESA flag (mode 0)
lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
# move IPL device to lowcore # move IPL device to lowcore
mvc __LC_IPLDEV(4),IPL_DEVICE-PARMAREA(%r12) mvc __LC_IPLDEV(4),IPL_DEVICE-PARMAREA(%r12)
#
# Setup stack
#
l %r15,.Linittu-.LPG1(%r13)
mvc __LC_CURRENT(4),__TI_task(%r15)
ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union+THREAD_SIZE
st %r15,__LC_KERNEL_STACK # set end of kernel stack
ahi %r15,-96
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
l %r14,.Lipl_save_parameters-.LPG1(%r13)
basr %r14,%r14
# #
# clear bss memory # clear bss memory
# #
...@@ -114,6 +125,10 @@ startup_continue: ...@@ -114,6 +125,10 @@ startup_continue:
b .Lfchunk-.LPG1(%r13) b .Lfchunk-.LPG1(%r13)
.align 4 .align 4
.Lipl_save_parameters:
.long ipl_save_parameters
.Linittu:
.long init_thread_union
.Lpmask: .Lpmask:
.byte 0 .byte 0
.align 8 .align 8
...@@ -273,7 +288,23 @@ startup_continue: ...@@ -273,7 +288,23 @@ startup_continue:
.Lbss_end: .long _end .Lbss_end: .long _end
.Lparmaddr: .long PARMAREA .Lparmaddr: .long PARMAREA
.Lsccbaddr: .long .Lsccb .Lsccbaddr: .long .Lsccb
.globl ipl_schib
ipl_schib:
.rept 13
.long 0
.endr
.globl ipl_flags
ipl_flags:
.long 0
.globl ipl_devno
ipl_devno:
.word 0
.org 0x12000 .org 0x12000
.globl s390_readinfo_sccb
s390_readinfo_sccb:
.Lsccb: .Lsccb:
.hword 0x1000 # length, one page .hword 0x1000 # length, one page
.byte 0x00,0x00,0x00 .byte 0x00,0x00,0x00
...@@ -302,16 +333,6 @@ startup_continue: ...@@ -302,16 +333,6 @@ startup_continue:
.globl _stext .globl _stext
_stext: basr %r13,0 # get base _stext: basr %r13,0 # get base
.LPG3: .LPG3:
#
# Setup stack
#
l %r15,.Linittu-.LPG3(%r13)
mvc __LC_CURRENT(4),__TI_task(%r15)
ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union+THREAD_SIZE
st %r15,__LC_KERNEL_STACK # set end of kernel stack
ahi %r15,-96
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
# check control registers # check control registers
stctl %c0,%c15,0(%r15) stctl %c0,%c15,0(%r15)
oi 2(%r15),0x40 # enable sigp emergency signal oi 2(%r15),0x40 # enable sigp emergency signal
...@@ -330,6 +351,5 @@ _stext: basr %r13,0 # get base ...@@ -330,6 +351,5 @@ _stext: basr %r13,0 # get base
# #
.align 8 .align 8
.Ldw: .long 0x000a0000,0x00000000 .Ldw: .long 0x000a0000,0x00000000
.Linittu:.long init_thread_union
.Lstart:.long start_kernel .Lstart:.long start_kernel
.Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 .Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
...@@ -26,8 +26,8 @@ startup:basr %r13,0 # get base ...@@ -26,8 +26,8 @@ startup:basr %r13,0 # get base
# #
.org PARMAREA .org PARMAREA
.quad 0 # IPL_DEVICE .quad 0 # IPL_DEVICE
.quad RAMDISK_ORIGIN # INITRD_START .quad 0 # INITRD_START
.quad RAMDISK_SIZE # INITRD_SIZE .quad 0 # INITRD_SIZE
.org COMMAND_LINE .org COMMAND_LINE
.byte "root=/dev/ram0 ro" .byte "root=/dev/ram0 ro"
...@@ -39,8 +39,8 @@ startup_continue: ...@@ -39,8 +39,8 @@ startup_continue:
basr %r13,0 # get base basr %r13,0 # get base
.LPG1: sll %r13,1 # remove high order bit .LPG1: sll %r13,1 # remove high order bit
srl %r13,1 srl %r13,1
GET_IPL_DEVICE
lhi %r1,1 # mode 1 = esame lhi %r1,1 # mode 1 = esame
mvi __LC_AR_MODE_ID,1 # set esame flag
slr %r0,%r0 # set cpuid to zero slr %r0,%r0 # set cpuid to zero
sigp %r1,%r0,0x12 # switch to esame mode sigp %r1,%r0,0x12 # switch to esame mode
sam64 # switch to 64 bit mode sam64 # switch to 64 bit mode
...@@ -48,7 +48,18 @@ startup_continue: ...@@ -48,7 +48,18 @@ startup_continue:
lg %r12,.Lparmaddr-.LPG1(%r13)# pointer to parameter area lg %r12,.Lparmaddr-.LPG1(%r13)# pointer to parameter area
# move IPL device to lowcore # move IPL device to lowcore
mvc __LC_IPLDEV(4),IPL_DEVICE+4-PARMAREA(%r12) mvc __LC_IPLDEV(4),IPL_DEVICE+4-PARMAREA(%r12)
#
# Setup stack
#
larl %r15,init_thread_union
lg %r14,__TI_task(%r15) # cache current in lowcore
stg %r14,__LC_CURRENT
aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE
stg %r15,__LC_KERNEL_STACK # set end of kernel stack
aghi %r15,-160
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
brasl %r14,ipl_save_parameters
# #
# clear bss memory # clear bss memory
# #
...@@ -239,6 +250,19 @@ startup_continue: ...@@ -239,6 +250,19 @@ startup_continue:
oi 7(%r12),0x80 # set IDTE flag oi 7(%r12),0x80 # set IDTE flag
0: 0:
#
# find out if we have the MVCOS instruction
#
la %r1,0f-.LPG1(%r13) # set program check address
stg %r1,__LC_PGM_NEW_PSW+8
.short 0xc800 # mvcos 0(%r0),0(%r0),%r0
.short 0x0000
.short 0x0000
0: tm 0x8f,0x13 # special-operation exception?
bno 1f-.LPG1(%r13) # if yes, MVCOS is present
oi 6(%r12),2 # set MVCOS flag
1:
lpswe .Lentry-.LPG1(13) # jump to _stext in primary-space, lpswe .Lentry-.LPG1(13) # jump to _stext in primary-space,
# virtual and never return ... # virtual and never return ...
.align 16 .align 16
...@@ -268,7 +292,22 @@ startup_continue: ...@@ -268,7 +292,22 @@ startup_continue:
.Lparmaddr: .Lparmaddr:
.quad PARMAREA .quad PARMAREA
.globl ipl_schib
ipl_schib:
.rept 13
.long 0
.endr
.globl ipl_flags
ipl_flags:
.long 0
.globl ipl_devno
ipl_devno:
.word 0
.org 0x12000 .org 0x12000
.globl s390_readinfo_sccb
s390_readinfo_sccb:
.Lsccb: .Lsccb:
.hword 0x1000 # length, one page .hword 0x1000 # length, one page
.byte 0x00,0x00,0x00 .byte 0x00,0x00,0x00
...@@ -297,24 +336,12 @@ startup_continue: ...@@ -297,24 +336,12 @@ startup_continue:
.globl _stext .globl _stext
_stext: basr %r13,0 # get base _stext: basr %r13,0 # get base
.LPG3: .LPG3:
#
# Setup stack
#
larl %r15,init_thread_union
lg %r14,__TI_task(%r15) # cache current in lowcore
stg %r14,__LC_CURRENT
aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE
stg %r15,__LC_KERNEL_STACK # set end of kernel stack
aghi %r15,-160
xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
# check control registers # check control registers
stctg %c0,%c15,0(%r15) stctg %c0,%c15,0(%r15)
oi 6(%r15),0x40 # enable sigp emergency signal oi 6(%r15),0x40 # enable sigp emergency signal
oi 4(%r15),0x10 # switch on low address proctection oi 4(%r15),0x10 # switch on low address proctection
lctlg %c0,%c15,0(%r15) lctlg %c0,%c15,0(%r15)
#
lam 0,15,.Laregs-.LPG3(%r13) # load access regs needed by uaccess lam 0,15,.Laregs-.LPG3(%r13) # load access regs needed by uaccess
brasl %r14,start_kernel # go to C code brasl %r14,start_kernel # go to C code
# #
...@@ -322,7 +349,7 @@ _stext: basr %r13,0 # get base ...@@ -322,7 +349,7 @@ _stext: basr %r13,0 # get base
# #
basr %r13,0 basr %r13,0
lpswe .Ldw-.(%r13) # load disabled wait psw lpswe .Ldw-.(%r13) # load disabled wait psw
#
.align 8 .align 8
.Ldw: .quad 0x0002000180000000,0x0000000000000000 .Ldw: .quad 0x0002000180000000,0x0000000000000000
.Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 .Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
This diff is collapsed.
This diff is collapsed.
...@@ -8,13 +8,30 @@ ...@@ -8,13 +8,30 @@
#include <asm/lowcore.h> #include <asm/lowcore.h>
.globl do_reipl .globl do_reipl_asm
do_reipl: basr %r13,0 do_reipl_asm: basr %r13,0
.Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) .Lpg0: lpsw .Lnewpsw-.Lpg0(%r13)
.Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13)
stctl %c0,%c0,.Lctlsave-.Lpg0(%r13) # switch off lowcore protection
ni .Lctlsave-.Lpg0(%r13),0xef
lctl %c0,%c0,.Lctlsave-.Lpg0(%r13) .Lpg1: stctl %c0,%c0,.Lctlsave1-.Lpg0(%r13)
stctl %c0,%c0,.Lctlsave2-.Lpg0(%r13)
ni .Lctlsave1-.Lpg0(%r13),0xef
lctl %c0,%c0,.Lctlsave1-.Lpg0(%r13)
# do store status of all registers
stm %r0,%r15,__LC_GPREGS_SAVE_AREA
stctl %c0,%c15,__LC_CREGS_SAVE_AREA
mvc __LC_CREGS_SAVE_AREA(4),.Lctlsave2-.Lpg0(%r13)
stam %a0,%a15,__LC_AREGS_SAVE_AREA
stpx __LC_PREFIX_SAVE_AREA
stckc .Lclkcmp-.Lpg0(%r13)
mvc __LC_CLOCK_COMP_SAVE_AREA(8),.Lclkcmp-.Lpg0(%r13)
stpt __LC_CPU_TIMER_SAVE_AREA
st %r13, __LC_PSW_SAVE_AREA+4
lctl %c6,%c6,.Lall-.Lpg0(%r13)
lr %r1,%r2 lr %r1,%r2
mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13) mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13) stsch .Lschib-.Lpg0(%r13)
...@@ -46,9 +63,11 @@ do_reipl: basr %r13,0 ...@@ -46,9 +63,11 @@ do_reipl: basr %r13,0
.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) .Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13)
lpsw .Ldispsw-.Lpg0(%r13) lpsw .Ldispsw-.Lpg0(%r13)
.align 8 .align 8
.Lclkcmp: .quad 0x0000000000000000
.Lall: .long 0xff000000 .Lall: .long 0xff000000
.Lnull: .long 0x00000000 .Lnull: .long 0x00000000
.Lctlsave: .long 0x00000000 .Lctlsave1: .long 0x00000000
.Lctlsave2: .long 0x00000000
.align 8 .align 8
.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 .Lnewpsw: .long 0x00080000,0x80000000+.Lpg1
.Lpcnew: .long 0x00080000,0x80000000+.Lecs .Lpcnew: .long 0x00080000,0x80000000+.Lecs
......
...@@ -8,13 +8,30 @@ ...@@ -8,13 +8,30 @@
*/ */
#include <asm/lowcore.h> #include <asm/lowcore.h>
.globl do_reipl .globl do_reipl_asm
do_reipl: basr %r13,0 do_reipl_asm: basr %r13,0
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
# do store status of all registers
.Lpg0: stg %r1,.Lregsave-.Lpg0(%r13)
lghi %r1,0x1000
stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1)
lg %r0,.Lregsave-.Lpg0(%r13)
stg %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1)
stctg %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1)
stam %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1)
stpx __LC_PREFIX_SAVE_AREA-0x1000(%r1)
stfpc __LC_FP_CREG_SAVE_AREA-0x1000(%r1)
stckc .Lclkcmp-.Lpg0(%r13)
mvc __LC_CLOCK_COMP_SAVE_AREA-0x1000(8,%r1),.Lclkcmp-.Lpg0(%r13)
stpt __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1)
stg %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1)
lpswe .Lnewpsw-.Lpg0(%r13)
.Lpg1: lctlg %c6,%c6,.Lall-.Lpg0(%r13) .Lpg1: lctlg %c6,%c6,.Lall-.Lpg0(%r13)
stctg %c0,%c0,.Lctlsave-.Lpg0(%r13) stctg %c0,%c0,.Lregsave-.Lpg0(%r13)
ni .Lctlsave+4-.Lpg0(%r13),0xef ni .Lregsave+4-.Lpg0(%r13),0xef
lctlg %c0,%c0,.Lctlsave-.Lpg0(%r13) lctlg %c0,%c0,.Lregsave-.Lpg0(%r13)
lgr %r1,%r2 lgr %r1,%r2
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13) mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13) stsch .Lschib-.Lpg0(%r13)
...@@ -50,8 +67,9 @@ do_reipl: basr %r13,0 ...@@ -50,8 +67,9 @@ do_reipl: basr %r13,0
st %r14,.Ldispsw+12-.Lpg0(%r13) st %r14,.Ldispsw+12-.Lpg0(%r13)
lpswe .Ldispsw-.Lpg0(%r13) lpswe .Ldispsw-.Lpg0(%r13)
.align 8 .align 8
.Lclkcmp: .quad 0x0000000000000000
.Lall: .quad 0x00000000ff000000 .Lall: .quad 0x00000000ff000000
.Lctlsave: .quad 0x0000000000000000 .Lregsave: .quad 0x0000000000000000
.Lnull: .long 0x0000000000000000 .Lnull: .long 0x0000000000000000
.align 16 .align 16
/* /*
...@@ -92,5 +110,3 @@ do_reipl: basr %r13,0 ...@@ -92,5 +110,3 @@ do_reipl: basr %r13,0
.long 0x00000000,0x00000000 .long 0x00000000,0x00000000
.long 0x00000000,0x00000000 .long 0x00000000,0x00000000
/*
* This file contains the implementation of the
* Linux re-IPL support
*
* (C) Copyright IBM Corp. 2005
*
* Author(s): Volker Sameske (sameske@de.ibm.com)
*
*/
#include <linux/kernel.h>
static unsigned int reipl_diag_rc1;
static unsigned int reipl_diag_rc2;
/*
* re-IPL the system using the last used IPL parameters
*/
void reipl_diag(void)
{
asm volatile (
" la %%r4,0\n"
" la %%r5,0\n"
" diag %%r4,%2,0x308\n"
"0:\n"
" st %%r4,%0\n"
" st %%r5,%1\n"
".section __ex_table,\"a\"\n"
#ifdef CONFIG_64BIT
" .align 8\n"
" .quad 0b, 0b\n"
#else
" .align 4\n"
" .long 0b, 0b\n"
#endif
".previous\n"
: "=m" (reipl_diag_rc1), "=m" (reipl_diag_rc2)
: "d" (3) : "cc", "4", "5" );
}
...@@ -25,12 +25,6 @@ EXPORT_SYMBOL(_oi_bitmap); ...@@ -25,12 +25,6 @@ EXPORT_SYMBOL(_oi_bitmap);
EXPORT_SYMBOL(_ni_bitmap); EXPORT_SYMBOL(_ni_bitmap);
EXPORT_SYMBOL(_zb_findmap); EXPORT_SYMBOL(_zb_findmap);
EXPORT_SYMBOL(_sb_findmap); EXPORT_SYMBOL(_sb_findmap);
EXPORT_SYMBOL(__copy_from_user_asm);
EXPORT_SYMBOL(__copy_to_user_asm);
EXPORT_SYMBOL(__copy_in_user_asm);
EXPORT_SYMBOL(__clear_user_asm);
EXPORT_SYMBOL(__strncpy_from_user_asm);
EXPORT_SYMBOL(__strnlen_user_asm);
EXPORT_SYMBOL(diag10); EXPORT_SYMBOL(diag10);
/* /*
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/pfn.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -49,6 +50,12 @@ ...@@ -49,6 +50,12 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/sections.h> #include <asm/sections.h>
/*
* User copy operations.
*/
struct uaccess_ops uaccess;
EXPORT_SYMBOL_GPL(uaccess);
/* /*
* Machine setup.. * Machine setup..
*/ */
...@@ -284,16 +291,9 @@ void (*_machine_power_off)(void) = machine_power_off_smp; ...@@ -284,16 +291,9 @@ void (*_machine_power_off)(void) = machine_power_off_smp;
/* /*
* Reboot, halt and power_off routines for non SMP. * Reboot, halt and power_off routines for non SMP.
*/ */
extern void reipl(unsigned long devno);
extern void reipl_diag(void);
static void do_machine_restart_nonsmp(char * __unused) static void do_machine_restart_nonsmp(char * __unused)
{ {
reipl_diag(); do_reipl();
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0, NULL);
else
reipl (0x10000 | S390_lowcore.ipl_device);
} }
static void do_machine_halt_nonsmp(void) static void do_machine_halt_nonsmp(void)
...@@ -501,13 +501,47 @@ setup_memory(void) ...@@ -501,13 +501,47 @@ setup_memory(void)
* partially used pages are not usable - thus * partially used pages are not usable - thus
* we are rounding upwards: * we are rounding upwards:
*/ */
start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT; start_pfn = PFN_UP(__pa(&_end));
end_pfn = max_pfn = memory_end >> PAGE_SHIFT; end_pfn = max_pfn = PFN_DOWN(memory_end);
/* Initialize storage key for kernel pages */ /* Initialize storage key for kernel pages */
for (init_pfn = 0 ; init_pfn < start_pfn; init_pfn++) for (init_pfn = 0 ; init_pfn < start_pfn; init_pfn++)
page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY); page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY);
#ifdef CONFIG_BLK_DEV_INITRD
/*
* Move the initrd in case the bitmap of the bootmem allocater
* would overwrite it.
*/
if (INITRD_START && INITRD_SIZE) {
unsigned long bmap_size;
unsigned long start;
bmap_size = bootmem_bootmap_pages(end_pfn - start_pfn + 1);
bmap_size = PFN_PHYS(bmap_size);
if (PFN_PHYS(start_pfn) + bmap_size > INITRD_START) {
start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE;
if (start + INITRD_SIZE > memory_end) {
printk("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\n"
"disabling initrd\n",
start + INITRD_SIZE, memory_end);
INITRD_START = INITRD_SIZE = 0;
} else {
printk("Moving initrd (0x%08lx -> 0x%08lx, "
"size: %ld)\n",
INITRD_START, start, INITRD_SIZE);
memmove((void *) start, (void *) INITRD_START,
INITRD_SIZE);
INITRD_START = start;
}
}
}
#endif
/* /*
* Initialize the boot-time allocator (with low memory only): * Initialize the boot-time allocator (with low memory only):
*/ */
...@@ -559,7 +593,7 @@ setup_memory(void) ...@@ -559,7 +593,7 @@ setup_memory(void)
reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
if (INITRD_START) { if (INITRD_START && INITRD_SIZE) {
if (INITRD_START + INITRD_SIZE <= memory_end) { if (INITRD_START + INITRD_SIZE <= memory_end) {
reserve_bootmem(INITRD_START, INITRD_SIZE); reserve_bootmem(INITRD_START, INITRD_SIZE);
initrd_start = INITRD_START; initrd_start = INITRD_START;
...@@ -613,6 +647,11 @@ setup_arch(char **cmdline_p) ...@@ -613,6 +647,11 @@ setup_arch(char **cmdline_p)
memory_end = memory_size; memory_end = memory_size;
if (MACHINE_HAS_MVCOS)
memcpy(&uaccess, &uaccess_mvcos, sizeof(uaccess));
else
memcpy(&uaccess, &uaccess_std, sizeof(uaccess));
parse_early_param(); parse_early_param();
#ifndef CONFIG_64BIT #ifndef CONFIG_64BIT
...@@ -720,214 +759,3 @@ struct seq_operations cpuinfo_op = { ...@@ -720,214 +759,3 @@ struct seq_operations cpuinfo_op = {
.show = show_cpuinfo, .show = show_cpuinfo,
}; };
#define DEFINE_IPL_ATTR(_name, _format, _value) \
static ssize_t ipl_##_name##_show(struct subsystem *subsys, \
char *page) \
{ \
return sprintf(page, _format, _value); \
} \
static struct subsys_attribute ipl_##_name##_attr = \
__ATTR(_name, S_IRUGO, ipl_##_name##_show, NULL);
DEFINE_IPL_ATTR(wwpn, "0x%016llx\n", (unsigned long long)
IPL_PARMBLOCK_START->fcp.wwpn);
DEFINE_IPL_ATTR(lun, "0x%016llx\n", (unsigned long long)
IPL_PARMBLOCK_START->fcp.lun);
DEFINE_IPL_ATTR(bootprog, "%lld\n", (unsigned long long)
IPL_PARMBLOCK_START->fcp.bootprog);
DEFINE_IPL_ATTR(br_lba, "%lld\n", (unsigned long long)
IPL_PARMBLOCK_START->fcp.br_lba);
enum ipl_type_type {
ipl_type_unknown,
ipl_type_ccw,
ipl_type_fcp,
};
static enum ipl_type_type
get_ipl_type(void)
{
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
if (!IPL_DEVNO_VALID)
return ipl_type_unknown;
if (!IPL_PARMBLOCK_VALID)
return ipl_type_ccw;
if (ipl->hdr.header.version > IPL_MAX_SUPPORTED_VERSION)
return ipl_type_unknown;
if (ipl->fcp.pbt != IPL_TYPE_FCP)
return ipl_type_unknown;
return ipl_type_fcp;
}
static ssize_t
ipl_type_show(struct subsystem *subsys, char *page)
{
switch (get_ipl_type()) {
case ipl_type_ccw:
return sprintf(page, "ccw\n");
case ipl_type_fcp:
return sprintf(page, "fcp\n");
default:
return sprintf(page, "unknown\n");
}
}
static struct subsys_attribute ipl_type_attr = __ATTR_RO(ipl_type);
static ssize_t
ipl_device_show(struct subsystem *subsys, char *page)
{
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
switch (get_ipl_type()) {
case ipl_type_ccw:
return sprintf(page, "0.0.%04x\n", ipl_devno);
case ipl_type_fcp:
return sprintf(page, "0.0.%04x\n", ipl->fcp.devno);
default:
return 0;
}
}
static struct subsys_attribute ipl_device_attr =
__ATTR(device, S_IRUGO, ipl_device_show, NULL);
static struct attribute *ipl_fcp_attrs[] = {
&ipl_type_attr.attr,
&ipl_device_attr.attr,
&ipl_wwpn_attr.attr,
&ipl_lun_attr.attr,
&ipl_bootprog_attr.attr,
&ipl_br_lba_attr.attr,
NULL,
};
static struct attribute_group ipl_fcp_attr_group = {
.attrs = ipl_fcp_attrs,
};
static struct attribute *ipl_ccw_attrs[] = {
&ipl_type_attr.attr,
&ipl_device_attr.attr,
NULL,
};
static struct attribute_group ipl_ccw_attr_group = {
.attrs = ipl_ccw_attrs,
};
static struct attribute *ipl_unknown_attrs[] = {
&ipl_type_attr.attr,
NULL,
};
static struct attribute_group ipl_unknown_attr_group = {
.attrs = ipl_unknown_attrs,
};
static ssize_t
ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
unsigned int size = IPL_PARMBLOCK_SIZE;
if (off > size)
return 0;
if (off + count > size)
count = size - off;
memcpy(buf, (void *) IPL_PARMBLOCK_START + off, count);
return count;
}
static struct bin_attribute ipl_parameter_attr = {
.attr = {
.name = "binary_parameter",
.mode = S_IRUGO,
.owner = THIS_MODULE,
},
.size = PAGE_SIZE,
.read = &ipl_parameter_read,
};
static ssize_t
ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
unsigned int size = IPL_PARMBLOCK_START->fcp.scp_data_len;
void *scp_data = &IPL_PARMBLOCK_START->fcp.scp_data;
if (off > size)
return 0;
if (off + count > size)
count = size - off;
memcpy(buf, scp_data + off, count);
return count;
}
static struct bin_attribute ipl_scp_data_attr = {
.attr = {
.name = "scp_data",
.mode = S_IRUGO,
.owner = THIS_MODULE,
},
.size = PAGE_SIZE,
.read = &ipl_scp_data_read,
};
static decl_subsys(ipl, NULL, NULL);
static int ipl_register_fcp_files(void)
{
int rc;
rc = sysfs_create_group(&ipl_subsys.kset.kobj,
&ipl_fcp_attr_group);
if (rc)
goto out;
rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
&ipl_parameter_attr);
if (rc)
goto out_ipl_parm;
rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
&ipl_scp_data_attr);
if (!rc)
goto out;
sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr);
out_ipl_parm:
sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group);
out:
return rc;
}
static int __init
ipl_device_sysfs_register(void) {
int rc;
rc = firmware_register(&ipl_subsys);
if (rc)
goto out;
switch (get_ipl_type()) {
case ipl_type_ccw:
rc = sysfs_create_group(&ipl_subsys.kset.kobj,
&ipl_ccw_attr_group);
break;
case ipl_type_fcp:
rc = ipl_register_fcp_files();
break;
default:
rc = sysfs_create_group(&ipl_subsys.kset.kobj,
&ipl_unknown_attr_group);
break;
}
if (rc)
firmware_unregister(&ipl_subsys);
out:
return rc;
}
__initcall(ipl_device_sysfs_register);
...@@ -114,29 +114,26 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, ...@@ -114,29 +114,26 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{ {
unsigned long old_mask = regs->psw.mask; unsigned long old_mask = regs->psw.mask;
int err; _sigregs user_sregs;
save_access_regs(current->thread.acrs); save_access_regs(current->thread.acrs);
/* Copy a 'clean' PSW mask to the user to avoid leaking /* Copy a 'clean' PSW mask to the user to avoid leaking
information about whether PER is currently on. */ information about whether PER is currently on. */
regs->psw.mask = PSW_MASK_MERGE(PSW_USER_BITS, regs->psw.mask); regs->psw.mask = PSW_MASK_MERGE(PSW_USER_BITS, regs->psw.mask);
err = __copy_to_user(&sregs->regs.psw, &regs->psw, memcpy(&user_sregs.regs.psw, &regs->psw, sizeof(sregs->regs.psw) +
sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs)); sizeof(sregs->regs.gprs));
regs->psw.mask = old_mask; regs->psw.mask = old_mask;
if (err != 0) memcpy(&user_sregs.regs.acrs, current->thread.acrs,
return err;
err = __copy_to_user(&sregs->regs.acrs, current->thread.acrs,
sizeof(sregs->regs.acrs)); sizeof(sregs->regs.acrs));
if (err != 0)
return err;
/* /*
* We have to store the fp registers to current->thread.fp_regs * We have to store the fp registers to current->thread.fp_regs
* to merge them with the emulated registers. * to merge them with the emulated registers.
*/ */
save_fp_regs(&current->thread.fp_regs); save_fp_regs(&current->thread.fp_regs);
return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs, memcpy(&user_sregs.fpregs, &current->thread.fp_regs,
sizeof(s390_fp_regs)); sizeof(s390_fp_regs));
return __copy_to_user(sregs, &user_sregs, sizeof(_sigregs));
} }
/* Returns positive number on error */ /* Returns positive number on error */
...@@ -144,27 +141,25 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -144,27 +141,25 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{ {
unsigned long old_mask = regs->psw.mask; unsigned long old_mask = regs->psw.mask;
int err; int err;
_sigregs user_sregs;
/* Alwys make any pending restarted system call return -EINTR */ /* Alwys make any pending restarted system call return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall; current_thread_info()->restart_block.fn = do_no_restart_syscall;
err = __copy_from_user(&regs->psw, &sregs->regs.psw, err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs));
sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs));
regs->psw.mask = PSW_MASK_MERGE(old_mask, regs->psw.mask); regs->psw.mask = PSW_MASK_MERGE(old_mask, regs->psw.mask);
regs->psw.addr |= PSW_ADDR_AMODE; regs->psw.addr |= PSW_ADDR_AMODE;
if (err) if (err)
return err; return err;
err = __copy_from_user(&current->thread.acrs, &sregs->regs.acrs, memcpy(&regs->psw, &user_sregs.regs.psw, sizeof(sregs->regs.psw) +
sizeof(sregs->regs.gprs));
memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
sizeof(sregs->regs.acrs)); sizeof(sregs->regs.acrs));
if (err)
return err;
restore_access_regs(current->thread.acrs); restore_access_regs(current->thread.acrs);
err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs, memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
sizeof(s390_fp_regs)); sizeof(s390_fp_regs));
current->thread.fp_regs.fpc &= FPC_VALID_MASK; current->thread.fp_regs.fpc &= FPC_VALID_MASK;
if (err)
return err;
restore_fp_regs(&current->thread.fp_regs); restore_fp_regs(&current->thread.fp_regs);
regs->trap = -1; /* disable syscall checks */ regs->trap = -1; /* disable syscall checks */
...@@ -457,6 +452,7 @@ void do_signal(struct pt_regs *regs) ...@@ -457,6 +452,7 @@ void do_signal(struct pt_regs *regs)
case -ERESTART_RESTARTBLOCK: case -ERESTART_RESTARTBLOCK:
regs->gprs[2] = -EINTR; regs->gprs[2] = -EINTR;
} }
regs->trap = -1; /* Don't deal with this again. */
} }
/* Get signal to deliver. When running under ptrace, at this point /* Get signal to deliver. When running under ptrace, at this point
......
...@@ -59,9 +59,6 @@ static struct task_struct *current_set[NR_CPUS]; ...@@ -59,9 +59,6 @@ static struct task_struct *current_set[NR_CPUS];
extern char vmhalt_cmd[]; extern char vmhalt_cmd[];
extern char vmpoff_cmd[]; extern char vmpoff_cmd[];
extern void reipl(unsigned long devno);
extern void reipl_diag(void);
static void smp_ext_bitcall(int, ec_bit_sig); static void smp_ext_bitcall(int, ec_bit_sig);
static void smp_ext_bitcall_others(ec_bit_sig); static void smp_ext_bitcall_others(ec_bit_sig);
...@@ -279,12 +276,7 @@ static void do_machine_restart(void * __unused) ...@@ -279,12 +276,7 @@ static void do_machine_restart(void * __unused)
* interrupted by an external interrupt and s390irq * interrupted by an external interrupt and s390irq
* locks are always held disabled). * locks are always held disabled).
*/ */
reipl_diag(); do_reipl();
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0, NULL);
else
reipl (0x10000 | S390_lowcore.ipl_device);
} }
void machine_restart_smp(char * __unused) void machine_restart_smp(char * __unused)
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/kprobes.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -39,6 +40,7 @@ ...@@ -39,6 +40,7 @@
#include <asm/s390_ext.h> #include <asm/s390_ext.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/debug.h> #include <asm/debug.h>
#include <asm/kdebug.h>
/* Called from entry.S only */ /* Called from entry.S only */
extern void handle_per_exception(struct pt_regs *regs); extern void handle_per_exception(struct pt_regs *regs);
...@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12; ...@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12;
static int kstack_depth_to_print = 20; static int kstack_depth_to_print = 20;
#endif /* CONFIG_64BIT */ #endif /* CONFIG_64BIT */
ATOMIC_NOTIFIER_HEAD(s390die_chain);
int register_die_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&s390die_chain, nb);
}
EXPORT_SYMBOL(register_die_notifier);
int unregister_die_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&s390die_chain, nb);
}
EXPORT_SYMBOL(unregister_die_notifier);
/* /*
* For show_trace we have tree different stack to consider: * For show_trace we have tree different stack to consider:
* - the panic stack which is used if the kernel stack has overflown * - the panic stack which is used if the kernel stack has overflown
...@@ -305,8 +321,9 @@ report_user_fault(long interruption_code, struct pt_regs *regs) ...@@ -305,8 +321,9 @@ report_user_fault(long interruption_code, struct pt_regs *regs)
#endif #endif
} }
static void inline do_trap(long interruption_code, int signr, char *str, static void __kprobes inline do_trap(long interruption_code, int signr,
struct pt_regs *regs, siginfo_t *info) char *str, struct pt_regs *regs,
siginfo_t *info)
{ {
/* /*
* We got all needed information from the lowcore and can * We got all needed information from the lowcore and can
...@@ -315,6 +332,10 @@ static void inline do_trap(long interruption_code, int signr, char *str, ...@@ -315,6 +332,10 @@ static void inline do_trap(long interruption_code, int signr, char *str,
if (regs->psw.mask & PSW_MASK_PSTATE) if (regs->psw.mask & PSW_MASK_PSTATE)
local_irq_enable(); local_irq_enable();
if (notify_die(DIE_TRAP, str, regs, interruption_code,
interruption_code, signr) == NOTIFY_STOP)
return;
if (regs->psw.mask & PSW_MASK_PSTATE) { if (regs->psw.mask & PSW_MASK_PSTATE) {
struct task_struct *tsk = current; struct task_struct *tsk = current;
...@@ -336,8 +357,12 @@ static inline void __user *get_check_address(struct pt_regs *regs) ...@@ -336,8 +357,12 @@ static inline void __user *get_check_address(struct pt_regs *regs)
return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN); return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
} }
void do_single_step(struct pt_regs *regs) void __kprobes do_single_step(struct pt_regs *regs)
{ {
if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0,
SIGTRAP) == NOTIFY_STOP){
return;
}
if ((current->ptrace & PT_PTRACED) != 0) if ((current->ptrace & PT_PTRACED) != 0)
force_sig(SIGTRAP, current); force_sig(SIGTRAP, current);
} }
......
...@@ -24,6 +24,7 @@ SECTIONS ...@@ -24,6 +24,7 @@ SECTIONS
*(.text) *(.text)
SCHED_TEXT SCHED_TEXT
LOCK_TEXT LOCK_TEXT
KPROBES_TEXT
*(.fixup) *(.fixup)
*(.gnu.warning) *(.gnu.warning)
} = 0x0700 } = 0x0700
...@@ -117,7 +118,7 @@ SECTIONS ...@@ -117,7 +118,7 @@ SECTIONS
/* Sections to be discarded */ /* Sections to be discarded */
/DISCARD/ : { /DISCARD/ : {
*(.exitcall.exit) *(.exit.text) *(.exit.data) *(.exitcall.exit)
} }
/* Stabs debugging sections. */ /* Stabs debugging sections. */
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
EXTRA_AFLAGS := -traditional EXTRA_AFLAGS := -traditional
lib-y += delay.o string.o lib-y += delay.o string.o uaccess_std.o
lib-y += $(if $(CONFIG_64BIT),uaccess64.o,uaccess.o) lib-$(CONFIG_64BIT) += uaccess_mvcos.o
lib-$(CONFIG_SMP) += spinlock.o lib-$(CONFIG_SMP) += spinlock.o
/*
* arch/s390/lib/uaccess.S
* __copy_{from|to}_user functions.
*
* s390
* Copyright (C) 2000,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* These functions have standard call interface
*/
#include <linux/errno.h>
#include <asm/lowcore.h>
#include <asm/asm-offsets.h>
.text
.align 4
.globl __copy_from_user_asm
# %r2 = to, %r3 = n, %r4 = from
__copy_from_user_asm:
slr %r0,%r0
0: mvcp 0(%r3,%r2),0(%r4),%r0
jnz 1f
slr %r2,%r2
br %r14
1: la %r2,256(%r2)
la %r4,256(%r4)
ahi %r3,-256
2: mvcp 0(%r3,%r2),0(%r4),%r0
jnz 1b
3: slr %r2,%r2
br %r14
4: lhi %r0,-4096
lr %r5,%r4
slr %r5,%r0
nr %r5,%r0 # %r5 = (%r4 + 4096) & -4096
slr %r5,%r4 # %r5 = #bytes to next user page boundary
clr %r3,%r5 # copy crosses next page boundary ?
jnh 6f # no, the current page faulted
# move with the reduced length which is < 256
5: mvcp 0(%r5,%r2),0(%r4),%r0
slr %r3,%r5
6: lr %r2,%r3
br %r14
.section __ex_table,"a"
.long 0b,4b
.long 2b,4b
.long 5b,6b
.previous
.align 4
.text
.globl __copy_to_user_asm
# %r2 = from, %r3 = n, %r4 = to
__copy_to_user_asm:
slr %r0,%r0
0: mvcs 0(%r3,%r4),0(%r2),%r0
jnz 1f
slr %r2,%r2
br %r14
1: la %r2,256(%r2)
la %r4,256(%r4)
ahi %r3,-256
2: mvcs 0(%r3,%r4),0(%r2),%r0
jnz 1b
3: slr %r2,%r2
br %r14
4: lhi %r0,-4096
lr %r5,%r4
slr %r5,%r0
nr %r5,%r0 # %r5 = (%r4 + 4096) & -4096
slr %r5,%r4 # %r5 = #bytes to next user page boundary
clr %r3,%r5 # copy crosses next page boundary ?
jnh 6f # no, the current page faulted
# move with the reduced length which is < 256
5: mvcs 0(%r5,%r4),0(%r2),%r0
slr %r3,%r5
6: lr %r2,%r3
br %r14
.section __ex_table,"a"
.long 0b,4b
.long 2b,4b
.long 5b,6b
.previous
.align 4
.text
.globl __copy_in_user_asm
# %r2 = from, %r3 = n, %r4 = to
__copy_in_user_asm:
ahi %r3,-1
jo 6f
sacf 256
bras %r1,4f
0: ahi %r3,257
1: mvc 0(1,%r4),0(%r2)
la %r2,1(%r2)
la %r4,1(%r4)
ahi %r3,-1
jnz 1b
2: lr %r2,%r3
br %r14
3: mvc 0(256,%r4),0(%r2)
la %r2,256(%r2)
la %r4,256(%r4)
4: ahi %r3,-256
jnm 3b
5: ex %r3,4(%r1)
sacf 0
6: slr %r2,%r2
br %r14
.section __ex_table,"a"
.long 1b,2b
.long 3b,0b
.long 5b,0b
.previous
.align 4
.text
.globl __clear_user_asm
# %r2 = to, %r3 = n
__clear_user_asm:
bras %r5,0f
.long empty_zero_page
0: l %r5,0(%r5)
slr %r0,%r0
1: mvcs 0(%r3,%r2),0(%r5),%r0
jnz 2f
slr %r2,%r2
br %r14
2: la %r2,256(%r2)
ahi %r3,-256
3: mvcs 0(%r3,%r2),0(%r5),%r0
jnz 2b
4: slr %r2,%r2
br %r14
5: lhi %r0,-4096
lr %r4,%r2
slr %r4,%r0
nr %r4,%r0 # %r4 = (%r2 + 4096) & -4096
slr %r4,%r2 # %r4 = #bytes to next user page boundary
clr %r3,%r4 # clear crosses next page boundary ?
jnh 7f # no, the current page faulted
# clear with the reduced length which is < 256
6: mvcs 0(%r4,%r2),0(%r5),%r0
slr %r3,%r4
7: lr %r2,%r3
br %r14
.section __ex_table,"a"
.long 1b,5b
.long 3b,5b
.long 6b,7b
.previous
.align 4
.text
.globl __strncpy_from_user_asm
# %r2 = count, %r3 = dst, %r4 = src
__strncpy_from_user_asm:
lhi %r0,0
lr %r1,%r4
la %r4,0(%r4) # clear high order bit from %r4
la %r2,0(%r2,%r4) # %r2 points to first byte after string
sacf 256
0: srst %r2,%r1
jo 0b
sacf 0
lr %r1,%r2
jh 1f # \0 found in string ?
ahi %r1,1 # include \0 in copy
1: slr %r1,%r4 # %r1 = copy length (without \0)
slr %r2,%r4 # %r2 = return length (including \0)
2: mvcp 0(%r1,%r3),0(%r4),%r0
jnz 3f
br %r14
3: la %r3,256(%r3)
la %r4,256(%r4)
ahi %r1,-256
mvcp 0(%r1,%r3),0(%r4),%r0
jnz 3b
br %r14
4: sacf 0
lhi %r2,-EFAULT
br %r14
.section __ex_table,"a"
.long 0b,4b
.previous
.align 4
.text
.globl __strnlen_user_asm
# %r2 = count, %r3 = src
__strnlen_user_asm:
lhi %r0,0
lr %r1,%r3
la %r3,0(%r3) # clear high order bit from %r4
la %r2,0(%r2,%r3) # %r2 points to first byte after string
sacf 256
0: srst %r2,%r1
jo 0b
sacf 0
ahi %r2,1 # strnlen_user result includes the \0
# or return count+1 if \0 not found
slr %r2,%r3
br %r14
2: sacf 0
slr %r2,%r2 # return 0 on exception
br %r14
.section __ex_table,"a"
.long 0b,2b
.previous
/*
* arch/s390x/lib/uaccess.S
* __copy_{from|to}_user functions.
*
* s390
* Copyright (C) 2000,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* These functions have standard call interface
*/
#include <linux/errno.h>
#include <asm/lowcore.h>
#include <asm/asm-offsets.h>
.text
.align 4
.globl __copy_from_user_asm
# %r2 = to, %r3 = n, %r4 = from
__copy_from_user_asm:
slgr %r0,%r0
0: mvcp 0(%r3,%r2),0(%r4),%r0
jnz 1f
slgr %r2,%r2
br %r14
1: la %r2,256(%r2)
la %r4,256(%r4)
aghi %r3,-256
2: mvcp 0(%r3,%r2),0(%r4),%r0
jnz 1b
3: slgr %r2,%r2
br %r14
4: lghi %r0,-4096
lgr %r5,%r4
slgr %r5,%r0
ngr %r5,%r0 # %r5 = (%r4 + 4096) & -4096
slgr %r5,%r4 # %r5 = #bytes to next user page boundary
clgr %r3,%r5 # copy crosses next page boundary ?
jnh 6f # no, the current page faulted
# move with the reduced length which is < 256
5: mvcp 0(%r5,%r2),0(%r4),%r0
slgr %r3,%r5
6: lgr %r2,%r3
br %r14
.section __ex_table,"a"
.quad 0b,4b
.quad 2b,4b
.quad 5b,6b
.previous
.align 4
.text
.globl __copy_to_user_asm
# %r2 = from, %r3 = n, %r4 = to
__copy_to_user_asm:
slgr %r0,%r0
0: mvcs 0(%r3,%r4),0(%r2),%r0
jnz 1f
slgr %r2,%r2
br %r14
1: la %r2,256(%r2)
la %r4,256(%r4)
aghi %r3,-256
2: mvcs 0(%r3,%r4),0(%r2),%r0
jnz 1b
3: slgr %r2,%r2
br %r14
4: lghi %r0,-4096
lgr %r5,%r4
slgr %r5,%r0
ngr %r5,%r0 # %r5 = (%r4 + 4096) & -4096
slgr %r5,%r4 # %r5 = #bytes to next user page boundary
clgr %r3,%r5 # copy crosses next page boundary ?
jnh 6f # no, the current page faulted
# move with the reduced length which is < 256
5: mvcs 0(%r5,%r4),0(%r2),%r0
slgr %r3,%r5
6: lgr %r2,%r3
br %r14
.section __ex_table,"a"
.quad 0b,4b
.quad 2b,4b
.quad 5b,6b
.previous
.align 4
.text
.globl __copy_in_user_asm
# %r2 = from, %r3 = n, %r4 = to
__copy_in_user_asm:
aghi %r3,-1
jo 6f
sacf 256
bras %r1,4f
0: aghi %r3,257
1: mvc 0(1,%r4),0(%r2)
la %r2,1(%r2)
la %r4,1(%r4)
aghi %r3,-1
jnz 1b
2: lgr %r2,%r3
br %r14
3: mvc 0(256,%r4),0(%r2)
la %r2,256(%r2)
la %r4,256(%r4)
4: aghi %r3,-256
jnm 3b
5: ex %r3,4(%r1)
sacf 0
6: slgr %r2,%r2
br 14
.section __ex_table,"a"
.quad 1b,2b
.quad 3b,0b
.quad 5b,0b
.previous
.align 4
.text
.globl __clear_user_asm
# %r2 = to, %r3 = n
__clear_user_asm:
slgr %r0,%r0
larl %r5,empty_zero_page
1: mvcs 0(%r3,%r2),0(%r5),%r0
jnz 2f
slgr %r2,%r2
br %r14
2: la %r2,256(%r2)
aghi %r3,-256
3: mvcs 0(%r3,%r2),0(%r5),%r0
jnz 2b
4: slgr %r2,%r2
br %r14
5: lghi %r0,-4096
lgr %r4,%r2
slgr %r4,%r0
ngr %r4,%r0 # %r4 = (%r2 + 4096) & -4096
slgr %r4,%r2 # %r4 = #bytes to next user page boundary
clgr %r3,%r4 # clear crosses next page boundary ?
jnh 7f # no, the current page faulted
# clear with the reduced length which is < 256
6: mvcs 0(%r4,%r2),0(%r5),%r0
slgr %r3,%r4
7: lgr %r2,%r3
br %r14
.section __ex_table,"a"
.quad 1b,5b
.quad 3b,5b
.quad 6b,7b
.previous
.align 4
.text
.globl __strncpy_from_user_asm
# %r2 = count, %r3 = dst, %r4 = src
__strncpy_from_user_asm:
lghi %r0,0
lgr %r1,%r4
la %r2,0(%r2,%r4) # %r2 points to first byte after string
sacf 256
0: srst %r2,%r1
jo 0b
sacf 0
lgr %r1,%r2
jh 1f # \0 found in string ?
aghi %r1,1 # include \0 in copy
1: slgr %r1,%r4 # %r1 = copy length (without \0)
slgr %r2,%r4 # %r2 = return length (including \0)
2: mvcp 0(%r1,%r3),0(%r4),%r0
jnz 3f
br %r14
3: la %r3,256(%r3)
la %r4,256(%r4)
aghi %r1,-256
mvcp 0(%r1,%r3),0(%r4),%r0
jnz 3b
br %r14
4: sacf 0
lghi %r2,-EFAULT
br %r14
.section __ex_table,"a"
.quad 0b,4b
.previous
.align 4
.text
.globl __strnlen_user_asm
# %r2 = count, %r3 = src
__strnlen_user_asm:
lghi %r0,0
lgr %r1,%r3
la %r2,0(%r2,%r3) # %r2 points to first byte after string
sacf 256
0: srst %r2,%r1
jo 0b
sacf 0
aghi %r2,1 # strnlen_user result includes the \0
# or return count+1 if \0 not found
slgr %r2,%r3
br %r14
2: sacf 0
slgr %r2,%r2 # return 0 on exception
br %r14
.section __ex_table,"a"
.quad 0b,2b
.previous
/*
* arch/s390/lib/uaccess_mvcos.c
*
* Optimized user space space access functions based on mvcos.
*
* Copyright (C) IBM Corp. 2006
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
* Gerald Schaefer (gerald.schaefer@de.ibm.com)
*/
#include <linux/errno.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/futex.h>
#ifndef __s390x__
#define AHI "ahi"
#define ALR "alr"
#define CLR "clr"
#define LHI "lhi"
#define SLR "slr"
#else
#define AHI "aghi"
#define ALR "algr"
#define CLR "clgr"
#define LHI "lghi"
#define SLR "slgr"
#endif
size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x)
{
register unsigned long reg0 asm("0") = 0x81UL;
unsigned long tmp1, tmp2;
tmp1 = -4096UL;
asm volatile(
"0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n"
" jz 4f\n"
"1:"ALR" %0,%3\n"
" "SLR" %1,%3\n"
" "SLR" %2,%3\n"
" j 0b\n"
"2: la %4,4095(%1)\n"/* %4 = ptr + 4095 */
" nr %4,%3\n" /* %4 = (ptr + 4095) & -4096 */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 5f\n"
"3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n"
" "SLR" %0,%4\n"
" j 5f\n"
"4:"SLR" %0,%0\n"
"5: \n"
EX_TABLE(0b,2b) EX_TABLE(3b,5b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: "d" (reg0) : "cc", "memory");
return size;
}
size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x)
{
register unsigned long reg0 asm("0") = 0x810000UL;
unsigned long tmp1, tmp2;
tmp1 = -4096UL;
asm volatile(
"0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
" jz 4f\n"
"1:"ALR" %0,%3\n"
" "SLR" %1,%3\n"
" "SLR" %2,%3\n"
" j 0b\n"
"2: la %4,4095(%1)\n"/* %4 = ptr + 4095 */
" nr %4,%3\n" /* %4 = (ptr + 4095) & -4096 */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 5f\n"
"3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n"
" "SLR" %0,%4\n"
" j 5f\n"
"4:"SLR" %0,%0\n"
"5: \n"
EX_TABLE(0b,2b) EX_TABLE(3b,5b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: "d" (reg0) : "cc", "memory");
return size;
}
size_t copy_in_user_mvcos(size_t size, void __user *to, const void __user *from)
{
register unsigned long reg0 asm("0") = 0x810081UL;
unsigned long tmp1, tmp2;
tmp1 = -4096UL;
/* FIXME: copy with reduced length. */
asm volatile(
"0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
" jz 2f\n"
"1:"ALR" %0,%3\n"
" "SLR" %1,%3\n"
" "SLR" %2,%3\n"
" j 0b\n"
"2:"SLR" %0,%0\n"
"3: \n"
EX_TABLE(0b,3b)
: "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
: "d" (reg0) : "cc", "memory");
return size;
}
size_t clear_user_mvcos(size_t size, void __user *to)
{
register unsigned long reg0 asm("0") = 0x810000UL;
unsigned long tmp1, tmp2;
tmp1 = -4096UL;
asm volatile(
"0: .insn ss,0xc80000000000,0(%0,%1),0(%4),0\n"
" jz 4f\n"
"1:"ALR" %0,%2\n"
" "SLR" %1,%2\n"
" j 0b\n"
"2: la %3,4095(%1)\n"/* %4 = to + 4095 */
" nr %3,%2\n" /* %4 = (to + 4095) & -4096 */
" "SLR" %3,%1\n"
" "CLR" %0,%3\n" /* copy crosses next page boundary? */
" jnh 5f\n"
"3: .insn ss,0xc80000000000,0(%3,%1),0(%4),0\n"
" "SLR" %0,%3\n"
" j 5f\n"
"4:"SLR" %0,%0\n"
"5: \n"
EX_TABLE(0b,2b) EX_TABLE(3b,5b)
: "+a" (size), "+a" (to), "+a" (tmp1), "=a" (tmp2)
: "a" (empty_zero_page), "d" (reg0) : "cc", "memory");
return size;
}
extern size_t copy_from_user_std_small(size_t, const void __user *, void *);
extern size_t copy_to_user_std_small(size_t, void __user *, const void *);
extern size_t strnlen_user_std(size_t, const char __user *);
extern size_t strncpy_from_user_std(size_t, const char __user *, char *);
extern int futex_atomic_op(int, int __user *, int, int *);
extern int futex_atomic_cmpxchg(int __user *, int, int);
struct uaccess_ops uaccess_mvcos = {
.copy_from_user = copy_from_user_mvcos,
.copy_from_user_small = copy_from_user_std_small,
.copy_to_user = copy_to_user_mvcos,
.copy_to_user_small = copy_to_user_std_small,
.copy_in_user = copy_in_user_mvcos,
.clear_user = clear_user_mvcos,
.strnlen_user = strnlen_user_std,
.strncpy_from_user = strncpy_from_user_std,
.futex_atomic_op = futex_atomic_op,
.futex_atomic_cmpxchg = futex_atomic_cmpxchg,
};
/*
* arch/s390/lib/uaccess_std.c
*
* Standard user space access functions based on mvcp/mvcs and doing
* interesting things in the secondary space mode.
*
* Copyright (C) IBM Corp. 2006
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
* Gerald Schaefer (gerald.schaefer@de.ibm.com)
*/
#include <linux/errno.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/futex.h>
#ifndef __s390x__
#define AHI "ahi"
#define ALR "alr"
#define CLR "clr"
#define LHI "lhi"
#define SLR "slr"
#else
#define AHI "aghi"
#define ALR "algr"
#define CLR "clgr"
#define LHI "lghi"
#define SLR "slgr"
#endif
size_t copy_from_user_std(size_t size, const void __user *ptr, void *x)
{
unsigned long tmp1, tmp2;
tmp1 = -256UL;
asm volatile(
"0: mvcp 0(%0,%2),0(%1),%3\n"
" jz 5f\n"
"1:"ALR" %0,%3\n"
" la %1,256(%1)\n"
" la %2,256(%2)\n"
"2: mvcp 0(%0,%2),0(%1),%3\n"
" jnz 1b\n"
" j 5f\n"
"3: la %4,255(%1)\n" /* %4 = ptr + 255 */
" "LHI" %3,-4096\n"
" nr %4,%3\n" /* %4 = (ptr + 255) & -4096 */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 6f\n"
"4: mvcp 0(%4,%2),0(%1),%3\n"
" "SLR" %0,%4\n"
" j 6f\n"
"5:"SLR" %0,%0\n"
"6: \n"
EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: : "cc", "memory");
return size;
}
size_t copy_from_user_std_small(size_t size, const void __user *ptr, void *x)
{
unsigned long tmp1, tmp2;
tmp1 = 0UL;
asm volatile(
"0: mvcp 0(%0,%2),0(%1),%3\n"
" "SLR" %0,%0\n"
" j 3f\n"
"1: la %4,255(%1)\n" /* %4 = ptr + 255 */
" "LHI" %3,-4096\n"
" nr %4,%3\n" /* %4 = (ptr + 255) & -4096 */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 3f\n"
"2: mvcp 0(%4,%2),0(%1),%3\n"
" "SLR" %0,%4\n"
"3:\n"
EX_TABLE(0b,1b) EX_TABLE(2b,3b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: : "cc", "memory");
return size;
}
size_t copy_to_user_std(size_t size, void __user *ptr, const void *x)
{
unsigned long tmp1, tmp2;
tmp1 = -256UL;
asm volatile(
"0: mvcs 0(%0,%1),0(%2),%3\n"
" jz 5f\n"
"1:"ALR" %0,%3\n"
" la %1,256(%1)\n"
" la %2,256(%2)\n"
"2: mvcs 0(%0,%1),0(%2),%3\n"
" jnz 1b\n"
" j 5f\n"
"3: la %4,255(%1)\n" /* %4 = ptr + 255 */
" "LHI" %3,-4096\n"
" nr %4,%3\n" /* %4 = (ptr + 255) & -4096 */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 6f\n"
"4: mvcs 0(%4,%1),0(%2),%3\n"
" "SLR" %0,%4\n"
" j 6f\n"
"5:"SLR" %0,%0\n"
"6: \n"
EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: : "cc", "memory");
return size;
}
size_t copy_to_user_std_small(size_t size, void __user *ptr, const void *x)
{
unsigned long tmp1, tmp2;
tmp1 = 0UL;
asm volatile(
"0: mvcs 0(%0,%1),0(%2),%3\n"
" "SLR" %0,%0\n"
" j 3f\n"
"1: la %4,255(%1)\n" /* ptr + 255 */
" "LHI" %3,-4096\n"
" nr %4,%3\n" /* (ptr + 255) & -4096UL */
" "SLR" %4,%1\n"
" "CLR" %0,%4\n" /* copy crosses next page boundary? */
" jnh 3f\n"
"2: mvcs 0(%4,%1),0(%2),%3\n"
" "SLR" %0,%4\n"
"3:\n"
EX_TABLE(0b,1b) EX_TABLE(2b,3b)
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
: : "cc", "memory");
return size;
}
size_t copy_in_user_std(size_t size, void __user *to, const void __user *from)
{
unsigned long tmp1;
asm volatile(
" "AHI" %0,-1\n"
" jo 5f\n"
" sacf 256\n"
" bras %3,3f\n"
"0:"AHI" %0,257\n"
"1: mvc 0(1,%1),0(%2)\n"
" la %1,1(%1)\n"
" la %2,1(%2)\n"
" "AHI" %0,-1\n"
" jnz 1b\n"
" j 5f\n"
"2: mvc 0(256,%1),0(%2)\n"
" la %1,256(%1)\n"
" la %2,256(%2)\n"
"3:"AHI" %0,-256\n"
" jnm 2b\n"
"4: ex %0,1b-0b(%3)\n"
" sacf 0\n"
"5: "SLR" %0,%0\n"
"6:\n"
EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
: "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)
: : "cc", "memory");
return size;
}
size_t clear_user_std(size_t size, void __user *to)
{
unsigned long tmp1, tmp2;
asm volatile(
" "AHI" %0,-1\n"
" jo 5f\n"
" sacf 256\n"
" bras %3,3f\n"
" xc 0(1,%1),0(%1)\n"
"0:"AHI" %0,257\n"
" la %2,255(%1)\n" /* %2 = ptr + 255 */
" srl %2,12\n"
" sll %2,12\n" /* %2 = (ptr + 255) & -4096 */
" "SLR" %2,%1\n"
" "CLR" %0,%2\n" /* clear crosses next page boundary? */
" jnh 5f\n"
" "AHI" %2,-1\n"
"1: ex %2,0(%3)\n"
" "AHI" %2,1\n"
" "SLR" %0,%2\n"
" j 5f\n"
"2: xc 0(256,%1),0(%1)\n"
" la %1,256(%1)\n"
"3:"AHI" %0,-256\n"
" jnm 2b\n"
"4: ex %0,0(%3)\n"
" sacf 0\n"
"5: "SLR" %0,%0\n"
"6:\n"
EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
: "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)
: : "cc", "memory");
return size;
}
size_t strnlen_user_std(size_t size, const char __user *src)
{
register unsigned long reg0 asm("0") = 0UL;
unsigned long tmp1, tmp2;
asm volatile(
" la %2,0(%1)\n"
" la %3,0(%0,%1)\n"
" "SLR" %0,%0\n"
" sacf 256\n"
"0: srst %3,%2\n"
" jo 0b\n"
" la %0,1(%3)\n" /* strnlen_user results includes \0 */
" "SLR" %0,%1\n"
"1: sacf 0\n"
EX_TABLE(0b,1b)
: "+a" (size), "+a" (src), "=a" (tmp1), "=a" (tmp2)
: "d" (reg0) : "cc", "memory");
return size;
}
size_t strncpy_from_user_std(size_t size, const char __user *src, char *dst)
{
register unsigned long reg0 asm("0") = 0UL;
unsigned long tmp1, tmp2;
asm volatile(
" la %3,0(%1)\n"
" la %4,0(%0,%1)\n"
" sacf 256\n"
"0: srst %4,%3\n"
" jo 0b\n"
" sacf 0\n"
" la %0,0(%4)\n"
" jh 1f\n" /* found \0 in string ? */
" "AHI" %4,1\n" /* include \0 in copy */
"1:"SLR" %0,%1\n" /* %0 = return length (without \0) */
" "SLR" %4,%1\n" /* %4 = copy length (including \0) */
"2: mvcp 0(%4,%2),0(%1),%5\n"
" jz 9f\n"
"3:"AHI" %4,-256\n"
" la %1,256(%1)\n"
" la %2,256(%2)\n"
"4: mvcp 0(%4,%2),0(%1),%5\n"
" jnz 3b\n"
" j 9f\n"
"7: sacf 0\n"
"8:"LHI" %0,%6\n"
"9:\n"
EX_TABLE(0b,7b) EX_TABLE(2b,8b) EX_TABLE(4b,8b)
: "+a" (size), "+a" (src), "+d" (dst), "=a" (tmp1), "=a" (tmp2)
: "d" (reg0), "K" (-EFAULT) : "cc", "memory");
return size;
}
#define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \
asm volatile( \
" sacf 256\n" \
"0: l %1,0(%6)\n" \
"1:"insn \
"2: cs %1,%2,0(%6)\n" \
"3: jl 1b\n" \
" lhi %0,0\n" \
"4: sacf 0\n" \
EX_TABLE(0b,4b) EX_TABLE(2b,4b) EX_TABLE(3b,4b) \
: "=d" (ret), "=&d" (oldval), "=&d" (newval), \
"=m" (*uaddr) \
: "0" (-EFAULT), "d" (oparg), "a" (uaddr), \
"m" (*uaddr) : "cc");
int futex_atomic_op(int op, int __user *uaddr, int oparg, int *old)
{
int oldval = 0, newval, ret;
inc_preempt_count();
switch (op) {
case FUTEX_OP_SET:
__futex_atomic_op("lr %2,%5\n",
ret, oldval, newval, uaddr, oparg);
break;
case FUTEX_OP_ADD:
__futex_atomic_op("lr %2,%1\nar %2,%5\n",
ret, oldval, newval, uaddr, oparg);
break;
case FUTEX_OP_OR:
__futex_atomic_op("lr %2,%1\nor %2,%5\n",
ret, oldval, newval, uaddr, oparg);
break;
case FUTEX_OP_ANDN:
__futex_atomic_op("lr %2,%1\nnr %2,%5\n",
ret, oldval, newval, uaddr, oparg);
break;
case FUTEX_OP_XOR:
__futex_atomic_op("lr %2,%1\nxr %2,%5\n",
ret, oldval, newval, uaddr, oparg);
break;
default:
ret = -ENOSYS;
}
dec_preempt_count();
*old = oldval;
return ret;
}
int futex_atomic_cmpxchg(int __user *uaddr, int oldval, int newval)
{
int ret;
asm volatile(
" sacf 256\n"
" cs %1,%4,0(%5)\n"
"0: lr %0,%1\n"
"1: sacf 0\n"
EX_TABLE(0b,1b)
: "=d" (ret), "+d" (oldval), "=m" (*uaddr)
: "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr)
: "cc", "memory" );
return ret;
}
struct uaccess_ops uaccess_std = {
.copy_from_user = copy_from_user_std,
.copy_from_user_small = copy_from_user_std_small,
.copy_to_user = copy_to_user_std,
.copy_to_user_small = copy_to_user_std_small,
.copy_in_user = copy_in_user_std,
.clear_user = clear_user_std,
.strnlen_user = strnlen_user_std,
.strncpy_from_user = strncpy_from_user_std,
.futex_atomic_op = futex_atomic_op,
.futex_atomic_cmpxchg = futex_atomic_cmpxchg,
};
...@@ -52,22 +52,6 @@ static struct timer_list cmm_timer; ...@@ -52,22 +52,6 @@ static struct timer_list cmm_timer;
static void cmm_timer_fn(unsigned long); static void cmm_timer_fn(unsigned long);
static void cmm_set_timer(void); static void cmm_set_timer(void);
static long
cmm_strtoul(const char *cp, char **endp)
{
unsigned int base = 10;
if (*cp == '0') {
base = 8;
cp++;
if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
base = 16;
cp++;
}
}
return simple_strtoul(cp, endp, base);
}
static long static long
cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list) cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
{ {
...@@ -276,7 +260,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, ...@@ -276,7 +260,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
return -EFAULT; return -EFAULT;
buf[sizeof(buf) - 1] = '\0'; buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p); cmm_skip_blanks(buf, &p);
pages = cmm_strtoul(p, &p); pages = simple_strtoul(p, &p, 0);
if (ctl == &cmm_table[0]) if (ctl == &cmm_table[0])
cmm_set_pages(pages); cmm_set_pages(pages);
else else
...@@ -317,9 +301,9 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp, ...@@ -317,9 +301,9 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
return -EFAULT; return -EFAULT;
buf[sizeof(buf) - 1] = '\0'; buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p); cmm_skip_blanks(buf, &p);
pages = cmm_strtoul(p, &p); pages = simple_strtoul(p, &p, 0);
cmm_skip_blanks(p, &p); cmm_skip_blanks(p, &p);
seconds = cmm_strtoul(p, &p); seconds = simple_strtoul(p, &p, 0);
cmm_set_timeout(pages, seconds); cmm_set_timeout(pages, seconds);
} else { } else {
len = sprintf(buf, "%ld %ld\n", len = sprintf(buf, "%ld %ld\n",
...@@ -382,24 +366,24 @@ cmm_smsg_target(char *from, char *msg) ...@@ -382,24 +366,24 @@ cmm_smsg_target(char *from, char *msg)
if (strncmp(msg, "SHRINK", 6) == 0) { if (strncmp(msg, "SHRINK", 6) == 0) {
if (!cmm_skip_blanks(msg + 6, &msg)) if (!cmm_skip_blanks(msg + 6, &msg))
return; return;
pages = cmm_strtoul(msg, &msg); pages = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg); cmm_skip_blanks(msg, &msg);
if (*msg == '\0') if (*msg == '\0')
cmm_set_pages(pages); cmm_set_pages(pages);
} else if (strncmp(msg, "RELEASE", 7) == 0) { } else if (strncmp(msg, "RELEASE", 7) == 0) {
if (!cmm_skip_blanks(msg + 7, &msg)) if (!cmm_skip_blanks(msg + 7, &msg))
return; return;
pages = cmm_strtoul(msg, &msg); pages = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg); cmm_skip_blanks(msg, &msg);
if (*msg == '\0') if (*msg == '\0')
cmm_add_timed_pages(pages); cmm_add_timed_pages(pages);
} else if (strncmp(msg, "REUSE", 5) == 0) { } else if (strncmp(msg, "REUSE", 5) == 0) {
if (!cmm_skip_blanks(msg + 5, &msg)) if (!cmm_skip_blanks(msg + 5, &msg))
return; return;
pages = cmm_strtoul(msg, &msg); pages = simple_strtoul(msg, &msg, 0);
if (!cmm_skip_blanks(msg, &msg)) if (!cmm_skip_blanks(msg, &msg))
return; return;
seconds = cmm_strtoul(msg, &msg); seconds = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg); cmm_skip_blanks(msg, &msg);
if (*msg == '\0') if (*msg == '\0')
cmm_set_timeout(pages, seconds); cmm_set_timeout(pages, seconds);
......
...@@ -25,10 +25,12 @@ ...@@ -25,10 +25,12 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/kprobes.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/kdebug.h>
#ifndef CONFIG_64BIT #ifndef CONFIG_64BIT
#define __FAIL_ADDR_MASK 0x7ffff000 #define __FAIL_ADDR_MASK 0x7ffff000
...@@ -48,6 +50,38 @@ extern int sysctl_userprocess_debug; ...@@ -48,6 +50,38 @@ extern int sysctl_userprocess_debug;
extern void die(const char *,struct pt_regs *,long); extern void die(const char *,struct pt_regs *,long);
#ifdef CONFIG_KPROBES
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
int register_page_fault_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
}
int unregister_page_fault_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
}
static inline int notify_page_fault(enum die_val val, const char *str,
struct pt_regs *regs, long err, int trap, int sig)
{
struct die_args args = {
.regs = regs,
.str = str,
.err = err,
.trapnr = trap,
.signr = sig
};
return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
}
#else
static inline int notify_page_fault(enum die_val val, const char *str,
struct pt_regs *regs, long err, int trap, int sig)
{
return NOTIFY_DONE;
}
#endif
extern spinlock_t timerlist_lock; extern spinlock_t timerlist_lock;
/* /*
...@@ -159,7 +193,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code, ...@@ -159,7 +193,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
* 11 Page translation -> Not present (nullification) * 11 Page translation -> Not present (nullification)
* 3b Region third trans. -> Not present (nullification) * 3b Region third trans. -> Not present (nullification)
*/ */
static inline void static inline void __kprobes
do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
{ {
struct task_struct *tsk; struct task_struct *tsk;
...@@ -173,6 +207,10 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) ...@@ -173,6 +207,10 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
tsk = current; tsk = current;
mm = tsk->mm; mm = tsk->mm;
if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
SIGSEGV) == NOTIFY_STOP)
return;
/* /*
* Check for low-address protection. This needs to be treated * Check for low-address protection. This needs to be treated
* as a special case because the translation exception code * as a special case because the translation exception code
......
...@@ -108,16 +108,23 @@ void __init paging_init(void) ...@@ -108,16 +108,23 @@ void __init paging_init(void)
unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
static const int ssm_mask = 0x04000000L; static const int ssm_mask = 0x04000000L;
unsigned long ro_start_pfn, ro_end_pfn; unsigned long ro_start_pfn, ro_end_pfn;
unsigned long zones_size[MAX_NR_ZONES];
ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata); ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata);
ro_end_pfn = PFN_UP((unsigned long)&__end_rodata); ro_end_pfn = PFN_UP((unsigned long)&__end_rodata);
memset(zones_size, 0, sizeof(zones_size));
zones_size[ZONE_DMA] = max_low_pfn;
free_area_init_node(0, &contig_page_data, zones_size,
__pa(PAGE_OFFSET) >> PAGE_SHIFT,
zholes_size);
/* unmap whole virtual address space */ /* unmap whole virtual address space */
pg_dir = swapper_pg_dir; pg_dir = swapper_pg_dir;
for (i=0;i<KERNEL_PGD_PTRS;i++) for (i = 0; i < PTRS_PER_PGD; i++)
pmd_clear((pmd_t*)pg_dir++); pmd_clear((pmd_t *) pg_dir++);
/* /*
* map whole physical memory to virtual memory (identity mapping) * map whole physical memory to virtual memory (identity mapping)
...@@ -131,10 +138,7 @@ void __init paging_init(void) ...@@ -131,10 +138,7 @@ void __init paging_init(void)
*/ */
pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
pg_dir->pgd0 = (_PAGE_TABLE | __pa(pg_table)); pmd_populate_kernel(&init_mm, (pmd_t *) pg_dir, pg_table);
pg_dir->pgd1 = (_PAGE_TABLE | (__pa(pg_table)+1024));
pg_dir->pgd2 = (_PAGE_TABLE | (__pa(pg_table)+2048));
pg_dir->pgd3 = (_PAGE_TABLE | (__pa(pg_table)+3072));
pg_dir++; pg_dir++;
for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
...@@ -143,7 +147,7 @@ void __init paging_init(void) ...@@ -143,7 +147,7 @@ void __init paging_init(void)
else else
pte = pfn_pte(pfn, PAGE_KERNEL); pte = pfn_pte(pfn, PAGE_KERNEL);
if (pfn >= max_low_pfn) if (pfn >= max_low_pfn)
pte_clear(&init_mm, 0, &pte); pte_val(pte) = _PAGE_TYPE_EMPTY;
set_pte(pg_table, pte); set_pte(pg_table, pte);
pfn++; pfn++;
} }
...@@ -159,16 +163,6 @@ void __init paging_init(void) ...@@ -159,16 +163,6 @@ void __init paging_init(void)
: : "m" (pgdir_k), "m" (ssm_mask)); : : "m" (pgdir_k), "m" (ssm_mask));
local_flush_tlb(); local_flush_tlb();
{
unsigned long zones_size[MAX_NR_ZONES];
memset(zones_size, 0, sizeof(zones_size));
zones_size[ZONE_DMA] = max_low_pfn;
free_area_init_node(0, &contig_page_data, zones_size,
__pa(PAGE_OFFSET) >> PAGE_SHIFT,
zholes_size);
}
return; return;
} }
...@@ -236,10 +230,8 @@ void __init paging_init(void) ...@@ -236,10 +230,8 @@ void __init paging_init(void)
pte = pfn_pte(pfn, __pgprot(_PAGE_RO)); pte = pfn_pte(pfn, __pgprot(_PAGE_RO));
else else
pte = pfn_pte(pfn, PAGE_KERNEL); pte = pfn_pte(pfn, PAGE_KERNEL);
if (pfn >= max_low_pfn) { if (pfn >= max_low_pfn)
pte_clear(&init_mm, 0, &pte); pte_val(pte) = _PAGE_TYPE_EMPTY;
continue;
}
set_pte(pt_dir, pte); set_pte(pt_dir, pte);
pfn++; pfn++;
} }
......
/* /*
* hypervisor.c - /sys/hypervisor subsystem. * hypervisor.c - /sys/hypervisor subsystem.
* *
* This file is released under the GPLv2 * Copyright (C) IBM Corp. 2006
* *
* This file is released under the GPLv2
*/ */
#include <linux/kobject.h> #include <linux/kobject.h>
......
...@@ -213,17 +213,35 @@ config MONREADER ...@@ -213,17 +213,35 @@ config MONREADER
help help
Character device driver for reading z/VM monitor service records Character device driver for reading z/VM monitor service records
config MONWRITER
tristate "API for writing z/VM monitor service records"
default "m"
help
Character device driver for writing z/VM monitor service records
endmenu endmenu
menu "Cryptographic devices" menu "Cryptographic devices"
config Z90CRYPT config ZCRYPT
tristate "Support for PCI-attached cryptographic adapters" tristate "Support for PCI-attached cryptographic adapters"
select ZCRYPT_MONOLITHIC if ZCRYPT="y"
default "m" default "m"
help help
Select this option if you want to use a PCI-attached cryptographic Select this option if you want to use a PCI-attached cryptographic
adapter like the PCI Cryptographic Accelerator (PCICA) or the PCI adapter like:
Cryptographic Coprocessor (PCICC). This option is also available + PCI Cryptographic Accelerator (PCICA)
as a module called z90crypt.ko. + PCI Cryptographic Coprocessor (PCICC)
+ PCI-X Cryptographic Coprocessor (PCIXCC)
+ Crypto Express2 Coprocessor (CEX2C)
+ Crypto Express2 Accelerator (CEX2A)
config ZCRYPT_MONOLITHIC
bool "Monolithic zcrypt module"
depends on ZCRYPT="m"
help
Select this option if you want to have a single module z90crypt.ko
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
endmenu endmenu
...@@ -184,7 +184,7 @@ dasd_state_known_to_basic(struct dasd_device * device) ...@@ -184,7 +184,7 @@ dasd_state_known_to_basic(struct dasd_device * device)
device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
8 * sizeof (long)); 8 * sizeof (long));
debug_register_view(device->debug_area, &debug_sprintf_view); debug_register_view(device->debug_area, &debug_sprintf_view);
debug_set_level(device->debug_area, DBF_EMERG); debug_set_level(device->debug_area, DBF_WARNING);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
device->state = DASD_STATE_BASIC; device->state = DASD_STATE_BASIC;
...@@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) ...@@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->device;
if (device == NULL || if (device == NULL ||
device != dasd_device_from_cdev(cdev) || device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id); cdev->dev.bus_id);
...@@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* first of all check for state change pending interrupt */ /* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
if ((irb->scsw.dstat & mask) == mask) { if ((irb->scsw.dstat & mask) == mask) {
device = dasd_device_from_cdev(cdev); device = dasd_device_from_cdev_locked(cdev);
if (!IS_ERR(device)) { if (!IS_ERR(device)) {
dasd_handle_state_change_pending(device); dasd_handle_state_change_pending(device);
dasd_put_device(device); dasd_put_device(device);
...@@ -2169,7 +2169,7 @@ dasd_init(void) ...@@ -2169,7 +2169,7 @@ dasd_init(void)
goto failed; goto failed;
} }
debug_register_view(dasd_debug_area, &debug_sprintf_view); debug_register_view(dasd_debug_area, &debug_sprintf_view);
debug_set_level(dasd_debug_area, DBF_EMERG); debug_set_level(dasd_debug_area, DBF_WARNING);
DBF_EVENT(DBF_EMERG, "%s", "debug area created"); DBF_EVENT(DBF_EMERG, "%s", "debug area created");
......
...@@ -258,8 +258,12 @@ dasd_parse_keyword( char *parsestring ) { ...@@ -258,8 +258,12 @@ dasd_parse_keyword( char *parsestring ) {
return residual_str; return residual_str;
} }
if (strncmp("nopav", parsestring, length) == 0) { if (strncmp("nopav", parsestring, length) == 0) {
if (MACHINE_IS_VM)
MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM");
else {
dasd_nopav = 1; dasd_nopav = 1;
MESSAGE(KERN_INFO, "%s", "disable PAV mode"); MESSAGE(KERN_INFO, "%s", "disable PAV mode");
}
return residual_str; return residual_str;
} }
if (strncmp("fixedbuffers", parsestring, length) == 0) { if (strncmp("fixedbuffers", parsestring, length) == 0) {
...@@ -523,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev) ...@@ -523,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev)
{ {
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
struct dasd_device *device; struct dasd_device *device;
unsigned long flags;
int rc; int rc;
devmap = dasd_devmap_from_cdev(cdev); devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap)) if (IS_ERR(devmap))
return (void *) devmap; return (void *) devmap;
cdev->dev.driver_data = devmap;
device = dasd_alloc_device(); device = dasd_alloc_device();
if (IS_ERR(device)) if (IS_ERR(device))
return device; return device;
atomic_set(&device->ref_count, 2); atomic_set(&device->ref_count, 3);
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
if (!devmap->device) { if (!devmap->device) {
...@@ -552,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev) ...@@ -552,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev)
dasd_free_device(device); dasd_free_device(device);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
cdev->dev.driver_data = device;
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return device; return device;
} }
...@@ -569,6 +578,7 @@ dasd_delete_device(struct dasd_device *device) ...@@ -569,6 +578,7 @@ dasd_delete_device(struct dasd_device *device)
{ {
struct ccw_device *cdev; struct ccw_device *cdev;
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
unsigned long flags;
/* First remove device pointer from devmap. */ /* First remove device pointer from devmap. */
devmap = dasd_find_busid(device->cdev->dev.bus_id); devmap = dasd_find_busid(device->cdev->dev.bus_id);
...@@ -582,9 +592,16 @@ dasd_delete_device(struct dasd_device *device) ...@@ -582,9 +592,16 @@ dasd_delete_device(struct dasd_device *device)
devmap->device = NULL; devmap->device = NULL;
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
/* Drop ref_count by 2, one for the devmap reference and /* Disconnect dasd_device structure from ccw_device structure. */
* one for the passed reference. */ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
atomic_sub(2, &device->ref_count); device->cdev->dev.driver_data = NULL;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
/*
* Drop ref_count by 3, one for the devmap reference, one for
* the cdev reference and one for the passed reference.
*/
atomic_sub(3, &device->ref_count);
/* Wait for reference counter to drop to zero. */ /* Wait for reference counter to drop to zero. */
wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0); wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
...@@ -593,9 +610,6 @@ dasd_delete_device(struct dasd_device *device) ...@@ -593,9 +610,6 @@ dasd_delete_device(struct dasd_device *device)
cdev = device->cdev; cdev = device->cdev;
device->cdev = NULL; device->cdev = NULL;
/* Disconnect dasd_devmap structure from ccw_device structure. */
cdev->dev.driver_data = NULL;
/* Put ccw_device structure. */ /* Put ccw_device structure. */
put_device(&cdev->dev); put_device(&cdev->dev);
...@@ -613,23 +627,34 @@ dasd_put_device_wake(struct dasd_device *device) ...@@ -613,23 +627,34 @@ dasd_put_device_wake(struct dasd_device *device)
wake_up(&dasd_delete_wq); wake_up(&dasd_delete_wq);
} }
/*
* Return dasd_device structure associated with cdev.
* This function needs to be called with the ccw device
* lock held. It can be used from interrupt context.
*/
struct dasd_device *
dasd_device_from_cdev_locked(struct ccw_device *cdev)
{
struct dasd_device *device = cdev->dev.driver_data;
if (!device)
return ERR_PTR(-ENODEV);
dasd_get_device(device);
return device;
}
/* /*
* Return dasd_device structure associated with cdev. * Return dasd_device structure associated with cdev.
*/ */
struct dasd_device * struct dasd_device *
dasd_device_from_cdev(struct ccw_device *cdev) dasd_device_from_cdev(struct ccw_device *cdev)
{ {
struct dasd_devmap *devmap;
struct dasd_device *device; struct dasd_device *device;
unsigned long flags;
device = ERR_PTR(-ENODEV); spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
spin_lock(&dasd_devmap_lock); device = dasd_device_from_cdev_locked(cdev);
devmap = cdev->dev.driver_data; spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
if (devmap && devmap->device) {
device = devmap->device;
dasd_get_device(device);
}
spin_unlock(&dasd_devmap_lock);
return device; return device;
} }
...@@ -730,16 +755,17 @@ static ssize_t ...@@ -730,16 +755,17 @@ static ssize_t
dasd_discipline_show(struct device *dev, struct device_attribute *attr, dasd_discipline_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct dasd_devmap *devmap; struct dasd_device *device;
char *dname; ssize_t len;
spin_lock(&dasd_devmap_lock); device = dasd_device_from_cdev(to_ccwdev(dev));
dname = "none"; if (!IS_ERR(device) && device->discipline) {
devmap = dev->driver_data; len = snprintf(buf, PAGE_SIZE, "%s\n",
if (devmap && devmap->device && devmap->device->discipline) device->discipline->name);
dname = devmap->device->discipline->name; dasd_put_device(device);
spin_unlock(&dasd_devmap_lock); } else
return snprintf(buf, PAGE_SIZE, "%s\n", dname); len = snprintf(buf, PAGE_SIZE, "none\n");
return len;
} }
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
......
...@@ -678,7 +678,7 @@ int __init dasd_eer_init(void) ...@@ -678,7 +678,7 @@ int __init dasd_eer_init(void)
return 0; return 0;
} }
void __exit dasd_eer_exit(void) void dasd_eer_exit(void)
{ {
WARN_ON(misc_deregister(&dasd_eer_dev) != 0); WARN_ON(misc_deregister(&dasd_eer_dev) != 0);
} }
...@@ -534,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *); ...@@ -534,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *);
void dasd_remove_sysfs_files(struct ccw_device *); void dasd_remove_sysfs_files(struct ccw_device *);
struct dasd_device *dasd_device_from_cdev(struct ccw_device *); struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int); struct dasd_device *dasd_device_from_devindex(int);
int dasd_parse(void); int dasd_parse(void);
......
...@@ -453,7 +453,7 @@ static int __init xpram_init(void) ...@@ -453,7 +453,7 @@ static int __init xpram_init(void)
PRINT_WARN("No expanded memory available\n"); PRINT_WARN("No expanded memory available\n");
return -ENODEV; return -ENODEV;
} }
xpram_pages = xpram_highest_page_index(); xpram_pages = xpram_highest_page_index() + 1;
PRINT_INFO(" %u pages expanded memory found (%lu KB).\n", PRINT_INFO(" %u pages expanded memory found (%lu KB).\n",
xpram_pages, (unsigned long) xpram_pages*4); xpram_pages, (unsigned long) xpram_pages*4);
rc = xpram_setup_sizes(xpram_pages); rc = xpram_setup_sizes(xpram_pages);
......
...@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o ...@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
/*
* drivers/s390/char/monwriter.c
*
* Character device driver for writing z/VM *MONITOR service records.
*
* Copyright (C) IBM Corp. 2006
*
* Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/appldata.h>
#include <asm/monwriter.h>
#define MONWRITE_MAX_DATALEN 4024
static int mon_max_bufs = 255;
struct mon_buf {
struct list_head list;
struct monwrite_hdr hdr;
int diag_done;
char *data;
};
struct mon_private {
struct list_head list;
struct monwrite_hdr hdr;
size_t hdr_to_read;
size_t data_to_read;
struct mon_buf *current_buf;
int mon_buf_count;
};
/*
* helper functions
*/
static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
{
struct appldata_product_id id;
int rc;
strcpy(id.prod_nr, "LNXAPPL");
id.prod_fn = myhdr->applid;
id.record_nr = myhdr->record_num;
id.version_nr = myhdr->version;
id.release_nr = myhdr->release;
id.mod_lvl = myhdr->mod_level;
rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
if (rc <= 0)
return rc;
if (rc == 5)
return -EPERM;
printk("DIAG X'DC' error with return code: %i\n", rc);
return -EINVAL;
}
static inline struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
struct monwrite_hdr *monhdr)
{
struct mon_buf *entry, *next;
list_for_each_entry_safe(entry, next, &monpriv->list, list)
if (entry->hdr.applid == monhdr->applid &&
entry->hdr.record_num == monhdr->record_num &&
entry->hdr.version == monhdr->version &&
entry->hdr.release == monhdr->release &&
entry->hdr.mod_level == monhdr->mod_level)
return entry;
return NULL;
}
static int monwrite_new_hdr(struct mon_private *monpriv)
{
struct monwrite_hdr *monhdr = &monpriv->hdr;
struct mon_buf *monbuf;
int rc;
if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
monhdr->mon_function > MONWRITE_START_CONFIG ||
monhdr->hdrlen != sizeof(struct monwrite_hdr))
return -EINVAL;
monbuf = monwrite_find_hdr(monpriv, monhdr);
if (monbuf) {
if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
monhdr->datalen = monbuf->hdr.datalen;
rc = monwrite_diag(monhdr, monbuf->data,
APPLDATA_STOP_REC);
list_del(&monbuf->list);
monpriv->mon_buf_count--;
kfree(monbuf->data);
kfree(monbuf);
monbuf = NULL;
}
} else {
if (monpriv->mon_buf_count >= mon_max_bufs)
return -ENOSPC;
monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
if (!monbuf)
return -ENOMEM;
monbuf->data = kzalloc(monbuf->hdr.datalen,
GFP_KERNEL | GFP_DMA);
if (!monbuf->data) {
kfree(monbuf);
return -ENOMEM;
}
monbuf->hdr = *monhdr;
list_add_tail(&monbuf->list, &monpriv->list);
monpriv->mon_buf_count++;
}
monpriv->current_buf = monbuf;
return 0;
}
static int monwrite_new_data(struct mon_private *monpriv)
{
struct monwrite_hdr *monhdr = &monpriv->hdr;
struct mon_buf *monbuf = monpriv->current_buf;
int rc = 0;
switch (monhdr->mon_function) {
case MONWRITE_START_INTERVAL:
if (!monbuf->diag_done) {
rc = monwrite_diag(monhdr, monbuf->data,
APPLDATA_START_INTERVAL_REC);
monbuf->diag_done = 1;
}
break;
case MONWRITE_START_CONFIG:
if (!monbuf->diag_done) {
rc = monwrite_diag(monhdr, monbuf->data,
APPLDATA_START_CONFIG_REC);
monbuf->diag_done = 1;
}
break;
case MONWRITE_GEN_EVENT:
rc = monwrite_diag(monhdr, monbuf->data,
APPLDATA_GEN_EVENT_REC);
list_del(&monpriv->current_buf->list);
kfree(monpriv->current_buf->data);
kfree(monpriv->current_buf);
monpriv->current_buf = NULL;
break;
default:
/* monhdr->mon_function is checked in monwrite_new_hdr */
BUG();
}
return rc;
}
/*
* file operations
*/
static int monwrite_open(struct inode *inode, struct file *filp)
{
struct mon_private *monpriv;
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
if (!monpriv)
return -ENOMEM;
INIT_LIST_HEAD(&monpriv->list);
monpriv->hdr_to_read = sizeof(monpriv->hdr);
filp->private_data = monpriv;
return nonseekable_open(inode, filp);
}
static int monwrite_close(struct inode *inode, struct file *filp)
{
struct mon_private *monpriv = filp->private_data;
struct mon_buf *entry, *next;
list_for_each_entry_safe(entry, next, &monpriv->list, list) {
if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
monwrite_diag(&entry->hdr, entry->data,
APPLDATA_STOP_REC);
monpriv->mon_buf_count--;
list_del(&entry->list);
kfree(entry->data);
kfree(entry);
}
kfree(monpriv);
return 0;
}
static ssize_t monwrite_write(struct file *filp, const char __user *data,
size_t count, loff_t *ppos)
{
struct mon_private *monpriv = filp->private_data;
size_t len, written;
void *to;
int rc;
for (written = 0; written < count; ) {
if (monpriv->hdr_to_read) {
len = min(count - written, monpriv->hdr_to_read);
to = (char *) &monpriv->hdr +
sizeof(monpriv->hdr) - monpriv->hdr_to_read;
if (copy_from_user(to, data + written, len)) {
rc = -EFAULT;
goto out_error;
}
monpriv->hdr_to_read -= len;
written += len;
if (monpriv->hdr_to_read > 0)
continue;
rc = monwrite_new_hdr(monpriv);
if (rc)
goto out_error;
monpriv->data_to_read = monpriv->current_buf ?
monpriv->current_buf->hdr.datalen : 0;
}
if (monpriv->data_to_read) {
len = min(count - written, monpriv->data_to_read);
to = monpriv->current_buf->data +
monpriv->hdr.datalen - monpriv->data_to_read;
if (copy_from_user(to, data + written, len)) {
rc = -EFAULT;
goto out_error;
}
monpriv->data_to_read -= len;
written += len;
if (monpriv->data_to_read > 0)
continue;
rc = monwrite_new_data(monpriv);
if (rc)
goto out_error;
}
monpriv->hdr_to_read = sizeof(monpriv->hdr);
}
return written;
out_error:
monpriv->data_to_read = 0;
monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
return rc;
}
static struct file_operations monwrite_fops = {
.owner = THIS_MODULE,
.open = &monwrite_open,
.release = &monwrite_close,
.write = &monwrite_write,
};
static struct miscdevice mon_dev = {
.name = "monwriter",
.fops = &monwrite_fops,
.minor = MISC_DYNAMIC_MINOR,
};
/*
* module init/exit
*/
static int __init mon_init(void)
{
if (MACHINE_IS_VM)
return misc_register(&mon_dev);
else
return -ENODEV;
}
static void __exit mon_exit(void)
{
WARN_ON(misc_deregister(&mon_dev) != 0);
}
module_init(mon_init);
module_exit(mon_exit);
module_param_named(max_bufs, mon_max_bufs, int, 0644);
MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers"
"that can be active at one time");
MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
MODULE_DESCRIPTION("Character device driver for writing z/VM "
"APPLDATA monitor records.");
MODULE_LICENSE("GPL");
/* /*
* Copyright (C) 2004,2005 IBM Corporation * Copyright (C) 2004,2005 IBM Corporation
* Interface implementation for communication with the v/VM control program * Interface implementation for communication with the z/VM control program
* Author(s): Christian Borntraeger <cborntra@de.ibm.com> * Author(s): Christian Borntraeger <cborntra@de.ibm.com>
* *
* *
......
/* /*
* Copyright (C) 2004, 2005 IBM Corporation * Copyright (C) 2004, 2005 IBM Corporation
* Interface implementation for communication with the v/VM control program * Interface implementation for communication with the z/VM control program
* Version 1.0 * Version 1.0
* Author(s): Christian Borntraeger <cborntra@de.ibm.com> * Author(s): Christian Borntraeger <cborntra@de.ibm.com>
* *
......
...@@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) ...@@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* trigger path verification. */ /* trigger path verification. */
if (sch->driver && sch->driver->verify) if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev); sch->driver->verify(&sch->dev);
else if (sch->vpm == mask) else if (sch->lpm == mask)
goto out_unreg; goto out_unreg;
out_unlock: out_unlock:
spin_unlock_irq(&sch->lock); spin_unlock_irq(&sch->lock);
...@@ -378,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) ...@@ -378,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
if (chp_mask == 0) { if (chp_mask == 0) {
spin_unlock_irq(&sch->lock); spin_unlock_irq(&sch->lock);
put_device(&sch->dev);
return 0; return 0;
} }
old_lpm = sch->lpm; old_lpm = sch->lpm;
...@@ -392,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) ...@@ -392,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
spin_unlock_irq(&sch->lock); spin_unlock_irq(&sch->lock);
put_device(&sch->dev); put_device(&sch->dev);
return (res_data->fla_mask == 0xffff) ? -ENODEV : 0; return 0;
} }
......
...@@ -16,11 +16,10 @@ ...@@ -16,11 +16,10 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/cio.h> #include <asm/cio.h>
#include <asm/delay.h> #include <asm/delay.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/setup.h>
#include "airq.h" #include "airq.h"
#include "cio.h" #include "cio.h"
#include "css.h" #include "css.h"
...@@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ ...@@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
sch->orb.pfch = sch->options.prefetch == 0; sch->orb.pfch = sch->options.prefetch == 0;
sch->orb.spnd = sch->options.suspend; sch->orb.spnd = sch->options.suspend;
sch->orb.ssic = sch->options.suspend && sch->options.inter; sch->orb.ssic = sch->options.suspend && sch->options.inter;
sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm; sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
/* /*
* for 64 bit we always support 64 bit IDAWs with 4k page size only * for 64 bit we always support 64 bit IDAWs with 4k page size only
...@@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) ...@@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
sch->opm = 0xff; sch->opm = 0xff;
if (!cio_is_console(sch->schid)) if (!cio_is_console(sch->schid))
chsc_validate_chpids(sch); chsc_validate_chpids(sch);
sch->lpm = sch->schib.pmcw.pim & sch->lpm = sch->schib.pmcw.pam & sch->opm;
sch->schib.pmcw.pam &
sch->schib.pmcw.pom &
sch->opm;
CIO_DEBUG(KERN_INFO, 0, CIO_DEBUG(KERN_INFO, 0,
"Detected device %04x on subchannel 0.%x.%04X" "Detected device %04x on subchannel 0.%x.%04X"
...@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid) ...@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid)
return -EBUSY; return -EBUSY;
} }
extern void do_reipl(unsigned long devno); struct sch_match_id {
static int struct subchannel_id schid;
__shutdown_subchannel_easy(struct subchannel_id schid, void *data) struct ccw_dev_id devid;
int rc;
};
static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid,
void *data)
{ {
struct schib schib; struct schib schib;
struct sch_match_id *match_id = data;
if (stsch_err(schid, &schib)) if (stsch_err(schid, &schib))
return -ENXIO; return -ENXIO;
if (match_id && schib.pmcw.dnv &&
(schib.pmcw.dev == match_id->devid.devno) &&
(schid.ssid == match_id->devid.ssid)) {
match_id->schid = schid;
match_id->rc = 0;
}
if (!schib.pmcw.ena) if (!schib.pmcw.ena)
return 0; return 0;
switch(__disable_subchannel_easy(schid, &schib)) { switch(__disable_subchannel_easy(schid, &schib)) {
...@@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data) ...@@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
return 0; return 0;
} }
void static int clear_all_subchannels_and_match(struct ccw_dev_id *devid,
clear_all_subchannels(void) struct subchannel_id *schid)
{ {
struct sch_match_id match_id;
match_id.devid = *devid;
match_id.rc = -ENODEV;
local_irq_disable(); local_irq_disable();
for_each_subchannel(__shutdown_subchannel_easy, NULL); for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id);
if (match_id.rc == 0)
*schid = match_id.schid;
return match_id.rc;
} }
void clear_all_subchannels(void)
{
local_irq_disable();
for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL);
}
extern void do_reipl_asm(__u32 schid);
/* Make sure all subchannels are quiet before we re-ipl an lpar. */ /* Make sure all subchannels are quiet before we re-ipl an lpar. */
void void reipl_ccw_dev(struct ccw_dev_id *devid)
reipl(unsigned long devno)
{ {
clear_all_subchannels(); struct subchannel_id schid;
if (clear_all_subchannels_and_match(devid, &schid))
panic("IPL Device not found\n");
cio_reset_channel_paths(); cio_reset_channel_paths();
do_reipl(devno); do_reipl_asm(*((__u32*)&schid));
}
extern struct schib ipl_schib;
/*
* ipl_save_parameters gets called very early. It is not allowed to access
* anything in the bss section at all. The bss section is not cleared yet,
* but may contain some ipl parameters written by the firmware.
* These parameters (if present) are copied to 0x2000.
* To avoid corruption of the ipl parameters, all variables used by this
* function must reside on the stack or in the data section.
*/
void ipl_save_parameters(void)
{
struct subchannel_id schid;
unsigned int *ipl_ptr;
void *src, *dst;
schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
if (!schid.one)
return;
if (stsch(schid, &ipl_schib))
return;
if (!ipl_schib.pmcw.dnv)
return;
ipl_devno = ipl_schib.pmcw.dev;
ipl_flags |= IPL_DEVNO_VALID;
if (!ipl_schib.pmcw.qf)
return;
ipl_flags |= IPL_PARMBLOCK_VALID;
ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
src = (void *)(unsigned long)*ipl_ptr;
dst = (void *)IPL_PARMBLOCK_ORIGIN;
memmove(dst, src, PAGE_SIZE);
*ipl_ptr = IPL_PARMBLOCK_ORIGIN;
} }
...@@ -182,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid) ...@@ -182,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid)
return dev ? to_subchannel(dev) : NULL; return dev ? to_subchannel(dev) : NULL;
} }
static inline int css_get_subchannel_status(struct subchannel *sch)
static inline int
css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid)
{ {
struct schib schib; struct schib schib;
int cc;
cc = stsch(schid, &schib); if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
if (cc)
return CIO_GONE;
if (!schib.pmcw.dnv)
return CIO_GONE; return CIO_GONE;
if (sch && sch->schib.pmcw.dnv && if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
(schib.pmcw.dev != sch->schib.pmcw.dev))
return CIO_REVALIDATE; return CIO_REVALIDATE;
if (sch && !sch->lpm) if (!sch->lpm)
return CIO_NO_PATH; return CIO_NO_PATH;
return CIO_OPER; return CIO_OPER;
} }
static int static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
css_evaluate_subchannel(struct subchannel_id schid, int slow)
{ {
int event, ret, disc; int event, ret, disc;
struct subchannel *sch;
unsigned long flags; unsigned long flags;
enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
sch = get_subchannel_by_schid(schid); spin_lock_irqsave(&sch->lock, flags);
disc = sch ? device_is_disconnected(sch) : 0; disc = device_is_disconnected(sch);
if (disc && slow) { if (disc && slow) {
if (sch) /* Disconnected devices are evaluated directly only.*/
put_device(&sch->dev); spin_unlock_irqrestore(&sch->lock, flags);
return 0; /* Already processed. */ return 0;
} }
/* /* No interrupt after machine check - kill pending timers. */
* We've got a machine check, so running I/O won't get an interrupt.
* Kill any pending timers.
*/
if (sch)
device_kill_pending_timer(sch); device_kill_pending_timer(sch);
if (!disc && !slow) { if (!disc && !slow) {
if (sch) /* Non-disconnected devices are evaluated on the slow path. */
put_device(&sch->dev); spin_unlock_irqrestore(&sch->lock, flags);
return -EAGAIN; /* Will be done on the slow path. */ return -EAGAIN;
} }
event = css_get_subchannel_status(sch, schid); event = css_get_subchannel_status(sch);
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
schid.ssid, schid.sch_no, event, sch->schid.ssid, sch->schid.sch_no, event,
sch?(disc?"disconnected":"normal"):"unknown", disc ? "disconnected" : "normal",
slow?"slow":"fast"); slow ? "slow" : "fast");
/* Analyze subchannel status. */
action = NONE;
switch (event) { switch (event) {
case CIO_NO_PATH: case CIO_NO_PATH:
case CIO_GONE: if (disc) {
if (!sch) { /* Check if paths have become available. */
/* Never used this subchannel. Ignore. */ action = REPROBE;
ret = 0;
break;
}
if (disc && (event == CIO_NO_PATH)) {
/*
* Uargh, hack again. Because we don't get a machine
* check on configure on, our path bookkeeping can
* be out of date here (it's fine while we only do
* logical varying or get chsc machine checks). We
* need to force reprobing or we might miss devices
* coming operational again. It won't do harm in real
* no path situations.
*/
spin_lock_irqsave(&sch->lock, flags);
device_trigger_reprobe(sch);
spin_unlock_irqrestore(&sch->lock, flags);
ret = 0;
break; break;
} }
if (sch->driver && sch->driver->notify && /* fall through */
sch->driver->notify(&sch->dev, event)) { case CIO_GONE:
/* Prevent unwanted effects when opening lock. */
cio_disable_subchannel(sch); cio_disable_subchannel(sch);
device_set_disconnected(sch); device_set_disconnected(sch);
ret = 0; /* Ask driver what to do with device. */
break; action = UNREGISTER;
if (sch->driver && sch->driver->notify) {
spin_unlock_irqrestore(&sch->lock, flags);
ret = sch->driver->notify(&sch->dev, event);
spin_lock_irqsave(&sch->lock, flags);
if (ret)
action = NONE;
} }
/*
* Unregister subchannel.
* The device will be killed automatically.
*/
cio_disable_subchannel(sch);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&sch->dev);
ret = 0;
break; break;
case CIO_REVALIDATE: case CIO_REVALIDATE:
/* /* Device will be removed, so no notify necessary. */
* Revalidation machine check. Sick. if (disc)
* We don't notify the driver since we have to throw the device /* Reprobe because immediate unregister might block. */
* away in any case. action = REPROBE;
*/ else
if (!disc) { action = UNREGISTER_PROBE;
break;
case CIO_OPER:
if (disc)
/* Get device operational again. */
action = REPROBE;
break;
}
/* Perform action. */
ret = 0;
switch (action) {
case UNREGISTER:
case UNREGISTER_PROBE:
/* Unregister device (will use subchannel lock). */
spin_unlock_irqrestore(&sch->lock, flags);
css_sch_device_unregister(sch); css_sch_device_unregister(sch);
spin_lock_irqsave(&sch->lock, flags);
/* Reset intparm to zeroes. */ /* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0; sch->schib.pmcw.intparm = 0;
cio_modify(sch); cio_modify(sch);
put_device(&sch->dev);
ret = css_probe_device(schid); /* Probe if necessary. */
} else { if (action == UNREGISTER_PROBE)
/* ret = css_probe_device(sch->schid);
* We can't immediately deregister the disconnected
* device since it might block.
*/
spin_lock_irqsave(&sch->lock, flags);
device_trigger_reprobe(sch);
spin_unlock_irqrestore(&sch->lock, flags);
ret = 0;
}
break; break;
case CIO_OPER: case REPROBE:
if (disc) {
spin_lock_irqsave(&sch->lock, flags);
/* Get device operational again. */
device_trigger_reprobe(sch); device_trigger_reprobe(sch);
spin_unlock_irqrestore(&sch->lock, flags);
}
ret = sch ? 0 : css_probe_device(schid);
break; break;
default: default:
BUG(); break;
ret = 0;
} }
spin_unlock_irqrestore(&sch->lock, flags);
return ret;
}
static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
{
struct schib schib;
if (!slow) {
/* Will be done on the slow path. */
return -EAGAIN;
}
if (stsch(schid, &schib) || !schib.pmcw.dnv) {
/* Unusable - ignore. */
return 0;
}
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
"slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
return css_probe_device(schid);
}
static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
{
struct subchannel *sch;
int ret;
sch = get_subchannel_by_schid(schid);
if (sch) {
ret = css_evaluate_known_subchannel(sch, slow);
put_device(&sch->dev);
} else
ret = css_evaluate_new_subchannel(schid, slow);
return ret; return ret;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -115,7 +115,7 @@ qdio_min(int a,int b) ...@@ -115,7 +115,7 @@ qdio_min(int a,int b)
static inline __u64 static inline __u64
qdio_get_micros(void) qdio_get_micros(void)
{ {
return (get_clock() >> 10); /* time>>12 is microseconds */ return (get_clock() >> 12); /* time>>12 is microseconds */
} }
/* /*
...@@ -1129,7 +1129,7 @@ qdio_get_inbound_buffer_frontier(struct qdio_q *q) ...@@ -1129,7 +1129,7 @@ qdio_get_inbound_buffer_frontier(struct qdio_q *q)
#ifdef QDIO_USE_PROCESSING_STATE #ifdef QDIO_USE_PROCESSING_STATE
if (last_position>=0) if (last_position>=0)
set_slsb(q, &last_position, SLSB_P_INPUT_NOT_INIT, &count); set_slsb(q, &last_position, SLSB_P_INPUT_PROCESSING, &count);
#endif /* QDIO_USE_PROCESSING_STATE */ #endif /* QDIO_USE_PROCESSING_STATE */
QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int)); QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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