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
S390
P: Martin Schwidefsky
M: schwidefsky@de.ibm.com
P: Heiko Carstens
M: heiko.carstens@de.ibm.com
M: linux390@de.ibm.com
L: linux-390@vm.marist.edu
W: http://www.ibm.com/developerworks/linux/linux390/
......
......@@ -460,8 +460,7 @@ config S390_HYPFS_FS
information in an s390 hypervisor environment.
config KEXEC
bool "kexec system call (EXPERIMENTAL)"
depends on EXPERIMENTAL
bool "kexec system call"
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
......@@ -487,8 +486,22 @@ source "drivers/net/Kconfig"
source "fs/Kconfig"
menu "Instrumentation Support"
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 "security/Kconfig"
......
......@@ -29,22 +29,6 @@
#define CTL_APPLDATA_NET_SUM 2125
#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_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x)
#define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x)
......
......@@ -14,20 +14,20 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/page-flags.h>
#include <linux/swap.h>
#include <linux/pagemap.h>
#include <linux/sysctl.h>
#include <asm/timer.h>
//#include <linux/kernel_stat.h>
#include <linux/notifier.h>
#include <linux/cpu.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"
......@@ -39,34 +39,6 @@
#define TOD_MICRO 0x01000 /* nr. of TOD clock units
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)
*/
......@@ -181,46 +153,17 @@ static void appldata_work_fn(void *data)
int appldata_diag(char record_nr, u16 function, unsigned long buffer,
u16 length, char *mod_lvl)
{
unsigned long ry;
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 */
struct appldata_product_id id = {
.prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
0xE7, 0xD2, 0xD9}, /* "LINUXKR" */
.prod_fn = {0xD5, 0xD3}, /* "NL" */
0xE7, 0xD2, 0xD9}, /* "LINUXKR" */
.prod_fn = 0xD5D3, /* "NL" */
.record_nr = record_nr,
.version_nr = {0xF2, 0xF6}, /* "26" */
.release_nr = {0xF0, 0xF1}, /* "01" */
.mod_lvl = {mod_lvl[0], 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)
.version_nr = 0xF2F6, /* "26" */
.release_nr = 0xF0F1, /* "01" */
.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1],
};
if (!MACHINE_IS_VM)
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;
return appldata_asm(&id, function, (void *) buffer, length);
}
/************************ timer, work, DIAG <END> ****************************/
......
......@@ -16,6 +16,7 @@
#include <linux/kernel_stat.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <asm/appldata.h>
#include <asm/smp.h>
#include "appldata.h"
......
......@@ -428,6 +428,7 @@ CONFIG_S390_TAPE_34XX=m
# CONFIG_VMLOGRDR is not set
# CONFIG_VMCP is not set
# CONFIG_MONREADER is not set
CONFIG_MONWRITER=m
#
# Cryptographic devices
......
/*
* fs/hypfs/hypfs.h
* arch/s390/hypfs/hypfs.h
* Hypervisor filesystem for Linux on s390.
*
* 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
* implementation.
*
......@@ -432,12 +432,14 @@ static int diag204_probe(void)
buf = diag204_get_buffer(INFO_EXT, &pages);
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_info_type = INFO_EXT;
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_info_type = INFO_EXT;
goto out;
......@@ -452,7 +454,8 @@ static int diag204_probe(void)
rc = PTR_ERR(buf);
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_info_type = INFO_SIMPLE;
goto out;
......@@ -476,7 +479,8 @@ static void *diag204_store(void)
buf = diag204_get_buffer(diag204_info_type, &pages);
if (IS_ERR(buf))
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);
out:
return buf;
......@@ -531,7 +535,7 @@ __init int hypfs_diag_init(void)
return rc;
}
__exit void hypfs_diag_exit(void)
void hypfs_diag_exit(void)
{
diag224_delete_name_table();
diag204_free_buffer();
......
/*
* fs/hypfs/hypfs_diag.h
* arch/s390/hypfs_diag.h
* Hypervisor filesystem for Linux on s390.
*
* Copyright (C) IBM Corp. 2006
......
/*
* fs/hypfs/inode.c
* arch/s390/hypfs/inode.c
* Hypervisor filesystem for Linux on s390.
*
* Copyright (C) IBM Corp. 2006
......@@ -312,10 +312,12 @@ static void hypfs_kill_super(struct super_block *sb)
{
struct hypfs_sb_info *sb_info = sb->s_fs_info;
hypfs_delete_tree(sb->s_root);
hypfs_remove(sb_info->update_file);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
if (sb->s_root) {
hypfs_delete_tree(sb->s_root);
hypfs_remove(sb_info->update_file);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
}
kill_litter_super(sb);
}
......
......@@ -6,7 +6,7 @@ EXTRA_AFLAGS := -traditional
obj-y := bitmap.o traps.o time.o process.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),reipl64.o,reipl.o)
......@@ -24,6 +24,7 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
obj-$(CONFIG_VIRT_TIMER) += vtime.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_KPROBES) += kprobes.o
# Kexec part
S390_KEXEC_OBJS := machine_kexec.o crash.o
......
......@@ -505,6 +505,8 @@ pgm_no_vtime2:
mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
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
la %r8,0x7f
nr %r8,%r3 # clear per-event-bit and ilc
......@@ -536,6 +538,16 @@ pgm_no_vtime3:
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
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
*/
......
......@@ -518,6 +518,8 @@ pgm_no_vtime2:
#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
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_address(8,%r1),__LC_PER_ADDRESS
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
......@@ -553,6 +555,16 @@ pgm_no_vtime3:
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
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
*/
......@@ -815,7 +827,7 @@ restart_go:
*/
stack_overflow:
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
stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack
la %r1,__LC_SAVE_AREA
......@@ -823,7 +835,7 @@ stack_overflow:
je 0f
chi %r12,__LC_PGM_OLD_PSW
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
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # clear back chain
la %r2,SP_PTREGS(%r15) # load pt_regs
......
......@@ -272,7 +272,7 @@ iplstart:
# load parameter file from ipl device
#
.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
ltr %r2,%r2 # got anything ?
bz .Lnopf
......@@ -280,7 +280,7 @@ iplstart:
bnh .Lnotrunc
la %r2,895
.Lnotrunc:
l %r4,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
l %r4,.Linitrd
clc 0(3,%r4),.L_hdr # if it is HDRx
bz .Lagain1 # skip dataset header
clc 0(3,%r4),.L_eof # if it is EOFx
......@@ -323,14 +323,15 @@ iplstart:
# load ramdisk from ipl device
#
.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
st %r2,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r12) # store size of ramdisk
ltr %r2,%r2
bnz .Lrdcont
st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # no ramdisk found
.Lrdcont:
l %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
l %r2,.Linitrd
clc 0(3,%r2),.L_hdr # skip HDRx and EOFx
bz .Lagain2
......@@ -379,6 +380,7 @@ iplstart:
l %r1,.Lstartup
br %r1
.Linitrd:.long _end + 0x400000 # default address of initrd
.Lparm: .long PARMAREA
.Lstartup: .long startup
.Lcvtab:.long _ebcasc # ebcdic to ascii table
......@@ -479,65 +481,6 @@ start:
.byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7
.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
#include "head64.S"
#else
......
......@@ -26,8 +26,8 @@ startup:basr %r13,0 # get base
#
.org PARMAREA
.long 0,0 # IPL_DEVICE
.long 0,RAMDISK_ORIGIN # INITRD_START
.long 0,RAMDISK_SIZE # INITRD_SIZE
.long 0,0 # INITRD_START
.long 0,0 # INITRD_SIZE
.org COMMAND_LINE
.byte "root=/dev/ram0 ro"
......@@ -37,12 +37,23 @@ startup:basr %r13,0 # get base
startup_continue:
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
l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
# move IPL device to lowcore
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
#
......@@ -114,6 +125,10 @@ startup_continue:
b .Lfchunk-.LPG1(%r13)
.align 4
.Lipl_save_parameters:
.long ipl_save_parameters
.Linittu:
.long init_thread_union
.Lpmask:
.byte 0
.align 8
......@@ -273,7 +288,23 @@ startup_continue:
.Lbss_end: .long _end
.Lparmaddr: .long PARMAREA
.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
.globl s390_readinfo_sccb
s390_readinfo_sccb:
.Lsccb:
.hword 0x1000 # length, one page
.byte 0x00,0x00,0x00
......@@ -302,16 +333,6 @@ startup_continue:
.globl _stext
_stext: basr %r13,0 # get base
.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
stctl %c0,%c15,0(%r15)
oi 2(%r15),0x40 # enable sigp emergency signal
......@@ -330,6 +351,5 @@ _stext: basr %r13,0 # get base
#
.align 8
.Ldw: .long 0x000a0000,0x00000000
.Linittu:.long init_thread_union
.Lstart:.long start_kernel
.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
#
.org PARMAREA
.quad 0 # IPL_DEVICE
.quad RAMDISK_ORIGIN # INITRD_START
.quad RAMDISK_SIZE # INITRD_SIZE
.quad 0 # INITRD_START
.quad 0 # INITRD_SIZE
.org COMMAND_LINE
.byte "root=/dev/ram0 ro"
......@@ -39,8 +39,8 @@ startup_continue:
basr %r13,0 # get base
.LPG1: sll %r13,1 # remove high order bit
srl %r13,1
GET_IPL_DEVICE
lhi %r1,1 # mode 1 = esame
mvi __LC_AR_MODE_ID,1 # set esame flag
slr %r0,%r0 # set cpuid to zero
sigp %r1,%r0,0x12 # switch to esame mode
sam64 # switch to 64 bit mode
......@@ -48,7 +48,18 @@ startup_continue:
lg %r12,.Lparmaddr-.LPG1(%r13)# pointer to parameter area
# move IPL device to lowcore
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
#
......@@ -239,6 +250,19 @@ startup_continue:
oi 7(%r12),0x80 # set IDTE flag
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,
# virtual and never return ...
.align 16
......@@ -268,7 +292,22 @@ startup_continue:
.Lparmaddr:
.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
.globl s390_readinfo_sccb
s390_readinfo_sccb:
.Lsccb:
.hword 0x1000 # length, one page
.byte 0x00,0x00,0x00
......@@ -297,24 +336,12 @@ startup_continue:
.globl _stext
_stext: basr %r13,0 # get base
.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
stctg %c0,%c15,0(%r15)
oi 6(%r15),0x40 # enable sigp emergency signal
oi 4(%r15),0x10 # switch on low address proctection
lctlg %c0,%c15,0(%r15)
#
lam 0,15,.Laregs-.LPG3(%r13) # load access regs needed by uaccess
brasl %r14,start_kernel # go to C code
#
......@@ -322,7 +349,7 @@ _stext: basr %r13,0 # get base
#
basr %r13,0
lpswe .Ldw-.(%r13) # load disabled wait psw
#
.align 8
.Ldw: .quad 0x0002000180000000,0x0000000000000000
.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 @@
#include <asm/lowcore.h>
.globl do_reipl
do_reipl: basr %r13,0
.globl do_reipl_asm
do_reipl_asm: basr %r13,0
.Lpg0: lpsw .Lnewpsw-.Lpg0(%r13)
.Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13)
stctl %c0,%c0,.Lctlsave-.Lpg0(%r13)
ni .Lctlsave-.Lpg0(%r13),0xef
lctl %c0,%c0,.Lctlsave-.Lpg0(%r13)
# switch off lowcore protection
.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
mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13)
......@@ -46,9 +63,11 @@ do_reipl: basr %r13,0
.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13)
lpsw .Ldispsw-.Lpg0(%r13)
.align 8
.Lclkcmp: .quad 0x0000000000000000
.Lall: .long 0xff000000
.Lnull: .long 0x00000000
.Lctlsave: .long 0x00000000
.Lctlsave1: .long 0x00000000
.Lctlsave2: .long 0x00000000
.align 8
.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1
.Lpcnew: .long 0x00080000,0x80000000+.Lecs
......
......@@ -8,13 +8,30 @@
*/
#include <asm/lowcore.h>
.globl do_reipl
do_reipl: basr %r13,0
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
.globl do_reipl_asm
do_reipl_asm: basr %r13,0
# 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)
stctg %c0,%c0,.Lctlsave-.Lpg0(%r13)
ni .Lctlsave+4-.Lpg0(%r13),0xef
lctlg %c0,%c0,.Lctlsave-.Lpg0(%r13)
stctg %c0,%c0,.Lregsave-.Lpg0(%r13)
ni .Lregsave+4-.Lpg0(%r13),0xef
lctlg %c0,%c0,.Lregsave-.Lpg0(%r13)
lgr %r1,%r2
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13)
......@@ -50,8 +67,9 @@ do_reipl: basr %r13,0
st %r14,.Ldispsw+12-.Lpg0(%r13)
lpswe .Ldispsw-.Lpg0(%r13)
.align 8
.Lclkcmp: .quad 0x0000000000000000
.Lall: .quad 0x00000000ff000000
.Lctlsave: .quad 0x0000000000000000
.Lregsave: .quad 0x0000000000000000
.Lnull: .long 0x0000000000000000
.align 16
/*
......@@ -92,5 +110,3 @@ do_reipl: basr %r13,0
.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);
EXPORT_SYMBOL(_ni_bitmap);
EXPORT_SYMBOL(_zb_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);
/*
......
......@@ -37,6 +37,7 @@
#include <linux/kernel_stat.h>
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/pfn.h>
#include <asm/uaccess.h>
#include <asm/system.h>
......@@ -49,6 +50,12 @@
#include <asm/ptrace.h>
#include <asm/sections.h>
/*
* User copy operations.
*/
struct uaccess_ops uaccess;
EXPORT_SYMBOL_GPL(uaccess);
/*
* Machine setup..
*/
......@@ -284,16 +291,9 @@ void (*_machine_power_off)(void) = machine_power_off_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)
{
reipl_diag();
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0, NULL);
else
reipl (0x10000 | S390_lowcore.ipl_device);
do_reipl();
}
static void do_machine_halt_nonsmp(void)
......@@ -501,13 +501,47 @@ setup_memory(void)
* partially used pages are not usable - thus
* we are rounding upwards:
*/
start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT;
end_pfn = max_pfn = memory_end >> PAGE_SHIFT;
start_pfn = PFN_UP(__pa(&_end));
end_pfn = max_pfn = PFN_DOWN(memory_end);
/* Initialize storage key for kernel pages */
for (init_pfn = 0 ; init_pfn < start_pfn; init_pfn++)
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):
*/
......@@ -559,7 +593,7 @@ setup_memory(void)
reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
#ifdef CONFIG_BLK_DEV_INITRD
if (INITRD_START) {
if (INITRD_START && INITRD_SIZE) {
if (INITRD_START + INITRD_SIZE <= memory_end) {
reserve_bootmem(INITRD_START, INITRD_SIZE);
initrd_start = INITRD_START;
......@@ -613,6 +647,11 @@ setup_arch(char **cmdline_p)
memory_end = memory_size;
if (MACHINE_HAS_MVCOS)
memcpy(&uaccess, &uaccess_mvcos, sizeof(uaccess));
else
memcpy(&uaccess, &uaccess_std, sizeof(uaccess));
parse_early_param();
#ifndef CONFIG_64BIT
......@@ -720,214 +759,3 @@ struct seq_operations cpuinfo_op = {
.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,
static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{
unsigned long old_mask = regs->psw.mask;
int err;
_sigregs user_sregs;
save_access_regs(current->thread.acrs);
/* Copy a 'clean' PSW mask to the user to avoid leaking
information about whether PER is currently on. */
regs->psw.mask = PSW_MASK_MERGE(PSW_USER_BITS, regs->psw.mask);
err = __copy_to_user(&sregs->regs.psw, &regs->psw,
sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs));
memcpy(&user_sregs.regs.psw, &regs->psw, sizeof(sregs->regs.psw) +
sizeof(sregs->regs.gprs));
regs->psw.mask = old_mask;
if (err != 0)
return err;
err = __copy_to_user(&sregs->regs.acrs, current->thread.acrs,
sizeof(sregs->regs.acrs));
if (err != 0)
return err;
memcpy(&user_sregs.regs.acrs, current->thread.acrs,
sizeof(sregs->regs.acrs));
/*
* We have to store the fp registers to current->thread.fp_regs
* to merge them with the emulated registers.
*/
save_fp_regs(&current->thread.fp_regs);
return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs,
sizeof(s390_fp_regs));
memcpy(&user_sregs.fpregs, &current->thread.fp_regs,
sizeof(s390_fp_regs));
return __copy_to_user(sregs, &user_sregs, sizeof(_sigregs));
}
/* Returns positive number on error */
......@@ -144,27 +141,25 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{
unsigned long old_mask = regs->psw.mask;
int err;
_sigregs user_sregs;
/* Alwys make any pending restarted system call return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
err = __copy_from_user(&regs->psw, &sregs->regs.psw,
sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs));
err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs));
regs->psw.mask = PSW_MASK_MERGE(old_mask, regs->psw.mask);
regs->psw.addr |= PSW_ADDR_AMODE;
if (err)
return err;
err = __copy_from_user(&current->thread.acrs, &sregs->regs.acrs,
sizeof(sregs->regs.acrs));
if (err)
return err;
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));
restore_access_regs(current->thread.acrs);
err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
sizeof(s390_fp_regs));
memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
sizeof(s390_fp_regs));
current->thread.fp_regs.fpc &= FPC_VALID_MASK;
if (err)
return err;
restore_fp_regs(&current->thread.fp_regs);
regs->trap = -1; /* disable syscall checks */
......@@ -457,6 +452,7 @@ void do_signal(struct pt_regs *regs)
case -ERESTART_RESTARTBLOCK:
regs->gprs[2] = -EINTR;
}
regs->trap = -1; /* Don't deal with this again. */
}
/* Get signal to deliver. When running under ptrace, at this point
......
......@@ -59,9 +59,6 @@ static struct task_struct *current_set[NR_CPUS];
extern char vmhalt_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_others(ec_bit_sig);
......@@ -279,12 +276,7 @@ static void do_machine_restart(void * __unused)
* interrupted by an external interrupt and s390irq
* locks are always held disabled).
*/
reipl_diag();
if (MACHINE_IS_VM)
cpcmd ("IPL", NULL, 0, NULL);
else
reipl (0x10000 | S390_lowcore.ipl_device);
do_reipl();
}
void machine_restart_smp(char * __unused)
......
......@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/reboot.h>
#include <linux/kprobes.h>
#include <asm/system.h>
#include <asm/uaccess.h>
......@@ -39,6 +40,7 @@
#include <asm/s390_ext.h>
#include <asm/lowcore.h>
#include <asm/debug.h>
#include <asm/kdebug.h>
/* Called from entry.S only */
extern void handle_per_exception(struct pt_regs *regs);
......@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12;
static int kstack_depth_to_print = 20;
#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:
* - 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)
#endif
}
static void inline do_trap(long interruption_code, int signr, char *str,
struct pt_regs *regs, siginfo_t *info)
static void __kprobes inline do_trap(long interruption_code, int signr,
char *str, struct pt_regs *regs,
siginfo_t *info)
{
/*
* 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,
if (regs->psw.mask & PSW_MASK_PSTATE)
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) {
struct task_struct *tsk = current;
......@@ -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);
}
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)
force_sig(SIGTRAP, current);
}
......
......@@ -24,6 +24,7 @@ SECTIONS
*(.text)
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
*(.fixup)
*(.gnu.warning)
} = 0x0700
......@@ -117,7 +118,7 @@ SECTIONS
/* Sections to be discarded */
/DISCARD/ : {
*(.exitcall.exit)
*(.exit.text) *(.exit.data) *(.exitcall.exit)
}
/* Stabs debugging sections. */
......
......@@ -4,6 +4,6 @@
EXTRA_AFLAGS := -traditional
lib-y += delay.o string.o
lib-y += $(if $(CONFIG_64BIT),uaccess64.o,uaccess.o)
lib-y += delay.o string.o uaccess_std.o
lib-$(CONFIG_64BIT) += uaccess_mvcos.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;
static void cmm_timer_fn(unsigned long);
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
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,
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p);
pages = cmm_strtoul(p, &p);
pages = simple_strtoul(p, &p, 0);
if (ctl == &cmm_table[0])
cmm_set_pages(pages);
else
......@@ -317,9 +301,9 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p);
pages = cmm_strtoul(p, &p);
pages = simple_strtoul(p, &p, 0);
cmm_skip_blanks(p, &p);
seconds = cmm_strtoul(p, &p);
seconds = simple_strtoul(p, &p, 0);
cmm_set_timeout(pages, seconds);
} else {
len = sprintf(buf, "%ld %ld\n",
......@@ -382,24 +366,24 @@ cmm_smsg_target(char *from, char *msg)
if (strncmp(msg, "SHRINK", 6) == 0) {
if (!cmm_skip_blanks(msg + 6, &msg))
return;
pages = cmm_strtoul(msg, &msg);
pages = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
cmm_set_pages(pages);
} else if (strncmp(msg, "RELEASE", 7) == 0) {
if (!cmm_skip_blanks(msg + 7, &msg))
return;
pages = cmm_strtoul(msg, &msg);
pages = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
cmm_add_timed_pages(pages);
} else if (strncmp(msg, "REUSE", 5) == 0) {
if (!cmm_skip_blanks(msg + 5, &msg))
return;
pages = cmm_strtoul(msg, &msg);
pages = simple_strtoul(msg, &msg, 0);
if (!cmm_skip_blanks(msg, &msg))
return;
seconds = cmm_strtoul(msg, &msg);
seconds = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
cmm_set_timeout(pages, seconds);
......
......@@ -25,10 +25,12 @@
#include <linux/console.h>
#include <linux/module.h>
#include <linux/hardirq.h>
#include <linux/kprobes.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/kdebug.h>
#ifndef CONFIG_64BIT
#define __FAIL_ADDR_MASK 0x7ffff000
......@@ -48,6 +50,38 @@ extern int sysctl_userprocess_debug;
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;
/*
......@@ -159,7 +193,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
* 11 Page translation -> 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)
{
struct task_struct *tsk;
......@@ -173,6 +207,10 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
tsk = current;
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
* as a special case because the translation exception code
......
......@@ -108,16 +108,23 @@ void __init paging_init(void)
unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
static const int ssm_mask = 0x04000000L;
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_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 */
pg_dir = swapper_pg_dir;
for (i=0;i<KERNEL_PGD_PTRS;i++)
pmd_clear((pmd_t*)pg_dir++);
for (i = 0; i < PTRS_PER_PGD; i++)
pmd_clear((pmd_t *) pg_dir++);
/*
* map whole physical memory to virtual memory (identity mapping)
......@@ -131,10 +138,7 @@ void __init paging_init(void)
*/
pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
pg_dir->pgd0 = (_PAGE_TABLE | __pa(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));
pmd_populate_kernel(&init_mm, (pmd_t *) pg_dir, pg_table);
pg_dir++;
for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
......@@ -143,8 +147,8 @@ void __init paging_init(void)
else
pte = pfn_pte(pfn, PAGE_KERNEL);
if (pfn >= max_low_pfn)
pte_clear(&init_mm, 0, &pte);
set_pte(pg_table, pte);
pte_val(pte) = _PAGE_TYPE_EMPTY;
set_pte(pg_table, pte);
pfn++;
}
}
......@@ -159,16 +163,6 @@ void __init paging_init(void)
: : "m" (pgdir_k), "m" (ssm_mask));
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;
}
......@@ -236,10 +230,8 @@ void __init paging_init(void)
pte = pfn_pte(pfn, __pgprot(_PAGE_RO));
else
pte = pfn_pte(pfn, PAGE_KERNEL);
if (pfn >= max_low_pfn) {
pte_clear(&init_mm, 0, &pte);
continue;
}
if (pfn >= max_low_pfn)
pte_val(pte) = _PAGE_TYPE_EMPTY;
set_pte(pt_dir, pte);
pfn++;
}
......
/*
* 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>
......
......@@ -213,17 +213,35 @@ config MONREADER
help
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
menu "Cryptographic devices"
config Z90CRYPT
config ZCRYPT
tristate "Support for PCI-attached cryptographic adapters"
default "m"
help
select ZCRYPT_MONOLITHIC if ZCRYPT="y"
default "m"
help
Select this option if you want to use a PCI-attached cryptographic
adapter like the PCI Cryptographic Accelerator (PCICA) or the PCI
Cryptographic Coprocessor (PCICC). This option is also available
as a module called z90crypt.ko.
adapter like:
+ PCI Cryptographic Accelerator (PCICA)
+ 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
......@@ -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,
8 * sizeof (long));
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");
device->state = DASD_STATE_BASIC;
......@@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
device = (struct dasd_device *) cqr->device;
if (device == NULL ||
device != dasd_device_from_cdev(cdev) ||
device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id);
......@@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
if ((irb->scsw.dstat & mask) == mask) {
device = dasd_device_from_cdev(cdev);
device = dasd_device_from_cdev_locked(cdev);
if (!IS_ERR(device)) {
dasd_handle_state_change_pending(device);
dasd_put_device(device);
......@@ -2169,7 +2169,7 @@ dasd_init(void)
goto failed;
}
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");
......
......@@ -258,8 +258,12 @@ dasd_parse_keyword( char *parsestring ) {
return residual_str;
}
if (strncmp("nopav", parsestring, length) == 0) {
dasd_nopav = 1;
MESSAGE(KERN_INFO, "%s", "disable PAV mode");
if (MACHINE_IS_VM)
MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM");
else {
dasd_nopav = 1;
MESSAGE(KERN_INFO, "%s", "disable PAV mode");
}
return residual_str;
}
if (strncmp("fixedbuffers", parsestring, length) == 0) {
......@@ -523,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
struct dasd_device *device;
unsigned long flags;
int rc;
devmap = dasd_devmap_from_cdev(cdev);
if (IS_ERR(devmap))
return (void *) devmap;
cdev->dev.driver_data = devmap;
device = dasd_alloc_device();
if (IS_ERR(device))
return device;
atomic_set(&device->ref_count, 2);
atomic_set(&device->ref_count, 3);
spin_lock(&dasd_devmap_lock);
if (!devmap->device) {
......@@ -552,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev)
dasd_free_device(device);
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;
}
......@@ -569,6 +578,7 @@ dasd_delete_device(struct dasd_device *device)
{
struct ccw_device *cdev;
struct dasd_devmap *devmap;
unsigned long flags;
/* First remove device pointer from devmap. */
devmap = dasd_find_busid(device->cdev->dev.bus_id);
......@@ -582,9 +592,16 @@ dasd_delete_device(struct dasd_device *device)
devmap->device = NULL;
spin_unlock(&dasd_devmap_lock);
/* Drop ref_count by 2, one for the devmap reference and
* one for the passed reference. */
atomic_sub(2, &device->ref_count);
/* Disconnect dasd_device structure from ccw_device structure. */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
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_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
......@@ -593,9 +610,6 @@ dasd_delete_device(struct dasd_device *device)
cdev = device->cdev;
device->cdev = NULL;
/* Disconnect dasd_devmap structure from ccw_device structure. */
cdev->dev.driver_data = NULL;
/* Put ccw_device structure. */
put_device(&cdev->dev);
......@@ -613,23 +627,34 @@ dasd_put_device_wake(struct dasd_device *device)
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.
*/
struct dasd_device *
dasd_device_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
struct dasd_device *device;
unsigned long flags;
device = ERR_PTR(-ENODEV);
spin_lock(&dasd_devmap_lock);
devmap = cdev->dev.driver_data;
if (devmap && devmap->device) {
device = devmap->device;
dasd_get_device(device);
}
spin_unlock(&dasd_devmap_lock);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
device = dasd_device_from_cdev_locked(cdev);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return device;
}
......@@ -730,16 +755,17 @@ static ssize_t
dasd_discipline_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dasd_devmap *devmap;
char *dname;
struct dasd_device *device;
ssize_t len;
spin_lock(&dasd_devmap_lock);
dname = "none";
devmap = dev->driver_data;
if (devmap && devmap->device && devmap->device->discipline)
dname = devmap->device->discipline->name;
spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", dname);
device = dasd_device_from_cdev(to_ccwdev(dev));
if (!IS_ERR(device) && device->discipline) {
len = snprintf(buf, PAGE_SIZE, "%s\n",
device->discipline->name);
dasd_put_device(device);
} else
len = snprintf(buf, PAGE_SIZE, "none\n");
return len;
}
static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
......
......@@ -678,7 +678,7 @@ int __init dasd_eer_init(void)
return 0;
}
void __exit dasd_eer_exit(void)
void dasd_eer_exit(void)
{
WARN_ON(misc_deregister(&dasd_eer_dev) != 0);
}
......@@ -534,6 +534,7 @@ int dasd_add_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_locked(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int);
int dasd_parse(void);
......
......@@ -453,7 +453,7 @@ static int __init xpram_init(void)
PRINT_WARN("No expanded memory available\n");
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",
xpram_pages, (unsigned long) xpram_pages*4);
rc = xpram_setup_sizes(xpram_pages);
......
......@@ -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_3590) += tape_3590.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
* 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>
*
*
......
/*
* 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
* Author(s): Christian Borntraeger <cborntra@de.ibm.com>
*
......
......@@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* trigger path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
else if (sch->vpm == mask)
else if (sch->lpm == mask)
goto out_unreg;
out_unlock:
spin_unlock_irq(&sch->lock);
......@@ -378,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
if (chp_mask == 0) {
spin_unlock_irq(&sch->lock);
put_device(&sch->dev);
return 0;
}
old_lpm = sch->lpm;
......@@ -392,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
spin_unlock_irq(&sch->lock);
put_device(&sch->dev);
return (res_data->fla_mask == 0xffff) ? -ENODEV : 0;
return 0;
}
......
......@@ -16,11 +16,10 @@
#include <linux/device.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
#include <asm/cio.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include "airq.h"
#include "cio.h"
#include "css.h"
......@@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
sch->orb.pfch = sch->options.prefetch == 0;
sch->orb.spnd = sch->options.suspend;
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
/*
* 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)
sch->opm = 0xff;
if (!cio_is_console(sch->schid))
chsc_validate_chpids(sch);
sch->lpm = sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
sch->schib.pmcw.pom &
sch->opm;
sch->lpm = sch->schib.pmcw.pam & sch->opm;
CIO_DEBUG(KERN_INFO, 0,
"Detected device %04x on subchannel 0.%x.%04X"
......@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid)
return -EBUSY;
}
extern void do_reipl(unsigned long devno);
static int
__shutdown_subchannel_easy(struct subchannel_id schid, void *data)
struct sch_match_id {
struct subchannel_id schid;
struct ccw_dev_id devid;
int rc;
};
static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid,
void *data)
{
struct schib schib;
struct sch_match_id *match_id = data;
if (stsch_err(schid, &schib))
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)
return 0;
switch(__disable_subchannel_easy(schid, &schib)) {
......@@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
return 0;
}
void
clear_all_subchannels(void)
static int clear_all_subchannels_and_match(struct ccw_dev_id *devid,
struct subchannel_id *schid)
{
struct sch_match_id match_id;
match_id.devid = *devid;
match_id.rc = -ENODEV;
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. */
void
reipl(unsigned long devno)
void reipl_ccw_dev(struct ccw_dev_id *devid)
{
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();
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;
}
This diff is collapsed.
......@@ -52,53 +52,81 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
return 1;
}
/*
* Hotplugging interface for ccw devices.
* Heavily modeled on pci and usb hotplug.
*/
static int
ccw_uevent (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
/* Store modalias string delimited by prefix/suffix string into buffer with
* specified size. Return length of resulting string (excluding trailing '\0')
* even if string doesn't fit buffer (snprintf semantics). */
static int snprint_alias(char *buf, size_t size, const char *prefix,
struct ccw_device_id *id, const char *suffix)
{
struct ccw_device *cdev = to_ccwdev(dev);
int i = 0;
int length = 0;
int len;
if (!cdev)
return -ENODEV;
len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
id->cu_model);
if (len > size)
return len;
buf += len;
size -= len;
/* what we want to pass to /sbin/hotplug */
if (id->dev_type != 0)
len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
id->dev_model, suffix);
else
len += snprintf(buf, size, "dtdm%s", suffix);
envp[i++] = buffer;
length += scnprintf(buffer, buffer_size - length, "CU_TYPE=%04X",
cdev->id.cu_type);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
buffer += length;
return len;
}
/* Set up environment variables for ccw device uevent. Return 0 on success,
* non-zero otherwise. */
static int ccw_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
int i = 0;
int len;
/* CU_TYPE= */
len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
if (len > buffer_size || i >= num_envp)
return -ENOMEM;
envp[i++] = buffer;
length += scnprintf(buffer, buffer_size - length, "CU_MODEL=%02X",
cdev->id.cu_model);
if ((buffer_size - length <= 0) || (i >= num_envp))
buffer += len;
buffer_size -= len;
/* CU_MODEL= */
len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
if (len > buffer_size || i >= num_envp)
return -ENOMEM;
++length;
buffer += length;
envp[i++] = buffer;
buffer += len;
buffer_size -= len;
/* The next two can be zero, that's ok for us */
envp[i++] = buffer;
length += scnprintf(buffer, buffer_size - length, "DEV_TYPE=%04X",
cdev->id.dev_type);
if ((buffer_size - length <= 0) || (i >= num_envp))
/* DEV_TYPE= */
len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
if (len > buffer_size || i >= num_envp)
return -ENOMEM;
++length;
buffer += length;
envp[i++] = buffer;
buffer += len;
buffer_size -= len;
/* DEV_MODEL= */
len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
(unsigned char) id->dev_model) + 1;
if (len > buffer_size || i >= num_envp)
return -ENOMEM;
envp[i++] = buffer;
length += scnprintf(buffer, buffer_size - length, "DEV_MODEL=%02X",
cdev->id.dev_model);
if ((buffer_size - length <= 0) || (i >= num_envp))
buffer += len;
buffer_size -= len;
/* MODALIAS= */
len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
if (len > buffer_size || i >= num_envp)
return -ENOMEM;
envp[i++] = buffer;
buffer += len;
buffer_size -= len;
envp[i] = NULL;
......@@ -251,16 +279,11 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_device_id *id = &(cdev->id);
int ret;
int len;
ret = sprintf(buf, "ccw:t%04Xm%02X",
id->cu_type, id->cu_model);
if (id->dev_type != 0)
ret += sprintf(buf + ret, "dt%04Xdm%02X\n",
id->dev_type, id->dev_model);
else
ret += sprintf(buf + ret, "dtdm\n");
return ret;
len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
return len > PAGE_SIZE ? PAGE_SIZE : len;
}
static ssize_t
......
This diff is collapsed.
......@@ -96,6 +96,12 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
ret = cio_set_options (sch, flags);
if (ret)
return ret;
/* Adjust requested path mask to excluded varied off paths. */
if (lpm) {
lpm &= sch->opm;
if (lpm == 0)
return -EACCES;
}
ret = cio_start_key (sch, cpa, lpm, key);
if (ret == 0)
cdev->private->intparm = intparm;
......@@ -250,7 +256,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
if (!sch)
return 0;
else
return sch->vpm;
return sch->lpm;
}
static void
......@@ -304,7 +310,7 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
sch = to_subchannel(cdev->dev.parent);
do {
ret = cio_start (sch, ccw, lpm);
if ((ret == -EBUSY) || (ret == -EACCES)) {
if (ret == -EBUSY) {
/* Try again later. */
spin_unlock_irq(&sch->lock);
msleep(10);
......@@ -433,6 +439,13 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
if (!ciw || ciw->cmd == 0)
return -EOPNOTSUPP;
/* Adjust requested path mask to excluded varied off paths. */
if (lpm) {
lpm &= sch->opm;
if (lpm == 0)
return -EACCES;
}
rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
if (!rcd_ccw)
return -ENOMEM;
......
This diff is collapsed.
......@@ -115,7 +115,7 @@ qdio_min(int a,int b)
static inline __u64
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)
#ifdef QDIO_USE_PROCESSING_STATE
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 */
QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
......
This diff is collapsed.
......@@ -2,5 +2,16 @@
# S/390 crypto devices
#
z90crypt-objs := z90main.o z90hardware.o
obj-$(CONFIG_Z90CRYPT) += z90crypt.o
ifdef CONFIG_ZCRYPT_MONOLITHIC
z90crypt-objs := zcrypt_mono.o ap_bus.o zcrypt_api.o \
zcrypt_pcica.o zcrypt_pcicc.o zcrypt_pcixcc.o zcrypt_cex2a.o
obj-$(CONFIG_ZCRYPT) += z90crypt.o
else
ap-objs := ap_bus.o
obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
endif
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.
include include/asm-generic/Kbuild.asm
unifdef-y += cmb.h debug.h
header-y += dasd.h qeth.h tape390.h ucontext.h vtoc.h z90crypt.h
header-y += dasd.h monwriter.h qeth.h tape390.h ucontext.h vtoc.h z90crypt.h
This diff is collapsed.
......@@ -270,6 +270,11 @@ struct diag210 {
__u32 vrdccrft : 8; /* real device feature (output) */
} __attribute__ ((packed,aligned(4)));
struct ccw_dev_id {
u8 ssid;
u16 devno;
};
extern int diag210(struct diag210 *addr);
extern void wait_cons_dev(void);
......@@ -280,6 +285,8 @@ extern void cio_reset_channel_paths(void);
extern void css_schedule_reprobe(void);
extern void reipl_ccw_dev(struct ccw_dev_id *id);
#endif
#endif
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