Commit 67cecd1e authored by Linus Torvalds's avatar Linus Torvalds

Merge penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/s390

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents 175ceea9 a8023367
......@@ -11,38 +11,6 @@ config SWAP
bool
default y
config ISA
bool
help
Find out whether you have ISA slots on your motherboard. ISA is the
name of a bus system, i.e. the way the CPU talks to the other stuff
inside your box. Other bus systems are PCI, EISA, MicroChannel
(MCA) or VESA. ISA is an older system, now being displaced by PCI;
newer boards don't support it. If you have ISA, say Y, otherwise N.
config EISA
bool
---help---
The Extended Industry Standard Architecture (EISA) bus was
developed as an open alternative to the IBM MicroChannel bus.
The EISA bus provided some of the features of the IBM MicroChannel
bus while maintaining backward compatibility with cards made for
the older ISA bus. The EISA bus saw limited use between 1988 and
1995 when it was made obsolete by the PCI bus.
Say Y here if you are building a kernel for an EISA-based machine.
Otherwise, say N.
config MCA
bool
help
MicroChannel Architecture is found in some IBM PS/2 machines and
laptops. It is a bus system similar to PCI or ISA. See
<file:Documentation/mca.txt> (and especially the web page given
there) before attempting to build an MCA bus kernel.
config UID16
bool
default y
......@@ -57,7 +25,6 @@ config RWSEM_XCHGADD_ALGORITHM
config GENERIC_BUST_SPINLOCK
bool
mainmenu "Linux Kernel Configuration"
config ARCH_S390
......@@ -115,6 +82,13 @@ config NR_CPUS
int "Maximum number of CPUs (2-32)"
depends on SMP
default "32"
help
This allows you to specify the maximum number of CPUs which this
kernel will support. The maximum supported value is 32 and the
minimum value which makes sense is 2.
This is purely to save memory - each supported CPU adds
approximately eight kilobytes to the kernel image.
comment "I/O subsystem configuration"
......@@ -153,6 +127,14 @@ comment "Misc"
config PREEMPT
bool "Preemptible Kernel"
help
This option reduces the latency of the kernel when reacting to
real-time or interactive events by allowing a low priority process to
be preempted even if it is in kernel mode executing a system call.
This allows applications to run more reliably even when the system is
under load.
Say N if you are unsure.
config IPL
bool "Builtin IPL record support"
......
......@@ -16,6 +16,7 @@
LDFLAGS := -m elf_s390
OBJCOPYFLAGS := -O binary
LDFLAGS_vmlinux := -e start
LDFLAGS_BLOB := --format binary --oformat elf32-s390
CFLAGS += -pipe -fno-strength-reduce
......@@ -28,18 +29,14 @@ libs-y += arch/s390/lib/
all: image listing
listing: vmlinux
@$(MAKEBOOT) listing
makeboot = $(call descend,arch/$(ARCH)/boot,$(1))
BOOTIMAGE= arch/$(ARCH)/boot/image
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
image: vmlinux
@$(MAKEBOOT) image
install: vmlinux
@$(MAKEBOOT) BOOTIMAGE=image install
listing install image: vmlinux
+@$(call makeboot,BOOTIMAGE=$(BOOTIMAGE) $@)
archclean:
+@$(call makeboot,clean)
archmrproper:
......
#
# arch/s390/boot/Makefile
# Makefile for the linux s390-specific parts of the memory manager.
#
EXTRA_AFLAGS := -traditional
include $(TOPDIR)/Rules.make
%.lnk: %.o
$(LD) $(LDFLAGS) -Ttext 0x0 -o $@ $<
quiet_cmd_listing = OBJDUMP $(echo_target)
cmd_listing = $(OBJDUMP) --disassemble --disassemble-all \
--disassemble-zeroes --reloc vmlinux > $@
%.boot: %.lnk
$(OBJCOPY) $(OBJCOPYFLAGS) $< $@
$(obj)/image: vmlinux
$(call if_changed,objcopy)
image: $(TOPDIR)/vmlinux \
iplfba.boot ipleckd.boot ipldump.boot
$(OBJCOPY) $(OBJCOPYFLAGS) $(TOPDIR)/vmlinux image
$(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aUw] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map
$(obj)/listing: vmlinux
$(call if_changed,listing)
listing: ../../../vmlinux
$(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing
image: $(obj)/image
listing: $(obj)/listing
clean:
rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
rm -f $(obj)/image $(obj)/listing
install: $(CONFIGURE) $(BOOTIMAGE)
sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
sh -x $(obj)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map Kerntypes "$(INSTALL_PATH)"
#
# Automatically generated make config: don't edit
#
# CONFIG_ISA is not set
# CONFIG_EISA is not set
# CONFIG_MCA is not set
CONFIG_MMU=y
CONFIG_SWAP=y
CONFIG_UID16=y
# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
# CONFIG_GENERIC_BUST_SPINLOCK is not set
CONFIG_ARCH_S390=y
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_CONFIDENTIAL=y
#
# General setup
......@@ -27,7 +25,8 @@ CONFIG_SYSCTL=y
# Loadable module support
#
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_UNLOAD is not set
# CONFIG_MODULE_FORCE_UNLOAD is not set
CONFIG_KMOD=y
#
......@@ -39,13 +38,13 @@ CONFIG_KMOD=y
#
CONFIG_SMP=y
CONFIG_MATHEMU=y
CONFIG_NR_CPUS=64
CONFIG_NR_CPUS=32
#
# I/O subsystem configuration
#
CONFIG_MACHCHK_WARNING=y
CONFIG_QDIO=m
CONFIG_QDIO=y
# CONFIG_QDIO_PERF_STATS is not set
#
......@@ -57,7 +56,7 @@ CONFIG_IPL=y
CONFIG_IPL_VM=y
CONFIG_KCORE_ELF=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
CONFIG_BINFMT_MISC=m
# CONFIG_PROCESS_DEBUG is not set
CONFIG_PFAULT=y
# CONFIG_SHARED_KERNEL is not set
......@@ -65,79 +64,15 @@ CONFIG_PFAULT=y
#
# SCSI support
#
CONFIG_SCSI=m
#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=m
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=m
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=m
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_SR_EXTRA_DEVS=10
CONFIG_CHR_DEV_SG=m
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
CONFIG_SCSI_MULTI_LUN=y
# CONFIG_SCSI_REPORT_LUNS is not set
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
#
# SCSI low-level drivers
#
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_DPT_I2O is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
# CONFIG_SCSI_MEGARAID is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_IMM is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PCI2000 is not set
# CONFIG_SCSI_PCI2220I is not set
# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_SYM53C416 is not set
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
#
# PCMCIA SCSI adapter support
#
# CONFIG_SCSI_PCMCIA is not set
# CONFIG_SCSI is not set
#
# Block device drivers
#
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_NBD=m
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=24576
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
CONFIG_BLK_DEV_XPRAM=m
......@@ -147,66 +82,63 @@ CONFIG_BLK_DEV_XPRAM=m
CONFIG_DASD=y
CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y
# CONFIG_DASD_DIAG is not set
CONFIG_DASD_DIAG=y
#
# Multi-device support (RAID and LVM)
#
CONFIG_MD=y
CONFIG_BLK_DEV_MD=m
# CONFIG_MD_LINEAR is not set
CONFIG_BLK_DEV_MD=y
CONFIG_MD_LINEAR=m
CONFIG_MD_RAID0=m
CONFIG_MD_RAID1=m
CONFIG_MD_RAID5=m
# CONFIG_MD_MULTIPATH is not set
# CONFIG_BLK_DEV_LVM is not set
CONFIG_MD_MULTIPATH=m
# CONFIG_BLK_DEV_DM is not set
#
# Character device drivers
#
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
CONFIG_UNIX98_PTY_COUNT=2048
#
# S/390 character device drivers
#
CONFIG_TN3270=y
CONFIG_TN3270_CONSOLE=y
# CONFIG_TN3270 is not set
CONFIG_TN3215=y
CONFIG_TN3215_CONSOLE=y
CONFIG_HWC=y
CONFIG_HWC_CONSOLE=y
CONFIG_HWC_CPI=m
CONFIG_SCLP=y
CONFIG_SCLP_TTY=y
CONFIG_SCLP_CONSOLE=y
CONFIG_SCLP_CPI=m
CONFIG_S390_TAPE=m
#
# S/390 tape interface support
#
CONFIG_S390_TAPE_CHAR=y
CONFIG_S390_TAPE_BLOCK=y
#
# S/390 tape hardware support
#
CONFIG_S390_TAPE_3490=m
CONFIG_S390_TAPE_3480=m
CONFIG_S390_TAPE_34XX=m
#
# Network device drivers
#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
CONFIG_DUMMY=m
CONFIG_BONDING=m
CONFIG_EQUALIZER=m
CONFIG_TUN=m
CONFIG_NET_ETHERNET=y
CONFIG_TR=y
# CONFIG_TR is not set
# CONFIG_FDDI is not set
#
# S/390 network device drivers
#
CONFIG_CHANDEV=y
CONFIG_HOTPLUG=y
CONFIG_LCS=m
CONFIG_CTC=m
......@@ -221,6 +153,7 @@ CONFIG_PACKET=y
# CONFIG_NETFILTER is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
......@@ -231,19 +164,19 @@ CONFIG_IP_MULTICAST=y
# CONFIG_ARPD is not set
# CONFIG_INET_ECN is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_XFRM_USER is not set
CONFIG_IPV6=m
#
# SCTP Configuration (EXPERIMENTAL)
#
CONFIG_IPV6_SCTP__=m
CONFIG_IPV6_SCTP__=y
# CONFIG_IP_SCTP is not set
# CONFIG_ATM is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_LLC is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_DEV_APPLETALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
......@@ -257,66 +190,70 @@ CONFIG_NET_FASTROUTE=y
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
# CONFIG_NET_SCH_HTB is not set
CONFIG_NET_SCH_CSZ=m
CONFIG_NET_SCH_PRIO=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TEQL=m
CONFIG_NET_SCH_TBF=m
CONFIG_NET_SCH_GRED=m
CONFIG_NET_SCH_DSMARK=m
CONFIG_NET_QOS=y
CONFIG_NET_ESTIMATOR=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_TCINDEX=m
CONFIG_NET_CLS_ROUTE4=m
CONFIG_NET_CLS_ROUTE=y
CONFIG_NET_CLS_FW=m
CONFIG_NET_CLS_U32=m
CONFIG_NET_CLS_RSVP=m
CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_POLICE=y
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
#
# File systems
#
# CONFIG_QUOTA is not set
# CONFIG_QFMT_V1 is not set
# CONFIG_QFMT_V2 is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_REISERFS_CHECK is not set
# CONFIG_REISERFS_PROC_INFO is not set
# CONFIG_ADFS_FS is not set
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
CONFIG_EXT3_FS=y
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
# CONFIG_EXT3_FS is not set
# CONFIG_JBD is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS_FS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_TMPFS is not set
CONFIG_RAMFS=y
# CONFIG_ISO9660_FS is not set
# CONFIG_JOLIET is not set
# CONFIG_ZISOFS is not set
# CONFIG_JFS_FS is not set
# CONFIG_JFS_DEBUG is not set
# CONFIG_JFS_STATISTICS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS_DEBUG is not set
# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
CONFIG_DEVFS_FS=y
# CONFIG_DEVFS_MOUNT is not set
# CONFIG_DEVFS_DEBUG is not set
# CONFIG_DEVPTS_FS is not set
# CONFIG_DEVFS_FS is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_QNX4FS_RW is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UDF_FS is not set
# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
# CONFIG_UFS_FS_WRITE is not set
# CONFIG_XFS_FS is not set
# CONFIG_XFS_RT is not set
# CONFIG_XFS_QUOTA is not set
#
# Network File Systems
......@@ -324,25 +261,20 @@ CONFIG_EXT2_FS=y
# CONFIG_CODA_FS is not set
# CONFIG_INTERMEZZO_FS is not set
CONFIG_NFS_FS=y
# CONFIG_NFS_V3 is not set
# CONFIG_ROOT_NFS is not set
CONFIG_NFS_V3=y
# CONFIG_NFS_V4 is not set
CONFIG_NFSD=y
# CONFIG_NFSD_V3 is not set
CONFIG_NFSD_V3=y
# CONFIG_NFSD_V4 is not set
# CONFIG_NFSD_TCP is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_EXPORTFS=y
# CONFIG_CIFS is not set
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
# CONFIG_NCPFS_STRONG is not set
# CONFIG_NCPFS_NFS_NS is not set
# CONFIG_NCPFS_OS2_NS is not set
# CONFIG_NCPFS_SMALLDOS is not set
# CONFIG_NCPFS_NLS is not set
# CONFIG_NCPFS_EXTRAS is not set
# CONFIG_ZISOFS_FS is not set
# CONFIG_AFS_FS is not set
#
# Partition Types
......@@ -360,8 +292,6 @@ CONFIG_IBM_PARTITION=y
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_EFI_PARTITION is not set
# CONFIG_SMB_NLS is not set
# CONFIG_NLS is not set
#
# Kernel hacking
......@@ -373,9 +303,20 @@ CONFIG_MAGIC_SYSRQ=y
#
CONFIG_SECURITY_CAPABILITIES=y
#
# Cryptographic options
#
CONFIG_CRYPTO=y
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_TEST is not set
#
# Library routines
#
# CONFIG_CRC32 is not set
# CONFIG_ZLIB_INFLATE is not set
# CONFIG_ZLIB_DEFLATE is not set
......@@ -10,7 +10,7 @@ obj-y := entry.o bitmap.o traps.o time.o process.o \
setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
semaphore.o reipl.o s390_ext.o debug.o
obj-$(CONFIG_MODULES) += s390_ksyms.o
obj-$(CONFIG_MODULES) += s390_ksyms.o module.o
obj-$(CONFIG_SMP) += smp.o
#
......
......@@ -474,10 +474,10 @@ sys_call_table:
.long sys_adjtimex
.long sys_mprotect /* 125 */
.long sys_sigprocmask
.long sys_create_module
.long sys_ni_syscall /* old "create module" */
.long sys_init_module
.long sys_delete_module
.long sys_get_kernel_syms /* 130 */
.long sys_ni_syscall /* 130: old get_kernel_syms */
.long sys_quotactl
.long sys_getpgid
.long sys_fchdir
......@@ -514,7 +514,7 @@ sys_call_table:
.long sys_setresuid16
.long sys_getresuid16 /* 165 */
.long sys_ni_syscall /* for vm86 */
.long sys_query_module
.long sys_ni_syscall /* old sys_query_module */
.long sys_poll
.long sys_nfsservctl
.long sys_setresgid16 /* 170 */
......@@ -596,7 +596,11 @@ sys_call_table:
.long sys_io_submit
.long sys_io_cancel
.long sys_exit_group
.rept 255-248
.long sys_epoll_create
.long sys_epoll_ctl /* 250 */
.long sys_epoll_wait
.long sys_set_tid_address
.rept 255-252
.long sys_ni_syscall
.endr
......
/*
* arch/s390x/kernel/module.c - Kernel module help for s390x.
*
* S390 version
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* based on i386 version
* Copyright (C) 2001 Rusty Russell.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#if 0
#define DEBUGP printk
#else
#define DEBUGP(fmt , ...)
#endif
void *module_alloc(unsigned long size)
{
if (size == 0)
return NULL;
return vmalloc(size);
}
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
{
vfree(module_region);
/* FIXME: If module_region == mod->init_region, trim exception
table entries. */
}
/* s390/s390x needs additional memory for GOT/PLT sections. */
long module_core_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
// FIXME: add space needed for GOT/PLT
return module->core_size;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->init_size;
}
int apply_relocate(Elf_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
unsigned int i;
ElfW(Rel) *rel = (void *)sechdrs[relsec].sh_offset;
ElfW(Sym) *sym;
ElfW(Addr) *location;
DEBUGP("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset
+ rel[i].r_offset;
/* This is the symbol it is referring to */
sym = (ElfW(Sym) *)sechdrs[symindex].sh_offset
+ ELFW(R_SYM)(rel[i].r_info);
if (!sym->st_value) {
printk(KERN_WARNING "%s: Unknown symbol %s\n",
me->name, strtab + sym->st_name);
return -ENOENT;
}
switch (ELF_R_TYPE(rel[i].r_info)) {
case R_390_8: /* Direct 8 bit. */
*(u8*) location += sym->st_value;
break;
case R_390_12: /* Direct 12 bit. */
*(u16*) location = (*(u16*) location & 0xf000) |
(sym->st_value & 0xfff);
break;
case R_390_16: /* Direct 16 bit. */
*(u16*) location += sym->st_value;
break;
case R_390_32: /* Direct 32 bit. */
*(u32*) location += sym->st_value;
break;
case R_390_PC16: /* PC relative 16 bit. */
*(u16*) location += sym->st_value
- (unsigned long )location;
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
*(u16*) location += (sym->st_value
- (unsigned long )location) >> 1;
case R_390_PC32: /* PC relative 32 bit. */
*(u32*) location += sym->st_value
- (unsigned long )location;
break;
case R_390_GOT12: /* 12 bit GOT offset. */
case R_390_GOT16: /* 16 bit GOT offset. */
case R_390_GOT32: /* 32 bit GOT offset. */
// FIXME: TODO
break;
case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
case R_390_PLT32: /* 32 bit PC relative PLT address. */
// FIXME: TODO
break;
case R_390_GLOB_DAT: /* Create GOT entry. */
case R_390_JMP_SLOT: /* Create PLT entry. */
*location = sym->st_value;
break;
case R_390_RELATIVE: /* Adjust by program base. */
// FIXME: TODO
break;
case R_390_GOTOFF: /* 32 bit offset to GOT. */
// FIXME: TODO
break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
// FIXME: TODO
break;
default:
printk(KERN_ERR "module %s: Unknown relocation: %lu\n",
me->name,
(unsigned long)ELF_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
return 0;
}
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
me->name);
return -ENOEXEC;
}
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
return 0;
}
......@@ -164,9 +164,9 @@ __setup("condev=", condev_setup);
static int __init conmode_setup(char *str)
{
#if defined(CONFIG_HWC_CONSOLE)
if (strncmp(str, "hwc", 4) == 0)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
SET_CONSOLE_SCLP;
#endif
#if defined(CONFIG_TN3215_CONSOLE)
if (strncmp(str, "3215", 5) == 0)
......@@ -198,8 +198,8 @@ static void __init conmode_default(void)
*/
cpcmd("TERM CONMODE 3215", NULL, 0);
if (ptr == NULL) {
#if defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
return;
}
......@@ -208,16 +208,16 @@ static void __init conmode_default(void)
SET_CONSOLE_3270;
#elif defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215;
#elif defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
} else if (strncmp(ptr + 8, "3215", 4) == 0) {
#if defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215;
#elif defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270;
#elif defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
}
} else if (MACHINE_IS_P390) {
......@@ -227,8 +227,8 @@ static void __init conmode_default(void)
SET_CONSOLE_3270;
#endif
} else {
#if defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
}
}
......
......@@ -274,7 +274,7 @@ void machine_power_off_smp(void)
void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
{
int bits;
unsigned long bits;
/*
* handle bit signal external calls
......@@ -282,9 +282,7 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
* For the ec_schedule signal we have to do nothing. All the work
* is done automatically when we return from the interrupt.
*/
do {
bits = atomic_read(&S390_lowcore.ext_call_fast);
} while (atomic_compare_and_swap(bits,0,&S390_lowcore.ext_call_fast));
bits = xchg(&S390_lowcore.ext_call_fast, 0);
if (test_bit(ec_call_function, &bits))
do_call_function();
......@@ -296,13 +294,12 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
*/
static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
{
struct _lowcore *lowcore = lowcore_ptr[cpu];
sigp_ccode ccode;
/*
* Set signaling bit in lowcore of target cpu and kick it
*/
atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast);
ccode = signal_processor(cpu, sigp_external_call);
return ccode;
}
......@@ -323,7 +320,7 @@ static void smp_ext_bitcall_others(ec_bit_sig sig)
/*
* Set signaling bit in lowcore of target cpu and kick it
*/
atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
set_bit(sig, (unsigned long *) &lowcore_ptr[i]->ext_call_fast);
while (signal_processor(i, sigp_external_call) == sigp_busy)
udelay(10);
}
......
......@@ -140,7 +140,6 @@ static inline __u32 div64_32(__u64 dividend, __u32 divisor)
*/
static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
{
int cpu = smp_processor_id();
__u64 tmp;
__u32 ticks;
......
......@@ -72,8 +72,8 @@ extern char _stext, _etext;
#ifdef CONFIG_MODULES
extern struct module *module_list;
extern struct module kernel_module;
/* FIXME: Accessed without a lock --RR */
extern struct list_head modules;
static inline int kernel_text_address(unsigned long addr)
{
......@@ -84,11 +84,11 @@ static inline int kernel_text_address(unsigned long addr)
addr <= (unsigned long) &_etext)
return 1;
for (mod = module_list; mod != &kernel_module; mod = mod->next) {
list_for_each_entry(mod, &modules, list) {
/* mod_bound tests for addr being inside the vmalloc'ed
* module area. Of course it'd be better to test only
* for the .text subset... */
if (mod_bound(addr, 0, mod)) {
if (mod_bound((void*)addr, 0, mod)) {
retval = 1;
break;
}
......
/*
* arch/s390/lib/uaccess.S
* fixup routines for copy_{from|to}_user functions.
* __copy_{from|to}_user functions.
*
* s390
* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Copyright (C) 2000,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* These functions have standard call interface
......@@ -22,17 +22,23 @@ __copy_from_user_asm:
1: sacf 0
lr %r2,%r5
br %r14
2: sll %r4,1
srl %r4,1
lhi %r3,-4096
sll %r3,1
srl %r3,1
n %r3,__LC_TRANS_EXC_ADDR
sr %r3,%r4
jm 1b
j 0b
2: lhi %r1,-4096
lr %r3,%r4
slr %r3,%r1 # %r3 = %r4 + 4096
nr %r3,%r1 # %r3 = (%r4 + 4096) & -4096
slr %r3,%r4 # %r3 = #bytes to next user page boundary
clr %r5,%r3 # copy crosses next page boundary ?
jnh 1b # no, this page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
3: mvcle %r2,%r4,0
jo 3b
j 1b
.section __ex_table,"a"
.long 0b,2b
.long 3b,1b
.previous
.align 4
......@@ -46,17 +52,23 @@ __copy_to_user_asm:
1: sacf 0
lr %r2,%r3
br %r14
2: sll %r4,1
srl %r4,1
lhi %r5,-4096
sll %r5,1
srl %r5,1
n %r5,__LC_TRANS_EXC_ADDR
sr %r5,%r4
jm 1b
j 0b
2: lhi %r1,-4096
lr %r5,%r4
slr %r5,%r1 # %r5 = %r4 + 4096
nr %r5,%r1 # %r5 = (%r4 + 4096) & -4096
slr %r5,%r4 # %r5 = #bytes to next user page boundary
clr %r3,%r5 # copy crosses next page boundary ?
jnh 1b # no, the current page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
3: mvcle %r4,%r2,0
jo 3b
j 1b
.section __ex_table,"a"
.long 0b,2b
.long 3b,1b
.previous
.align 4
......@@ -71,18 +83,26 @@ __clear_user_asm:
0: mvcle %r4,%r2,0
jo 0b
1: sacf 0
lr %r2,%r3
br %r14
2: sll %r4,1
srl %r4,1
lhi %r5,-4096
sll %r5,1
srl %r5,1
n %r5,__LC_TRANS_EXC_ADDR
sr %r5,%r4
jm 1b
j 0b
2: lr %r2,%r5
lhi %r1,-4096
slr %r5,%r1 # %r5 = %r4 + 4096
nr %r5,%r1 # %r5 = (%r4 + 4096) & -4096
slr %r5,%r4 # %r5 = #bytes to next user page boundary
clr %r2,%r5 # copy crosses next page boundary ?
jnh 1b # no, the current page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
slr %r2,%r5
3: mvcle %r4,%r2,0
jo 3b
j 1b
4: alr %r2,%r5
j 1b
.section __ex_table,"a"
.long 0b,2b
.long 3b,4b
.previous
......@@ -42,6 +42,7 @@ extern spinlock_t modlist_lock;
unsigned long
search_exception_table(unsigned long addr)
{
struct list_head *i;
unsigned long ret = 0;
#ifndef CONFIG_MODULES
......@@ -52,16 +53,17 @@ search_exception_table(unsigned long addr)
return ret;
#else
unsigned long flags;
/* The kernel is the last "module" -- no need to treat it special. */
struct module *mp;
addr &= 0x7fffffff; /* remove amode bit from address */
/* The kernel is the last "module" -- no need to treat it special. */
spin_lock_irqsave(&modlist_lock, flags);
for (mp = module_list; mp != NULL; mp = mp->next) {
if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
list_for_each(i, &extables) {
struct exception_table *ex
= list_entry(i, struct exception_table, list);
if (ex->num_entries == 0)
continue;
ret = search_one_table(mp->ex_table_start,
mp->ex_table_end - 1, addr);
ret = search_one_table(ex->entry,
ex->entry + ex->num_entries - 1, addr);
if (ret) {
ret = ret | PSW_ADDR_AMODE31;
break;
......
......@@ -188,7 +188,7 @@ void free_initmem(void)
free_page(addr);
totalram_pages++;
}
printk ("Freeing unused kernel memory: %dk freed\n",
printk ("Freeing unused kernel memory: %ldk freed\n",
((unsigned long)&__init_end - (unsigned long)&__init_begin) >> 10);
}
......
......@@ -25,6 +25,7 @@ SECTIONS
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
. = ALIGN(64);
__start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;
......@@ -40,18 +41,31 @@ SECTIONS
CONSTRUCTORS
}
. = ALIGN(4096);
__nosave_begin = .;
.data_nosave : { *(.data.nosave) }
. = ALIGN(4096);
__nosave_end = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
_edata = .; /* End of data section */
. = ALIGN(8192); /* init_task */
.data.init_task : { *(.data.init_task) }
/* will be freed after init */
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
.init.text : { *(.init.text) }
.init.data : { *(.init.data) }
. = ALIGN(256);
__setup_start = .;
.setup.init : { *(.setup.init) }
.init.setup : { *(.init.setup) }
__setup_end = .;
__initcall_start = .;
.initcall.init : {
......@@ -65,23 +79,29 @@ SECTIONS
}
__initcall_end = .;
. = ALIGN(256);
__initramfs_start = .;
.init.ramfs : { *(.init.initramfs) }
__initramfs_end = .;
. = ALIGN(256);
__per_cpu_start = .;
.data.percpu : { *(.data.percpu) }
__per_cpu_end = .;
. = ALIGN(4096);
__init_end = .;
/* freed after init ends here */
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
__bss_start = .; /* BSS */
.bss : { *(.bss) }
__bss_stop = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
_end = . ;
__bss_start = .; /* BSS */
.bss : {
*(.bss)
/* Sections to be discarded */
/DISCARD/ : {
*(.exit.text)
*(.exit.data)
*(.exitcall.exit)
}
_end = . ;
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
......
......@@ -11,38 +11,6 @@ config SWAP
bool
default y
config ISA
bool
help
Find out whether you have ISA slots on your motherboard. ISA is the
name of a bus system, i.e. the way the CPU talks to the other stuff
inside your box. Other bus systems are PCI, EISA, MicroChannel
(MCA) or VESA. ISA is an older system, now being displaced by PCI;
newer boards don't support it. If you have ISA, say Y, otherwise N.
config EISA
bool
---help---
The Extended Industry Standard Architecture (EISA) bus was
developed as an open alternative to the IBM MicroChannel bus.
The EISA bus provided some of the features of the IBM MicroChannel
bus while maintaining backward compatibility with cards made for
the older ISA bus. The EISA bus saw limited use between 1988 and
1995 when it was made obsolete by the PCI bus.
Say Y here if you are building a kernel for an EISA-based machine.
Otherwise, say N.
config MCA
bool
help
MicroChannel Architecture is found in some IBM PS/2 machines and
laptops. It is a bus system similar to PCI or ISA. See
<file:Documentation/mca.txt> (and especially the web page given
there) before attempting to build an MCA bus kernel.
config RWSEM_GENERIC_SPINLOCK
bool
......@@ -108,6 +76,13 @@ config NR_CPUS
int "Maximum number of CPUs (2-64)"
depends on SMP
default "64"
help
This allows you to specify the maximum number of CPUs which this
kernel will support. The maximum supported value is 64 and the
minimum value which makes sense is 2.
This is purely to save memory - each supported CPU adds
approximately sixteen kilobytes to the kernel image.
config S390_SUPPORT
bool "Kernel support for 31 bit emulation"
......@@ -161,6 +136,14 @@ comment "Misc"
config PREEMPT
bool "Preemptible Kernel"
help
This option reduces the latency of the kernel when reacting to
real-time or interactive events by allowing a low priority process to
be preempted even if it is in kernel mode executing a system call.
This allows applications to run more reliably even when the system is
under load.
Say N if you are unsure.
config IPL
bool "Builtin IPL record support"
......
......@@ -16,7 +16,8 @@
LDFLAGS := -m elf64_s390
OBJCOPYFLAGS := -O binary
LDFLAGS_vmlinux := -e start
MODFLAGS += -fpic
MODFLAGS += -fpic -D__PIC__
LDFLAGS_BLOB := --format binary --oformat elf64-s390
CFLAGS += -pipe -fno-strength-reduce
......@@ -28,18 +29,14 @@ libs-y += arch/s390x/lib/
all: image listing
listing: vmlinux
@$(MAKEBOOT) listing
makeboot = $(call descend,arch/$(ARCH)/boot,$(1))
BOOTIMAGE= arch/$(ARCH)/boot/image
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
image: vmlinux
@$(MAKEBOOT) image
install: vmlinux
@$(MAKEBOOT) BOOTIMAGE=image install
listing install image: vmlinux
+@$(call makeboot,BOOTIMAGE=$(BOOTIMAGE) $@)
archclean:
+@$(call makeboot,clean)
archmrproper:
......
......@@ -6,22 +6,22 @@ EXTRA_AFLAGS := -traditional
include $(TOPDIR)/Rules.make
%.lnk: %.o
$(LD) -Ttext 0x0 -o $@ $<
quiet_cmd_listing = OBJDUMP $(echo_target)
cmd_listing = $(OBJDUMP) --disassemble --disassemble-all \
--disassemble-zeroes --reloc vmlinux > $@
%.boot: %.lnk
$(OBJCOPY) $(OBJCOPYFLAGS) $< $@
$(obj)/image: vmlinux
$(call if_changed,objcopy)
image: $(TOPDIR)/vmlinux \
iplfba.boot ipleckd.boot ipldump.boot
$(OBJCOPY) $(OBJCOPYFLAGS) $< $@
$(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aUw] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map
$(obj)/listing: vmlinux
$(call if_changed,listing)
listing: ../../../vmlinux
$(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing
image: $(obj)/image
listing: $(obj)/listing
clean:
rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
rm -f $(obj)/image $(obj)/listing
install: $(CONFIGURE) $(BOOTIMAGE)
sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
sh -x $(obj)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map Kerntypes "$(INSTALL_PATH)"
#
# Automatically generated make config: don't edit
#
# CONFIG_ISA is not set
# CONFIG_EISA is not set
# CONFIG_MCA is not set
# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
CONFIG_MMU=y
CONFIG_SWAP=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
# CONFIG_GENERIC_BUST_SPINLOCK is not set
CONFIG_ARCH_S390=y
CONFIG_ARCH_S390X=y
......@@ -14,6 +11,7 @@ CONFIG_ARCH_S390X=y
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_CONFIDENTIAL=y
#
# General setup
......@@ -27,7 +25,8 @@ CONFIG_SYSCTL=y
# Loadable module support
#
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_UNLOAD is not set
# CONFIG_MODULE_FORCE_UNLOAD is not set
CONFIG_KMOD=y
#
......@@ -38,9 +37,9 @@ CONFIG_KMOD=y
# Processor type and features
#
CONFIG_SMP=y
CONFIG_NR_CPUS=64
CONFIG_S390_SUPPORT=y
CONFIG_BINFMT_ELF32=y
CONFIG_NR_CPUS=64
#
# I/O subsystem configuration
......@@ -58,7 +57,7 @@ CONFIG_IPL=y
CONFIG_IPL_VM=y
CONFIG_KCORE_ELF=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
CONFIG_BINFMT_MISC=m
# CONFIG_PROCESS_DEBUG is not set
CONFIG_PFAULT=y
# CONFIG_SHARED_KERNEL is not set
......@@ -72,18 +71,16 @@ CONFIG_SCSI=m
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=m
CONFIG_SD_EXTRA_DEVS=40
CONFIG_CHR_DEV_ST=m
# CONFIG_CHR_DEV_OSST is not set
CONFIG_CHR_DEV_OSST=m
CONFIG_BLK_DEV_SR=m
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_SR_EXTRA_DEVS=10
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=m
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
CONFIG_SCSI_MULTI_LUN=y
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SCSI_REPORT_LUNS is not set
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
......@@ -91,41 +88,36 @@ CONFIG_SCSI_LOGGING=y
#
# SCSI low-level drivers
#
# CONFIG_SCSI_7000FASST is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_DPT_I2O is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
# CONFIG_SCSI_MEGARAID is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_IMM is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PCI2000 is not set
# CONFIG_SCSI_PCI2220I is not set
# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_SYM53C416 is not set
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
CONFIG_SCSI_ACARD=m
CONFIG_SCSI_AIC7XXX=m
CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
CONFIG_AIC7XXX_RESET_DELAY_MS=15000
# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
CONFIG_SCSI_AIC7XXX_OLD=m
CONFIG_SCSI_DPT_I2O=m
CONFIG_SCSI_ADVANSYS=m
CONFIG_SCSI_IN2000=m
CONFIG_SCSI_MEGARAID=m
CONFIG_SCSI_BUSLOGIC=m
# CONFIG_SCSI_OMIT_FLASHPOINT is not set
CONFIG_SCSI_EATA=m
CONFIG_SCSI_EATA_TAGGED_QUEUE=y
# CONFIG_SCSI_EATA_LINKED_COMMANDS is not set
CONFIG_SCSI_EATA_MAX_TAGS=16
CONFIG_SCSI_EATA_DMA=m
CONFIG_SCSI_EATA_PIO=m
CONFIG_SCSI_FUTURE_DOMAIN=m
CONFIG_SCSI_GDTH=m
CONFIG_SCSI_GENERIC_NCR5380=m
# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
# CONFIG_SCSI_GENERIC_NCR53C400 is not set
CONFIG_SCSI_PCI2000=m
CONFIG_SCSI_PCI2220I=m
CONFIG_SCSI_U14_34F=m
# CONFIG_SCSI_U14_34F_LINKED_COMMANDS is not set
CONFIG_SCSI_U14_34F_MAX_TAGS=8
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
CONFIG_SCSI_DEBUG=m
#
# PCMCIA SCSI adapter support
......@@ -135,10 +127,10 @@ CONFIG_SCSI_LOGGING=y
#
# Block device drivers
#
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_NBD=m
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=24576
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_BLK_DEV_INITRD=y
CONFIG_BLK_DEV_XPRAM=m
......@@ -148,65 +140,63 @@ CONFIG_BLK_DEV_XPRAM=m
CONFIG_DASD=y
CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y
# CONFIG_DASD_DIAG is not set
#
# Multi-device support (RAID and LVM)
#
CONFIG_MD=y
CONFIG_BLK_DEV_MD=m
# CONFIG_MD_LINEAR is not set
CONFIG_BLK_DEV_MD=y
CONFIG_MD_LINEAR=m
CONFIG_MD_RAID0=m
CONFIG_MD_RAID1=m
CONFIG_MD_RAID5=m
# CONFIG_MD_MULTIPATH is not set
# CONFIG_BLK_DEV_LVM is not set
CONFIG_MD_MULTIPATH=m
# CONFIG_BLK_DEV_DM is not set
#
# Character device drivers
#
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
CONFIG_UNIX98_PTY_COUNT=2048
#
# S/390 character device drivers
#
CONFIG_TN3270=y
CONFIG_TN3270_CONSOLE=y
# CONFIG_TN3270 is not set
CONFIG_TN3215=y
CONFIG_TN3215_CONSOLE=y
CONFIG_HWC=y
CONFIG_HWC_CONSOLE=y
CONFIG_HWC_CPI=m
CONFIG_SCLP=y
CONFIG_SCLP_TTY=y
CONFIG_SCLP_CONSOLE=y
CONFIG_SCLP_CPI=m
CONFIG_S390_TAPE=m
#
# S/390 tape interface support
#
CONFIG_S390_TAPE_CHAR=y
CONFIG_S390_TAPE_BLOCK=y
#
# S/390 tape hardware support
#
CONFIG_S390_TAPE_3490=m
CONFIG_S390_TAPE_3480=m
CONFIG_S390_TAPE_34XX=m
#
# Network device drivers
#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
CONFIG_DUMMY=m
CONFIG_BONDING=m
CONFIG_EQUALIZER=m
CONFIG_TUN=m
CONFIG_NET_ETHERNET=y
CONFIG_TR=y
# CONFIG_TR is not set
# CONFIG_FDDI is not set
#
# S/390 network device drivers
#
CONFIG_CHANDEV=y
CONFIG_HOTPLUG=y
CONFIG_LCS=m
CONFIG_CTC=m
......@@ -221,6 +211,7 @@ CONFIG_PACKET=y
# CONFIG_NETFILTER is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
......@@ -231,7 +222,10 @@ CONFIG_IP_MULTICAST=y
# CONFIG_ARPD is not set
# CONFIG_INET_ECN is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_IPV6 is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_XFRM_USER is not set
CONFIG_IPV6=m
#
# SCTP Configuration (EXPERIMENTAL)
......@@ -241,9 +235,6 @@ CONFIG_IPV6_SCTP__=y
# CONFIG_ATM is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_LLC is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_DEV_APPLETALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
......@@ -257,66 +248,73 @@ CONFIG_NET_FASTROUTE=y
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
# CONFIG_NET_SCH_HTB is not set
CONFIG_NET_SCH_CSZ=m
CONFIG_NET_SCH_PRIO=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TEQL=m
CONFIG_NET_SCH_TBF=m
CONFIG_NET_SCH_GRED=m
CONFIG_NET_SCH_DSMARK=m
CONFIG_NET_QOS=y
CONFIG_NET_ESTIMATOR=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_TCINDEX=m
CONFIG_NET_CLS_ROUTE4=m
CONFIG_NET_CLS_ROUTE=y
CONFIG_NET_CLS_FW=m
CONFIG_NET_CLS_U32=m
CONFIG_NET_CLS_RSVP=m
CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_POLICE=y
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
#
# File systems
#
# CONFIG_QUOTA is not set
CONFIG_QUOTA=y
# CONFIG_QFMT_V1 is not set
# CONFIG_QFMT_V2 is not set
CONFIG_QUOTACTL=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_REISERFS_CHECK is not set
# CONFIG_REISERFS_PROC_INFO is not set
# CONFIG_ADFS_FS is not set
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
CONFIG_EXT3_FS=y
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
# CONFIG_EXT3_FS is not set
# CONFIG_JBD is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS_FS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_TMPFS is not set
CONFIG_RAMFS=y
# CONFIG_ISO9660_FS is not set
# CONFIG_JOLIET is not set
# CONFIG_ZISOFS is not set
# CONFIG_JFS_FS is not set
# CONFIG_JFS_DEBUG is not set
# CONFIG_JFS_STATISTICS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS_DEBUG is not set
# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
CONFIG_DEVFS_FS=y
# CONFIG_DEVFS_MOUNT is not set
# CONFIG_DEVFS_DEBUG is not set
# CONFIG_DEVPTS_FS is not set
# CONFIG_DEVFS_FS is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_QNX4FS_RW is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UDF_FS is not set
# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
# CONFIG_UFS_FS_WRITE is not set
# CONFIG_XFS_FS is not set
# CONFIG_XFS_RT is not set
# CONFIG_XFS_QUOTA is not set
#
# Network File Systems
......@@ -324,25 +322,20 @@ CONFIG_EXT2_FS=y
# CONFIG_CODA_FS is not set
# CONFIG_INTERMEZZO_FS is not set
CONFIG_NFS_FS=y
# CONFIG_NFS_V3 is not set
# CONFIG_ROOT_NFS is not set
CONFIG_NFS_V3=y
# CONFIG_NFS_V4 is not set
CONFIG_NFSD=y
# CONFIG_NFSD_V3 is not set
CONFIG_NFSD_V3=y
# CONFIG_NFSD_V4 is not set
# CONFIG_NFSD_TCP is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_EXPORTFS=y
# CONFIG_CIFS is not set
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
# CONFIG_NCPFS_STRONG is not set
# CONFIG_NCPFS_NFS_NS is not set
# CONFIG_NCPFS_OS2_NS is not set
# CONFIG_NCPFS_SMALLDOS is not set
# CONFIG_NCPFS_NLS is not set
# CONFIG_NCPFS_EXTRAS is not set
# CONFIG_ZISOFS_FS is not set
# CONFIG_AFS_FS is not set
#
# Partition Types
......@@ -360,8 +353,6 @@ CONFIG_IBM_PARTITION=y
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_EFI_PARTITION is not set
# CONFIG_SMB_NLS is not set
# CONFIG_NLS is not set
#
# Kernel hacking
......@@ -373,9 +364,20 @@ CONFIG_MAGIC_SYSRQ=y
#
CONFIG_SECURITY_CAPABILITIES=y
#
# Cryptographic options
#
CONFIG_CRYPTO=y
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_TEST is not set
#
# Library routines
#
# CONFIG_CRC32 is not set
# CONFIG_ZLIB_INFLATE is not set
# CONFIG_ZLIB_DEFLATE is not set
......@@ -12,7 +12,7 @@ obj-y := entry.o bitmap.o traps.o time.o process.o \
setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
semaphore.o reipl.o s390_ext.o debug.o
obj-$(CONFIG_MODULES) += s390_ksyms.o
obj-$(CONFIG_MODULES) += s390_ksyms.o module.o
obj-$(CONFIG_SMP) += smp.o
#
......
......@@ -503,10 +503,10 @@ sys_call_table:
.long SYSCALL(sys_adjtimex,sys32_adjtimex_wrapper)
.long SYSCALL(sys_mprotect,sys32_mprotect_wrapper) /* 125 */
.long SYSCALL(sys_sigprocmask,sys32_sigprocmask_wrapper)
.long SYSCALL(sys_create_module,sys32_create_module_wrapper)
.long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old "create module" */
.long SYSCALL(sys_init_module,sys32_init_module_wrapper)
.long SYSCALL(sys_delete_module,sys32_delete_module_wrapper)
.long SYSCALL(sys_get_kernel_syms,sys32_get_kernel_syms_wrapper) /* 130 */
.long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 130: old get_kernel_syms */
.long SYSCALL(sys_quotactl,sys32_quotactl_wrapper)
.long SYSCALL(sys_getpgid,sys32_getpgid_wrapper)
.long SYSCALL(sys_fchdir,sys32_fchdir_wrapper)
......@@ -543,7 +543,7 @@ sys_call_table:
.long SYSCALL(sys_ni_syscall,sys32_setresuid16_wrapper) /* old setresuid16 syscall */
.long SYSCALL(sys_ni_syscall,sys32_getresuid16_wrapper) /* old getresuid16 syscall */
.long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* for vm86 */
.long SYSCALL(sys_query_module,sys32_query_module_wrapper)
.long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old sys_query_module */
.long SYSCALL(sys_poll,sys32_poll_wrapper)
.long SYSCALL(sys_nfsservctl,sys32_nfsservctl_wrapper)
.long SYSCALL(sys_ni_syscall,sys32_setresgid16_wrapper) /* old setresgid16 syscall */
......@@ -625,7 +625,11 @@ sys_call_table:
.long SYSCALL(sys_io_submit,sys_ni_syscall)
.long SYSCALL(sys_io_cancel,sys_ni_syscall)
.long SYSCALL(sys_exit_group,sys32_exit_group_wrapper)
.rept 255-248
.long SYSCALL(sys_epoll_create,sys_ni_syscall)
.long SYSCALL(sys_epoll_ctl,sys_ni_syscall)
.long SYSCALL(sys_epoll_wait,sys_ni_syscall)
.long SYSCALL(sys_set_tid_address,sys_ni_syscall)
.rept 255-252
.long SYSCALL(sys_ni_syscall,sys_ni_syscall)
.endr
......
......@@ -1024,6 +1024,8 @@ int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
static void ioctl32_insert(struct ioctl32_list *entry)
{
int hash = ioctl32_hash(entry->handler.cmd);
entry->next = 0;
if (!ioctl32_hash_table[hash])
ioctl32_hash_table[hash] = entry;
else {
......@@ -1032,10 +1034,51 @@ static void ioctl32_insert(struct ioctl32_list *entry)
while (l->next)
l = l->next;
l->next = entry;
entry->next = 0;
}
}
int register_ioctl32_conversion(unsigned int cmd,
int (*handler)(unsigned int, unsigned int,
unsigned long, struct file *))
{
struct ioctl32_list *l, *new;
int hash;
hash = ioctl32_hash(cmd);
for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next)
if (l->handler.cmd == cmd)
return -EBUSY;
new = kmalloc(sizeof(struct ioctl32_list), GFP_KERNEL);
if (new == NULL)
return -ENOMEM;
new->handler.cmd = cmd;
new->handler.function = (void *) handler;
ioctl32_insert(new);
return 0;
}
int unregister_ioctl32_conversion(unsigned int cmd)
{
struct ioctl32_list *p, *l;
int hash;
hash = ioctl32_hash(cmd);
p = NULL;
for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next) {
if (l->handler.cmd == cmd)
break;
p = l;
}
if (l == NULL)
return -ENOENT;
if (p == NULL)
ioctl32_hash_table[hash] = l->next;
else
p->next = l->next;
kfree(l);
return 0;
}
static int __init init_ioctl32(void)
{
int i;
......
......@@ -2492,8 +2492,23 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up
CMSG32_ALIGN(sizeof(struct cmsghdr32)));
kcmsg32->cmsg_len = clen32;
switch (kcmsg32->cmsg_type) {
/*
* The timestamp type's data needs to be converted
* from 64-bit time values to 32-bit time values
*/
case SO_TIMESTAMP: {
__kernel_time_t32* ptr_time32 = CMSG32_DATA(kcmsg32);
__kernel_time_t* ptr_time = CMSG_DATA(ucmsg);
get_user(*ptr_time32, ptr_time);
get_user(*(ptr_time32+1), ptr_time+1);
kcmsg32->cmsg_len -= 2*(sizeof(__kernel_time_t) -
sizeof(__kernel_time_t32));
}
default:;
}
ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64));
wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32));
wp = (((char *)kcmsg32) + CMSG32_ALIGN(kcmsg32->cmsg_len));
}
/* Copy back fixed up data, and adjust pointers. */
......@@ -2516,148 +2531,6 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up
kmsg->msg_control = (void *) orig_cmsg_uptr;
}
#if 0
asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
struct iovec iov[UIO_FASTIOV];
unsigned char ctl[sizeof(struct cmsghdr) + 20];
unsigned char *ctl_buf = ctl;
struct msghdr kern_msg;
int err, total_len;
if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
return -EFAULT;
if(kern_msg.msg_iovlen > UIO_MAXIOV)
return -EINVAL;
err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
if (err < 0)
goto out;
total_len = err;
if(kern_msg.msg_controllen) {
err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl));
if(err)
goto out_freeiov;
ctl_buf = kern_msg.msg_control;
}
kern_msg.msg_flags = user_flags;
sock = sockfd_lookup(fd, &err);
if (sock != NULL) {
if (sock->file->f_flags & O_NONBLOCK)
kern_msg.msg_flags |= MSG_DONTWAIT;
err = sock_sendmsg(sock, &kern_msg, total_len);
sockfd_put(sock);
}
/* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
if(ctl_buf != ctl)
kfree(ctl_buf);
out_freeiov:
if(kern_msg.msg_iov != iov)
kfree(kern_msg.msg_iov);
out:
return err;
}
asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags)
{
struct iovec iovstack[UIO_FASTIOV];
struct msghdr kern_msg;
char addr[MAX_SOCK_ADDR];
struct socket *sock;
struct iovec *iov = iovstack;
struct sockaddr *uaddr;
int *uaddr_len;
unsigned long cmsg_ptr;
int err, total_len, len = 0;
if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
return -EFAULT;
if(kern_msg.msg_iovlen > UIO_MAXIOV)
return -EINVAL;
uaddr = kern_msg.msg_name;
uaddr_len = &user_msg->msg_namelen;
err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE);
if (err < 0)
goto out;
total_len = err;
cmsg_ptr = (unsigned long) kern_msg.msg_control;
kern_msg.msg_flags = 0;
sock = sockfd_lookup(fd, &err);
if (sock != NULL) {
struct sock_iocb *si;
struct kiocb iocb;
if (sock->file->f_flags & O_NONBLOCK)
user_flags |= MSG_DONTWAIT;
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = &kern_msg;
si->size = total_len;
si->flags = user_flags;
memset(si->scm, 0, sizeof(*si->scm));
err = sock->ops->recvmsg(&iocb, sock, &kern_msg, total_len,
user_flags, si->scm);
if (-EIOCBQUEUED == err)
err = wait_on_sync_kiocb(&iocb);
if(err >= 0) {
len = err;
if(!kern_msg.msg_control) {
if(sock->passcred || si->scm->fp)
kern_msg.msg_flags |= MSG_CTRUNC;
if(si->scm->fp)
__scm_destroy(si->scm);
} else {
/* If recvmsg processing itself placed some
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) kern_msg.msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(&kern_msg,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(si->scm->creds),
&si->scm->creds);
if(si->scm->fp != NULL)
scm_detach_fds32(&kern_msg, si->scm);
}
}
sockfd_put(sock);
}
if(uaddr != NULL && err >= 0)
err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
if(cmsg_ptr != 0 && err >= 0) {
unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control);
__kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr);
err |= __put_user(uclen, &user_msg->msg_controllen);
}
if(err >= 0)
err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
if(kern_msg.msg_iov != iov)
kfree(kern_msg.msg_iov);
out:
if(err < 0)
return err;
return len;
}
#endif
/*
* BSD sendmsg interface
*/
......@@ -2742,6 +2615,63 @@ int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags)
return err;
}
static __inline__ void
scm_recv32(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags, unsigned long cmsg_ptr)
{
if(!msg->msg_control)
{
if(sock->passcred || scm->fp)
msg->msg_flags |= MSG_CTRUNC;
scm_destroy(scm);
return;
}
/* If recvmsg processing itself placed some
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) msg->msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(msg, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(msg,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(scm->creds), &scm->creds);
if(!scm->fp)
return;
scm_detach_fds32(msg, scm);
}
static int
sock_recvmsg32(struct socket *sock, struct msghdr *msg, int size, int flags,
unsigned long cmsg_ptr)
{
struct sock_iocb *si;
struct kiocb iocb;
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = msg;
si->size = size;
si->flags = flags;
memset(si->scm, 0, sizeof(*si->scm));
size = sock->ops->recvmsg(&iocb, sock, msg, size, flags, si->scm);
if (size >= 0)
scm_recv32(sock, msg, si->scm, flags, cmsg_ptr);
if (-EIOCBQUEUED == size)
size = wait_on_sync_kiocb(&iocb);
return size;
}
/*
* BSD recvmsg interface
*/
......@@ -2755,8 +2685,6 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
struct msghdr msg_sys;
unsigned long cmsg_ptr;
int err, iov_size, total_len, len;
struct sock_iocb *si;
struct kiocb iocb;
/* kernel mode address */
char addr[MAX_SOCK_ADDR];
......@@ -2803,20 +2731,7 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = &msg_sys;
si->size = total_len;
si->flags = flags;
memset(si->scm, 0, sizeof(*si->scm));
err = sock->ops->recvmsg(&iocb, sock, &msg_sys, total_len,
flags, si->scm);
if (-EIOCBQUEUED == err)
err = wait_on_sync_kiocb(&iocb);
err = sock_recvmsg32(sock, &msg_sys, total_len, flags, cmsg_ptr);
if (err < 0)
goto out_freeiov;
len = err;
......@@ -2828,27 +2743,13 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
if (err < 0)
goto out_freeiov;
}
if(!msg_sys.msg_control) {
if(sock->passcred || si->scm->fp)
msg_sys.msg_flags |= MSG_CTRUNC;
if(si->scm->fp)
__scm_destroy(si->scm);
} else {
/* If recvmsg processing itself placed some
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) msg_sys.msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(&msg_sys, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(&msg_sys,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(si->scm->creds), &si->scm->creds);
if(si->scm->fp != NULL)
scm_detach_fds32(&msg_sys, si->scm);
}
err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
if (err)
goto out_freeiov;
err = __put_user((__kernel_size_t32) ((unsigned long)msg_sys.msg_control - cmsg_ptr), &msg->msg_controllen);
if (err)
goto out_freeiov;
err = len;
out_freeiov:
if (iov != iovstack)
......@@ -2941,6 +2842,20 @@ asmlinkage int sys32_setsockopt(int fd, int level, int optname,
if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
return do_set_icmpv6_filter(fd, level, optname,
optval, optlen);
if (level == SOL_SOCKET &&
(optname == SO_SNDTIMEO || optname == SO_RCVTIMEO)) {
long ret;
struct timeval tmp;
mm_segment_t old_fs;
if (get_tv32(&tmp, (struct timeval32 *)optval ))
return -EFAULT;
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_setsockopt(fd, level, optname, (char *) &tmp, sizeof(struct timeval));
set_fs(old_fs);
return ret;
}
return sys_setsockopt(fd, level, optname, optval, optlen);
}
......@@ -3136,13 +3051,6 @@ sys32_execve(struct pt_regs regs)
#ifdef CONFIG_MODULES
extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size);
asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size)
{
return sys_create_module(name_user, (size_t)size);
}
extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user);
/* Hey, when you're trying to init module, take time and prepare us a nice 64bit
......@@ -3421,103 +3329,13 @@ qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret)
return error;
}
asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret)
{
struct module *mod;
int err;
lock_kernel();
if (name_user == 0) {
/* This finds "kernel_module" which is not exported. */
for(mod = module_list; mod->next != NULL; mod = mod->next)
;
} else {
long namelen;
char *name;
if ((namelen = get_mod_name(name_user, &name)) < 0) {
err = namelen;
goto out;
}
err = -ENOENT;
if (namelen == 0) {
/* This finds "kernel_module" which is not exported. */
for(mod = module_list; mod->next != NULL; mod = mod->next)
;
} else if ((mod = find_module(name)) == NULL) {
put_mod_name(name);
goto out;
}
put_mod_name(name);
}
switch (which)
{
case 0:
err = 0;
break;
case QM_MODULES:
err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret));
break;
case QM_DEPS:
err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
break;
case QM_REFS:
err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
break;
case QM_SYMBOLS:
err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
break;
case QM_INFO:
err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret));
break;
default:
err = -EINVAL;
break;
}
out:
unlock_kernel();
return err;
}
struct kernel_sym32 {
u32 value;
char name[60];
};
extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table);
asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table)
{
int len, i;
struct kernel_sym *tbl;
mm_segment_t old_fs;
len = sys_get_kernel_syms(NULL);
if (!table) return len;
tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL);
if (!tbl) return -ENOMEM;
old_fs = get_fs();
set_fs (KERNEL_DS);
sys_get_kernel_syms(tbl);
set_fs (old_fs);
for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) {
if (put_user (tbl[i].value, &table->value) ||
copy_to_user (table->name, tbl[i].name, 60))
break;
}
kfree (tbl);
return i;
}
#else /* CONFIG_MODULES */
asmlinkage unsigned long
sys32_create_module(const char *name_user, size_t size)
{
return -ENOSYS;
}
asmlinkage int
sys32_init_module(const char *name_user, struct module *mod_user)
{
......@@ -3530,24 +3348,6 @@ sys32_delete_module(const char *name_user)
return -ENOSYS;
}
asmlinkage int
sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
size_t *ret)
{
/* Let the program know about the new interface. Not that
it'll do them much good. */
if (which == 0)
return 0;
return -ENOSYS;
}
asmlinkage int
sys32_get_kernel_syms(struct kernel_sym *table)
{
return -ENOSYS;
}
#endif /* CONFIG_MODULES */
/* Stuff for NFS server syscalls... */
......@@ -4012,12 +3812,16 @@ typedef __kernel_ssize_t32 ssize_t32;
asmlinkage ssize_t32 sys32_pread64(unsigned int fd, char *ubuf,
__kernel_size_t32 count, u32 poshi, u32 poslo)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
}
asmlinkage ssize_t32 sys32_pwrite64(unsigned int fd, char *ubuf,
__kernel_size_t32 count, u32 poshi, u32 poslo)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
}
......@@ -4551,26 +4355,38 @@ asmlinkage int
sys32_futex(void *uaddr, int op, int val,
struct timespec32 *timeout32)
{
long ret;
struct timespec tmp, *timeout;
ret = -ENOMEM;
timeout = kmalloc(sizeof(*timeout), GFP_USER);
if (!timeout)
goto out;
struct timespec tmp;
mm_segment_t old_fs;
int ret;
ret = -EINVAL;
if (get_user (tmp.tv_sec, &timeout32->tv_sec) ||
get_user (tmp.tv_nsec, &timeout32->tv_nsec) ||
put_user (tmp.tv_sec, &timeout->tv_sec) ||
put_user (tmp.tv_nsec, &timeout->tv_nsec))
goto out_free;
get_user (tmp.tv_nsec, &timeout32->tv_nsec))
return -EINVAL;
ret = sys_futex(uaddr, op, val, timeout);
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_futex(uaddr, op, val, &tmp);
set_fs(old_fs);
out_free:
kfree(timeout);
out:
return ret;
}
asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count);
asmlinkage ssize_t32 sys32_read(unsigned int fd, char * buf, size_t count)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_read(fd, buf, count);
}
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count);
asmlinkage ssize_t32 sys32_write(unsigned int fd, char * buf, size_t count)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_write(fd, buf, count);
}
/*
* arch/s390x/kernel/module.c - Kernel module help for s390x.
*
* S390 version
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*
* based on i386 version
* Copyright (C) 2001 Rusty Russell.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#if 0
#define DEBUGP printk
#else
#define DEBUGP(fmt , ...)
#endif
void *module_alloc(unsigned long size)
{
if (size == 0)
return NULL;
return vmalloc(size);
}
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
{
vfree(module_region);
/* FIXME: If module_region == mod->init_region, trim exception
table entries. */
}
/* s390/s390x needs additional memory for GOT/PLT sections. */
long module_core_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
// FIXME: add space needed for GOT/PLT
return module->core_size;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->init_size;
}
int apply_relocate(Elf_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
unsigned int i;
ElfW(Rel) *rel = (void *)sechdrs[relsec].sh_offset;
ElfW(Sym) *sym;
ElfW(Addr) *location;
DEBUGP("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset
+ rel[i].r_offset;
/* This is the symbol it is referring to */
sym = (ElfW(Sym) *)sechdrs[symindex].sh_offset
+ ELFW(R_SYM)(rel[i].r_info);
if (!sym->st_value) {
printk(KERN_WARNING "%s: Unknown symbol %s\n",
me->name, strtab + sym->st_name);
return -ENOENT;
}
switch (ELF_R_TYPE(rel[i].r_info)) {
case R_390_8: /* Direct 8 bit. */
*(u8*) location += sym->st_value;
break;
case R_390_12: /* Direct 12 bit. */
*(u16*) location = (*(u16*) location & 0xf000) |
(sym->st_value & 0xfff);
break;
case R_390_16: /* Direct 16 bit. */
*(u16*) location += sym->st_value;
break;
case R_390_32: /* Direct 32 bit. */
*(u32*) location += sym->st_value;
break;
case R_390_64: /* Direct 64 bit. */
*(u64*) location += sym->st_value;
break;
case R_390_PC16: /* PC relative 16 bit. */
*(u16*) location += sym->st_value
- (unsigned long )location;
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
*(u16*) location += (sym->st_value
- (unsigned long )location) >> 1;
case R_390_PC32: /* PC relative 32 bit. */
*(u32*) location += sym->st_value
- (unsigned long )location;
break;
case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
*(u32*) location += (sym->st_value
- (unsigned long )location) >> 1;
break;
case R_390_PC64: /* PC relative 64 bit. */
*(u64*) location += sym->st_value
- (unsigned long )location;
break;
case R_390_GOT12: /* 12 bit GOT offset. */
case R_390_GOT16: /* 16 bit GOT offset. */
case R_390_GOT32: /* 32 bit GOT offset. */
case R_390_GOT64: /* 64 bit GOT offset. */
case R_390_GOTENT: /* 32 bit PC rel. to GOT entry >> 1. */
// FIXME: TODO
break;
case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
case R_390_PLT32: /* 32 bit PC relative PLT address. */
case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
case R_390_PLT64: /* 64 bit PC relative PLT address. */
// FIXME: TODO
break;
case R_390_GLOB_DAT: /* Create GOT entry. */
case R_390_JMP_SLOT: /* Create PLT entry. */
*location = sym->st_value;
break;
case R_390_RELATIVE: /* Adjust by program base. */
// FIXME: TODO
break;
case R_390_GOTOFF: /* 32 bit offset to GOT. */
// FIXME: TODO
break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
case R_390_GOTPCDBL: /* 32 bit PC rel. GOT shifted by 1. */
// FIXME: TODO
break;
default:
printk(KERN_ERR "module %s: Unknown relocation: %lu\n",
me->name,
(unsigned long)ELF_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
return 0;
}
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
me->name);
return -ENOEXEC;
}
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
return 0;
}
......@@ -60,6 +60,20 @@ EXPORT_SYMBOL(dump_fpu);
EXPORT_SYMBOL(overflowuid);
EXPORT_SYMBOL(overflowgid);
#ifdef CONFIG_S390_SUPPORT
/*
* Dynamically add/remove 31 bit ioctl conversion functions.
*/
extern int register_ioctl32_conversion(unsigned int cmd,
int (*handler)(unsigned int,
unsigned int,
unsigned long,
struct file *));
int unregister_ioctl32_conversion(unsigned int cmd);
EXPORT_SYMBOL(register_ioctl32_conversion);
EXPORT_SYMBOL(unregister_ioctl32_conversion);
#endif
/*
* misc.
*/
......
......@@ -164,9 +164,9 @@ __setup("condev=", condev_setup);
static int __init conmode_setup(char *str)
{
#if defined(CONFIG_HWC_CONSOLE)
if (strncmp(str, "hwc", 4) == 0)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
SET_CONSOLE_SCLP;
#endif
#if defined(CONFIG_TN3215_CONSOLE)
if (strncmp(str, "3215", 5) == 0)
......@@ -198,8 +198,8 @@ static void __init conmode_default(void)
*/
cpcmd("TERM CONMODE 3215", NULL, 0);
if (ptr == NULL) {
#if defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
return;
}
......@@ -208,16 +208,16 @@ static void __init conmode_default(void)
SET_CONSOLE_3270;
#elif defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215;
#elif defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
} else if (strncmp(ptr + 8, "3215", 4) == 0) {
#if defined(CONFIG_TN3215_CONSOLE)
SET_CONSOLE_3215;
#elif defined(CONFIG_TN3270_CONSOLE)
SET_CONSOLE_3270;
#elif defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#elif defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
}
} else if (MACHINE_IS_P390) {
......@@ -227,8 +227,8 @@ static void __init conmode_default(void)
SET_CONSOLE_3270;
#endif
} else {
#if defined(CONFIG_HWC_CONSOLE)
SET_CONSOLE_HWC;
#if defined(CONFIG_SCLP_CONSOLE)
SET_CONSOLE_SCLP;
#endif
}
}
......
......@@ -126,7 +126,6 @@ void do_settimeofday(struct timeval *tv)
*/
static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
{
int cpu = smp_processor_id();
__u64 tmp;
__u32 ticks;
......
......@@ -74,8 +74,8 @@ extern char _stext, _etext;
#ifdef CONFIG_MODULES
extern struct module *module_list;
extern struct module kernel_module;
/* FIXME: Accessed without a lock --RR */
extern struct list_head modules;
static inline int kernel_text_address(unsigned long addr)
{
......@@ -86,11 +86,11 @@ static inline int kernel_text_address(unsigned long addr)
addr <= (unsigned long) &_etext)
return 1;
for (mod = module_list; mod != &kernel_module; mod = mod->next) {
list_for_each_entry(mod, &modules, list) {
/* mod_bound tests for addr being inside the vmalloc'ed
* module area. Of course it'd be better to test only
* for the .text subset... */
if (mod_bound(addr, 0, mod)) {
if (mod_bound((void*)addr, 0, mod)) {
retval = 1;
break;
}
......
......@@ -17,14 +17,14 @@ sys32_read_wrapper:
llgfr %r2,%r2 # unsigned int
llgtr %r3,%r3 # char *
llgfr %r4,%r4 # size_t
jg sys_read # branch to sys_read
jg sys32_read # branch to sys_read
.globl sys32_write_wrapper
sys32_write_wrapper:
llgfr %r2,%r2 # unsigned int
llgtr %r3,%r3 # const char *
llgfr %r4,%r4 # size_t
jg sys_write # branch to system call
jg sys32_write # branch to system call
.globl sys32_open_wrapper
sys32_open_wrapper:
......@@ -564,12 +564,6 @@ sys32_sigprocmask_wrapper:
llgtr %r4,%r4 # old_sigset_emu31 *
jg sys32_sigprocmask # branch to system call
.globl sys32_create_module_wrapper
sys32_create_module_wrapper:
llgtr %r2,%r2 # const char *
llgfr %r3,%r3 # size_t
jg sys32_create_module # branch to system call
.globl sys32_init_module_wrapper
sys32_init_module_wrapper:
llgtr %r2,%r2 # const char *
......@@ -581,11 +575,6 @@ sys32_delete_module_wrapper:
llgtr %r2,%r2 # const char *
jg sys32_delete_module # branch to system call
.globl sys32_get_kernel_syms_wrapper
sys32_get_kernel_syms_wrapper:
llgtr %r2,%r2 # struct kernel_sym_emu31 *
jg sys32_get_kernel_syms # branch to system call
.globl sys32_quotactl_wrapper
sys32_quotactl_wrapper:
lgfr %r2,%r2 # int
......@@ -786,15 +775,6 @@ sys32_getresuid16_wrapper:
llgtr %r4,%r4 # __kernel_old_uid_emu31_t *
jg sys32_getresuid16 # branch to system call
.globl sys32_query_module_wrapper
sys32_query_module_wrapper:
llgtr %r2,%r2 # const char *
lgfr %r3,%r3 # int
llgtr %r4,%r4 # char *
llgfr %r5,%r5 # size_t
llgtr %r6,%r6 # size_t *
jg sys32_query_module # branch to system call
.globl sys32_poll_wrapper
sys32_poll_wrapper:
llgtr %r2,%r2 # struct pollfd *
......
/*
* arch/s390x/lib/uaccess.S
* fixup routines for copy_{from|to}_user functions.
* __copy_{from|to}_user functions.
*
* S390
* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* 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
......@@ -22,14 +22,23 @@ __copy_from_user_asm:
1: sacf 0
lgr %r2,%r5
br %r14
2: lghi %r3,-4096
ng %r3,__LC_TRANS_EXC_ADDR
sgr %r3,%r4
jm 1b
j 0b
2: lghi %r1,-4096
lgr %r3,%r4
slgr %r3,%r1 # %r3 = %r4 + 4096
ngr %r3,%r1 # %r3 = (%r4 + 4096) & -4096
slgr %r3,%r4 # %r3 = #bytes to next user page boundary
clgr %r5,%r3 # copy crosses next page boundary ?
jnh 1b # no, this page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
3: mvcle %r2,%r4,0
jo 3b
j 1b
.section __ex_table,"a"
.align 8
.quad 0b,2b
.quad 3b,1b
.previous
.align 4
......@@ -43,14 +52,23 @@ __copy_to_user_asm:
1: sacf 0
lgr %r2,%r3
br %r14
2: lghi %r5,-4096
ng %r5,__LC_TRANS_EXC_ADDR
sgr %r5,%r4
jm 1b
j 0b
2: lghi %r1,-4096
lgr %r5,%r4
slgr %r5,%r1 # %r5 = %r4 + 4096
ngr %r5,%r1 # %r5 = (%r4 + 4096) & -4096
slgr %r5,%r4 # %r5 = #bytes to next user page boundary
clgr %r3,%r5 # copy crosses next page boundary ?
jnh 1b # no, the current page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
3: mvcle %r4,%r2,0
jo 3b
j 1b
.section __ex_table,"a"
.align 8
.quad 0b,2b
.quad 3b,1b
.previous
.align 4
......@@ -65,14 +83,25 @@ __clear_user_asm:
0: mvcle %r4,%r2,0
jo 0b
1: sacf 0
lgr %r2,%r5
br %r14
2: lghi %r5,-4096
ng %r5,__LC_TRANS_EXC_ADDR
sgr %r5,%r4
jm 1b
j 0b
2: lgr %r2,%r5
lghi %r1,-4096
slgr %r5,%r1 # %r5 = %r4 + 4096
ngr %r5,%r1 # %r5 = (%r4 + 4096) & -4096
slgr %r5,%r4 # %r5 = #bytes to next user page boundary
clgr %r2,%r5 # copy crosses next page boundary ?
jnh 1b # no, the current page fauled
# The page after the current user page might have faulted.
# We cant't find out which page because the program check handler
# might have callled schedule, destroying all lowcore information.
# We retry with the shortened length.
slgr %r2,%r5
3: mvcle %r4,%r2,0
jo 3b
j 1b
4: algr %r2,%r5
j 1b
.section __ex_table,"a"
.align 8
.quad 0b,2b
.quad 3b,4b
.previous
......@@ -2,10 +2,8 @@
* arch/s390/mm/extable.c
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Hartmut Penner (hp@de.ibm.com)
*
* Derived from "arch/i386/mm/extable.c"
* identical to arch/i386/mm/extable.c
*/
#include <linux/config.h>
......@@ -42,6 +40,7 @@ extern spinlock_t modlist_lock;
unsigned long
search_exception_table(unsigned long addr)
{
struct list_head *i;
unsigned long ret = 0;
#ifndef CONFIG_MODULES
......@@ -50,15 +49,17 @@ search_exception_table(unsigned long addr)
return ret;
#else
unsigned long flags;
/* The kernel is the last "module" -- no need to treat it special. */
struct module *mp;
struct list_head *i;
/* The kernel is the last "module" -- no need to treat it special. */
spin_lock_irqsave(&modlist_lock, flags);
for (mp = module_list; mp != NULL; mp = mp->next) {
if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING)))
list_for_each(i, &extables) {
struct exception_table *ex
= list_entry(i, struct exception_table, list);
if (ex->num_entries == 0)
continue;
ret = search_one_table(mp->ex_table_start,
mp->ex_table_end - 1, addr);
ret = search_one_table(ex->entry,
ex->entry + ex->num_entries - 1, addr);
if (ret)
break;
}
......
/* ld script to make s390 Linux kernel
/*
* Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
......@@ -25,6 +25,7 @@ SECTIONS
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
. = ALIGN(64);
__start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;
......@@ -40,18 +41,31 @@ SECTIONS
CONSTRUCTORS
}
. = ALIGN(4096);
__nosave_begin = .;
.data_nosave : { *(.data.nosave) }
. = ALIGN(4096);
__nosave_end = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
_edata = .; /* End of data section */
. = ALIGN(16384); /* init_task */
. = ALIGN(8192); /* init_task */
.data.init_task : { *(.data.init_task) }
/* will be freed after init */
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
.init.text : { *(.init.text) }
.init.data : { *(.init.data) }
. = ALIGN(256);
__setup_start = .;
.setup.init : { *(.setup.init) }
.init.setup : { *(.init.setup) }
__setup_end = .;
__initcall_start = .;
.initcall.init : {
......@@ -65,24 +79,29 @@ SECTIONS
}
__initcall_end = .;
. = ALIGN(256);
__initramfs_start = .;
.init.ramfs : { *(.init.initramfs) }
__initramfs_end = .;
. = ALIGN(256);
__per_cpu_start = .;
.data.percpu : { *(.data.percpu) }
__per_cpu_end = .;
. = ALIGN(4096);
__init_end = .;
/* freed after init ends here */
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
__bss_start = .; /* BSS */
.bss : { *(.bss) }
__bss_stop = .;
_end = . ;
__bss_start = .; /* BSS */
.bss : {
*(.bss)
/* Sections to be discarded */
/DISCARD/ : {
*(.exit.text)
*(.exit.data)
*(.exitcall.exit)
}
_end = . ;
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
......
......@@ -146,10 +146,9 @@ extern long serial167_console_init(void);
extern void console_8xx_init(void);
extern int rs_8xx_init(void);
extern void mac_scc_console_init(void);
extern void hwc_console_init(void);
extern void hwc_tty_init(void);
extern void sclp_console_init(void);
extern void sclp_tty_init(void);
extern void con3215_init(void);
extern void tty3215_init(void);
extern void tub3270_con_init(void);
extern void tub3270_init(void);
extern void uart_console_init(void);
......@@ -2208,8 +2207,8 @@ void __init console_init(void)
#ifdef CONFIG_TN3215
con3215_init();
#endif
#ifdef CONFIG_HWC
hwc_console_init();
#ifdef CONFIG_SCLP_CONSOLE
sclp_console_init();
#endif
#ifdef CONFIG_STDIO_CONSOLE
stdio_console_init();
......@@ -2349,8 +2348,8 @@ void __init tty_init(void)
#ifdef CONFIG_TN3215
tty3215_init();
#endif
#ifdef CONFIG_HWC
hwc_tty_init();
#ifdef CONFIG_SCLP
sclp_tty_init();
#endif
#ifdef CONFIG_A2232
a2232board_init();
......
......@@ -257,24 +257,30 @@ config TN3215_CONSOLE
Include support for using an IBM 3215 line-mode terminal as a
Linux system console.
config HWC
bool "Support for HWC line mode terminal"
config SCLP
bool "Support for SCLP"
help
Include support for IBM HWC line-mode terminals.
Include support for the SCLP interface to the service element.
config HWC_CONSOLE
bool "console on HWC line mode terminal"
depends on HWC
config SCLP_TTY
bool "Support for SCLP line mode terminal"
depends on SCLP
help
Include support for IBM SCLP line-mode terminals.
config SCLP_CONSOLE
bool "Support for console on SCLP line mode terminal"
depends on SCLP_TTY
help
Include support for using an IBM HWC line-mode terminal as the Linux
system console.
config HWC_CPI
config SCLP_CPI
tristate "Control-Program Identification"
depends on HWC
depends on SCLP
help
This option enables the hardware console interface for system
identification This is commonly used for workload management and
identification. This is commonly used for workload management and
gives you a nice name for the system on the service element.
Please select this option as a module since built-in operation is
completely untested.
......
......@@ -11,9 +11,9 @@ dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
dasd_genhd.o dasd_erp.o
obj-$(CONFIG_DASD) += dasd_mod.o
obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
include $(TOPDIR)/Rules.make
......@@ -325,7 +325,6 @@ static int xpram_ioctl (struct inode *inode, struct file *filp,
{
struct hd_geometry *geo;
unsigned long size;
int idx = minor(inode->i_rdev);
if (cmd != HDIO_GETGEO)
return -EINVAL;
/*
......@@ -474,6 +473,7 @@ static int __init xpram_setup_blkdev(void)
out:
while (i--)
put_disk(xpram_disks[i]);
return rc;
}
/*
......
......@@ -2,28 +2,23 @@
# S/390 character devices
#
export-objs := hwc_rw.o tape.o tape34xx.o
export-objs := sclp_rw.o tape_core.o tape_devmap.o tape_std.o
tub3270-objs := tuball.o tubfs.o tubtty.o \
tubttyaid.o tubttybld.o tubttyscl.o \
tubttyrcl.o tubttysiz.o
tape390-$(CONFIG_S390_TAPE_CHAR) += tapechar.o
tape390-$(CONFIG_S390_TAPE_BLOCK) += tapeblock.o
tape390-objs := tape.o tape34xx.o $(sort $(tape390-y))
tape_3480_mod-objs := tape3480.o
tape_3490_mod-objs := tape3490.o
obj-y += ctrlchar.o
obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_HWC) += hwc_con.o hwc_rw.o hwc_tty.o
obj-$(CONFIG_HWC_CPI) += hwc_cpi.o
obj-$(CONFIG_SCLP) += sclp.o
obj-$(CONFIG_SCLP_TTY) += sclp_rw.o sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
obj-$(CONFIG_TN3270) += tub3270.o
obj-$(CONFIG_S390_TAPE) += tape390.o
obj-$(CONFIG_S390_TAPE_3480) += tape_3480_mod.o
obj-$(CONFIG_S390_TAPE_3490) += tape_3490_mod.o
tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
tape-$(CONFIG_PROC_FS) += tape_proc.o
tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
obj-$(CONFIG_S390_TAPE) += tape.o
obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
include $(TOPDIR)/Rules.make
/*
* drivers/s390/char/hwc.h
*
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
*
*
*/
#ifndef __HWC_H__
#define __HWC_H__
#define HWC_EXT_INT_PARAM_ADDR 0xFFFFFFF8
#define HWC_EXT_INT_PARAM_PEND 0x00000001
#define ET_OpCmd 0x01
#define ET_Msg 0x02
#define ET_StateChange 0x08
#define ET_PMsgCmd 0x09
#define ET_CntlProgOpCmd 0x20
#define ET_CntlProgIdent 0x0B
#define ET_SigQuiesce 0x1D
#define ET_OpCmd_Mask 0x80000000
#define ET_Msg_Mask 0x40000000
#define ET_StateChange_Mask 0x01000000
#define ET_PMsgCmd_Mask 0x00800000
#define ET_CtlProgOpCmd_Mask 0x00000001
#define ET_CtlProgIdent_Mask 0x00200000
#define ET_SigQuiesce_Mask 0x00000008
#define GMF_DOM 0x8000
#define GMF_SndAlrm 0x4000
#define GMF_HoldMsg 0x2000
#define LTF_CntlText 0x8000
#define LTF_LabelText 0x4000
#define LTF_DataText 0x2000
#define LTF_EndText 0x1000
#define LTF_PromptText 0x0800
#define HWC_COMMAND_INITIATED 0
#define HWC_BUSY 2
#define HWC_NOT_OPERATIONAL 3
#define hwc_cmdw_t u32;
#define HWC_CMDW_READDATA 0x00770005
#define HWC_CMDW_WRITEDATA 0x00760005
#define HWC_CMDW_WRITEMASK 0x00780005
#define GDS_ID_MDSMU 0x1310
#define GDS_ID_MDSRouteInfo 0x1311
#define GDS_ID_AgUnWrkCorr 0x1549
#define GDS_ID_SNACondReport 0x1532
#define GDS_ID_CPMSU 0x1212
#define GDS_ID_RoutTargInstr 0x154D
#define GDS_ID_OpReq 0x8070
#define GDS_ID_TextCmd 0x1320
#define GDS_KEY_SelfDefTextMsg 0x31
#define _HWCB_HEADER u16 length; \
u8 function_code; \
u8 control_mask[3]; \
u16 response_code;
#define _EBUF_HEADER u16 length; \
u8 type; \
u8 flags; \
u16 _reserved;
typedef struct {
_EBUF_HEADER
} __attribute__ ((packed))
evbuf_t;
#define _MDB_HEADER u16 length; \
u16 type; \
u32 tag; \
u32 revision_code;
#define _GO_HEADER u16 length; \
u16 type; \
u32 domid; \
u8 hhmmss_time[8]; \
u8 th_time[3]; \
u8 _reserved_0; \
u8 dddyyyy_date[7]; \
u8 _reserved_1; \
u16 general_msg_flags; \
u8 _reserved_2[10]; \
u8 originating_system_name[8]; \
u8 job_guest_name[8];
#define _MTO_HEADER u16 length; \
u16 type; \
u16 line_type_flags; \
u8 alarm_control; \
u8 _reserved[3];
typedef struct {
_GO_HEADER
} __attribute__ ((packed))
go_t;
typedef struct {
go_t go;
} __attribute__ ((packed))
mdb_body_t;
typedef struct {
_MDB_HEADER
mdb_body_t mdb_body;
} __attribute__ ((packed))
mdb_t;
typedef struct {
_EBUF_HEADER
mdb_t mdb;
} __attribute__ ((packed))
msgbuf_t;
typedef struct {
_HWCB_HEADER
msgbuf_t msgbuf;
} __attribute__ ((packed))
write_hwcb_t;
typedef struct {
_MTO_HEADER
} __attribute__ ((packed))
mto_t;
/* FIXME: don't define static variables in a header!!! */
#warning
static write_hwcb_t write_hwcb_template =
{
sizeof (write_hwcb_t),
0x00,
{
0x00,
0x00,
0x00
},
0x0000,
{
sizeof (msgbuf_t),
ET_Msg,
0x00,
0x0000,
{
sizeof (mdb_t),
0x0001,
0xD4C4C240,
0x00000001,
{
{
sizeof (go_t),
0x0001
}
}
}
}
};
#warning
static mto_t mto_template =
{
sizeof (mto_t),
0x0004,
LTF_EndText,
0x00
};
typedef u32 _hwcb_mask_t;
typedef struct {
_HWCB_HEADER
u16 _reserved;
u16 mask_length;
_hwcb_mask_t cp_receive_mask;
_hwcb_mask_t cp_send_mask;
_hwcb_mask_t hwc_receive_mask;
_hwcb_mask_t hwc_send_mask;
} __attribute__ ((packed))
init_hwcb_t;
#warning
static init_hwcb_t init_hwcb_template =
{
sizeof (init_hwcb_t),
0x00,
{
0x00,
0x00,
0x00
},
0x0000,
0x0000,
sizeof (_hwcb_mask_t),
ET_OpCmd_Mask | ET_PMsgCmd_Mask |
ET_StateChange_Mask | ET_SigQuiesce_Mask,
ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask
};
typedef struct {
_EBUF_HEADER
u8 validity_hwc_active_facility_mask:1;
u8 validity_hwc_receive_mask:1;
u8 validity_hwc_send_mask:1;
u8 validity_read_data_function_mask:1;
u16 _zeros:12;
u16 mask_length;
u64 hwc_active_facility_mask;
_hwcb_mask_t hwc_receive_mask;
_hwcb_mask_t hwc_send_mask;
u32 read_data_function_mask;
} __attribute__ ((packed))
statechangebuf_t;
#define _GDS_VECTOR_HEADER u16 length; \
u16 gds_id;
#define _GDS_SUBVECTOR_HEADER u8 length; \
u8 key;
typedef struct {
_GDS_VECTOR_HEADER
} __attribute__ ((packed))
gds_vector_t;
typedef struct {
_GDS_SUBVECTOR_HEADER
} __attribute__ ((packed))
gds_subvector_t;
typedef struct {
_HWCB_HEADER
} __attribute__ ((packed))
read_hwcb_t;
#warning
static read_hwcb_t read_hwcb_template =
{
PAGE_SIZE,
0x00,
{
0x00,
0x00,
0x80
}
};
#endif /* __HWC_H__ */
/*
* drivers/s390/char/hwc_con.c
* HWC line mode console driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/kdev_t.h>
#include <linux/string.h>
#include <linux/console.h>
#include <linux/fs.h>
#include <linux/init.h>
#include "hwc_rw.h"
#ifdef CONFIG_HWC_CONSOLE
#define hwc_console_major 4
#define hwc_console_minor 64
#define hwc_console_name "console"
void hwc_console_write (struct console *, const char *, unsigned int);
kdev_t hwc_console_device (struct console *);
void hwc_console_unblank (void);
#define HWC_CON_PRINT_HEADER "hwc console driver: "
struct console hwc_console =
{
.name = hwc_console_name,
.write = hwc_console_write,
.device = hwc_console_device,
.unblank = hwc_console_unblank,
.flags = CON_PRINTBUFFER,
};
void
hwc_console_write (
struct console *console,
const char *message,
unsigned int count)
{
if (kdev_val (console->device (console)) !=
kdev_val (hwc_console.device (&hwc_console))) {
hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER
"hwc_console_write() called with wrong "
"device number");
return;
}
hwc_write (0, message, count);
}
kdev_t
hwc_console_device (struct console * c)
{
return mk_kdev (hwc_console_major, hwc_console_minor);
}
void
hwc_console_unblank (void)
{
hwc_unblank ();
}
#endif
void __init
hwc_console_init (void)
{
if (!MACHINE_HAS_HWC)
return;
if (hwc_init () == 0) {
#ifdef CONFIG_HWC_CONSOLE
if (CONSOLE_IS_HWC)
register_console (&hwc_console);
#endif
} else
panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !");
return;
}
/*
* Author: Martin Peschke <mpeschke@de.ibm.com>
* Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
*/
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <asm/semaphore.h>
#include <asm/ebcdic.h>
#include "hwc_rw.h"
#include "hwc.h"
#define CPI_RETRIES 3
#define CPI_SLEEP_TICKS 50
#define CPI_LENGTH_SYSTEM_TYPE 8
#define CPI_LENGTH_SYSTEM_NAME 8
#define CPI_LENGTH_SYSPLEX_NAME 8
typedef struct {
_EBUF_HEADER
u8 id_format;
u8 reserved0;
u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
u64 reserved1;
u8 system_name[CPI_LENGTH_SYSTEM_NAME];
u64 reserved2;
u64 system_level;
u64 reserved3;
u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
u8 reserved4[16];
} __attribute__ ((packed))
cpi_evbuf_t;
typedef struct _cpi_hwcb_t {
_HWCB_HEADER
cpi_evbuf_t cpi_evbuf;
} __attribute__ ((packed))
cpi_hwcb_t;
cpi_hwcb_t *cpi_hwcb;
static int __init cpi_module_init (void);
static void __exit cpi_module_exit (void);
module_init (cpi_module_init);
module_exit (cpi_module_exit);
MODULE_AUTHOR (
"Martin Peschke, IBM Deutschland Entwicklung GmbH "
"<mpeschke@de.ibm.com>");
MODULE_DESCRIPTION (
"identify this operating system instance to the S/390 or zSeries hardware");
static char *system_name = NULL;
MODULE_PARM (system_name, "s");
MODULE_PARM_DESC (system_name, "e.g. hostname - max. 8 characters");
static char *sysplex_name = NULL;
#ifdef ALLOW_SYSPLEX_NAME
MODULE_PARM (sysplex_name, "s");
MODULE_PARM_DESC (sysplex_name, "if applicable - max. 8 characters");
#endif
static char *system_type = "LINUX";
hwc_request_t cpi_request =
{};
hwc_callback_t cpi_callback;
static DECLARE_MUTEX_LOCKED (sem);
static int __init
cpi_module_init (void)
{
int retval;
int system_type_length;
int system_name_length;
int sysplex_name_length = 0;
int retries;
if (!MACHINE_HAS_HWC) {
printk ("cpi: bug: hardware console not present\n");
retval = -EINVAL;
goto out;
}
if (!system_type) {
printk ("cpi: bug: no system type specified\n");
retval = -EINVAL;
goto out;
}
system_type_length = strlen (system_type);
if (system_type_length > CPI_LENGTH_SYSTEM_NAME) {
printk ("cpi: bug: system type has length of %i characters - "
"only %i characters supported\n",
system_type_length,
CPI_LENGTH_SYSTEM_TYPE);
retval = -EINVAL;
goto out;
}
if (!system_name) {
printk ("cpi: no system name specified\n");
retval = -EINVAL;
goto out;
}
system_name_length = strlen (system_name);
if (system_name_length > CPI_LENGTH_SYSTEM_NAME) {
printk ("cpi: system name has length of %i characters - "
"only %i characters supported\n",
system_name_length,
CPI_LENGTH_SYSTEM_NAME);
retval = -EINVAL;
goto out;
}
if (sysplex_name) {
sysplex_name_length = strlen (sysplex_name);
if (sysplex_name_length > CPI_LENGTH_SYSPLEX_NAME) {
printk ("cpi: sysplex name has length of %i characters - "
"only %i characters supported\n",
sysplex_name_length,
CPI_LENGTH_SYSPLEX_NAME);
retval = -EINVAL;
goto out;
}
}
cpi_hwcb = kmalloc (sizeof (cpi_hwcb_t), GFP_KERNEL);
if (!cpi_hwcb) {
printk ("cpi: no storage to fulfill request\n");
retval = -ENOMEM;
goto out;
}
memset (cpi_hwcb, 0, sizeof (cpi_hwcb_t));
cpi_hwcb->length = sizeof (cpi_hwcb_t);
cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t);
cpi_hwcb->cpi_evbuf.type = 0x0B;
memset (cpi_hwcb->cpi_evbuf.system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
memcpy (cpi_hwcb->cpi_evbuf.system_type, system_type, system_type_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
memset (cpi_hwcb->cpi_evbuf.system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
memcpy (cpi_hwcb->cpi_evbuf.system_name, system_name, system_name_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE;
if (sysplex_name) {
memset (cpi_hwcb->cpi_evbuf.sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
memcpy (cpi_hwcb->cpi_evbuf.sysplex_name, sysplex_name, sysplex_name_length);
HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
EBC_TOUPPER (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
}
cpi_request.block = cpi_hwcb;
cpi_request.word = HWC_CMDW_WRITEDATA;
cpi_request.callback = cpi_callback;
for (retries = CPI_RETRIES; retries; retries--) {
retval = hwc_send (&cpi_request);
if (retval) {
set_current_state (TASK_INTERRUPTIBLE);
schedule_timeout (CPI_SLEEP_TICKS);
} else {
down (&sem);
switch (cpi_hwcb->response_code) {
case 0x0020:
printk ("cpi: succeeded\n");
break;
default:
printk ("cpi: failed with response code 0x%x\n",
cpi_hwcb->response_code);
}
goto free;
}
}
printk ("cpi: failed (%i)\n", retval);
free:
kfree (cpi_hwcb);
out:
return retval;
}
static void __exit
cpi_module_exit (void)
{
printk ("cpi: exit\n");
}
void
cpi_callback (hwc_request_t * req)
{
up (&sem);
}
/*
* drivers/s390/char/hwc_rw.c
* driver: reading from and writing to system console on S/390 via HWC
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
*
*
*
*
*
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/bootmem.h>
#include <linux/module.h>
#include <asm/ebcdic.h>
#include <asm/uaccess.h>
#include <asm/types.h>
#include <asm/bitops.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/s390_ext.h>
#include <asm/irq.h>
#ifndef MIN
#define MIN(a,b) (((a<b) ? a : b))
#endif
extern void ctrl_alt_del (void);
#define HWC_RW_PRINT_HEADER "hwc low level driver: "
#define USE_VM_DETECTION
#define DEFAULT_CASE_DELIMITER '%'
#undef DUMP_HWC_INIT_ERROR
#undef DUMP_HWC_WRITE_ERROR
#undef DUMP_HWC_WRITE_LIST_ERROR
#undef DUMP_HWC_READ_ERROR
#undef DUMP_HWCB_INPUT
#undef BUFFER_STRESS_TEST
typedef struct {
unsigned char *next;
unsigned short int mto_char_sum;
unsigned char mto_number;
unsigned char times_lost;
unsigned short int mto_number_lost;
unsigned long int mto_char_sum_lost;
} __attribute__ ((packed))
hwcb_list_t;
#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))
#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))
#define BUF_HWCB hwc_data.hwcb_list_tail
#define OUT_HWCB hwc_data.hwcb_list_head
#define ALL_HWCB_MTO hwc_data.mto_number
#define ALL_HWCB_CHAR hwc_data.mto_char_sum
#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))
#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)
#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)
#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)
#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)
#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)
#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)
#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)
#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)
#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)
#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)
#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)
#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)
#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)
#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)
#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)
#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)
#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)
#include "hwc.h"
#define __HWC_RW_C__
#include "hwc_rw.h"
#undef __HWC_RW_C__
static unsigned char _obuf[MAX_HWCB_ROOM];
static unsigned char
_page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
typedef unsigned long kmem_pages_t;
#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
#define HWC_WTIMER_RUNS 1
#define HWC_FLUSH 2
#define HWC_INIT 4
#define HWC_BROKEN 8
#define HWC_INTERRUPT 16
#define HWC_PTIMER_RUNS 32
static struct {
hwc_ioctls_t ioctls;
hwc_ioctls_t init_ioctls;
unsigned char *hwcb_list_head;
unsigned char *hwcb_list_tail;
unsigned short int mto_number;
unsigned int mto_char_sum;
unsigned char hwcb_count;
unsigned long kmem_start;
unsigned long kmem_end;
kmem_pages_t kmem_pages;
unsigned char *obuf;
unsigned short int obuf_cursor;
unsigned short int obuf_count;
unsigned short int obuf_start;
unsigned char *page;
u32 current_servc;
unsigned char *current_hwcb;
unsigned char write_nonprio:1;
unsigned char write_prio:1;
unsigned char read_nonprio:1;
unsigned char read_prio:1;
unsigned char read_statechange:1;
unsigned char sig_quiesce:1;
unsigned char flags;
hwc_high_level_calls_t *calls;
hwc_request_t *request;
spinlock_t lock;
struct timer_list write_timer;
struct timer_list poll_timer;
} hwc_data =
{
{
},
{
8,
0,
80,
1,
MAX_KMEM_PAGES,
MAX_KMEM_PAGES,
0,
0x6c
},
NULL,
NULL,
0,
0,
0,
0,
0,
0,
_obuf,
0,
0,
0,
_page,
0,
NULL,
0,
0,
0,
0,
0,
0,
0,
NULL,
NULL
};
static unsigned long cr0 __attribute__ ((aligned (8)));
static unsigned long cr0_save __attribute__ ((aligned (8)));
static unsigned char psw_mask __attribute__ ((aligned (8)));
static ext_int_info_t ext_int_info_hwc;
#define DELAYED_WRITE 0
#define IMMEDIATE_WRITE 1
static signed int do_hwc_write (int from_user, unsigned char *,
unsigned int,
unsigned char);
unsigned char hwc_ip_buf[512];
static asmlinkage int
internal_print (char write_time, char *fmt,...)
{
va_list args;
int i;
va_start (args, fmt);
i = vsprintf (hwc_ip_buf, fmt, args);
va_end (args);
return do_hwc_write (0, hwc_ip_buf, i, write_time);
}
int
hwc_printk (const char *fmt,...)
{
va_list args;
int i;
unsigned long flags;
int retval;
spin_lock_irqsave (&hwc_data.lock, flags);
i = vsprintf (hwc_ip_buf, fmt, args);
va_end (args);
retval = do_hwc_write (0, hwc_ip_buf, i, IMMEDIATE_WRITE);
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
#ifdef DUMP_HWCB_INPUT
static void
dump_storage_area (unsigned char *area, unsigned short int count)
{
unsigned short int index;
ioctl_nl_t old_final_nl;
if (!area || !count)
return;
old_final_nl = hwc_data.ioctls.final_nl;
hwc_data.ioctls.final_nl = 1;
internal_print (DELAYED_WRITE, "\n%8x ", area);
for (index = 0; index < count; index++) {
if (area[index] <= 0xF)
internal_print (DELAYED_WRITE, "0%x", area[index]);
else
internal_print (DELAYED_WRITE, "%x", area[index]);
if ((index & 0xF) == 0xF)
internal_print (DELAYED_WRITE, "\n%8x ",
&area[index + 1]);
else if ((index & 3) == 3)
internal_print (DELAYED_WRITE, " ");
}
internal_print (IMMEDIATE_WRITE, "\n");
hwc_data.ioctls.final_nl = old_final_nl;
}
#endif
static inline u32
service_call (
u32 hwc_command_word,
unsigned char hwcb[])
{
unsigned int condition_code = 1;
__asm__ __volatile__ ("L 1, 0(%0) \n\t"
"LRA 2, 0(%1) \n\t"
".long 0xB2200012 \n\t"
:
:"a" (&hwc_command_word), "a" (hwcb)
:"1", "2", "memory");
__asm__ __volatile__ ("IPM %0 \n\t"
"SRL %0, 28 \n\t"
:"=r" (condition_code));
return condition_code;
}
static inline unsigned long
hwc_ext_int_param (void)
{
u32 param;
__asm__ __volatile__ ("L %0,128\n\t"
:"=r" (param));
return (unsigned long) param;
}
static int
prepare_write_hwcb (void)
{
write_hwcb_t *hwcb;
if (!BUF_HWCB)
return -ENOMEM;
BUF_HWCB_MTO = 0;
BUF_HWCB_CHAR = 0;
hwcb = (write_hwcb_t *) BUF_HWCB;
memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t));
return 0;
}
static int
sane_write_hwcb (void)
{
unsigned short int lost_msg;
unsigned int lost_char;
unsigned char lost_hwcb;
unsigned char *bad_addr;
unsigned long page;
int page_nr;
if (!OUT_HWCB)
return -ENOMEM;
if ((unsigned long) OUT_HWCB & 0xFFF) {
bad_addr = OUT_HWCB;
#ifdef DUMP_HWC_WRITE_LIST_ERROR
__asm__ ("LHI 1,0xe30\n\t"
"LRA 2,0(%0) \n\t"
"J .+0 \n\t"
:
: "a" (bad_addr)
: "1", "2");
#endif
hwc_data.kmem_pages = 0;
if ((unsigned long) BUF_HWCB & 0xFFF) {
lost_hwcb = hwc_data.hwcb_count;
lost_msg = ALL_HWCB_MTO;
lost_char = ALL_HWCB_CHAR;
OUT_HWCB = NULL;
BUF_HWCB = NULL;
ALL_HWCB_MTO = 0;
ALL_HWCB_CHAR = 0;
hwc_data.hwcb_count = 0;
} else {
lost_hwcb = hwc_data.hwcb_count - 1;
lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO;
lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR;
OUT_HWCB = BUF_HWCB;
ALL_HWCB_MTO = BUF_HWCB_MTO;
ALL_HWCB_CHAR = BUF_HWCB_CHAR;
hwc_data.hwcb_count = 1;
page = (unsigned long) BUF_HWCB;
if (page >= hwc_data.kmem_start &&
page <= hwc_data.kmem_end) {
page_nr = (int)
((page - hwc_data.kmem_start) >> 12);
set_bit (page_nr, &hwc_data.kmem_pages);
}
}
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"found invalid HWCB at address 0x%lx. List corrupted. "
"Lost %i HWCBs with %i characters within up to %i "
"messages. Saved %i HWCB with last %i characters i"
"within up to %i messages.\n",
(unsigned long) bad_addr,
lost_hwcb, lost_char, lost_msg,
hwc_data.hwcb_count,
ALL_HWCB_CHAR, ALL_HWCB_MTO);
}
return 0;
}
static int
reuse_write_hwcb (void)
{
int retval;
if (hwc_data.hwcb_count < 2)
#ifdef DUMP_HWC_WRITE_LIST_ERROR
__asm__ ("LHI 1,0xe31\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (BUF_HWCB), "a" (OUT_HWCB)
: "1", "2", "3");
#else
return -EPERM;
#endif
if (hwc_data.current_hwcb == OUT_HWCB) {
if (hwc_data.hwcb_count > 2) {
BUF_HWCB_NEXT = OUT_HWCB_NEXT;
BUF_HWCB = OUT_HWCB_NEXT;
OUT_HWCB_NEXT = BUF_HWCB_NEXT;
BUF_HWCB_NEXT = NULL;
}
} else {
BUF_HWCB_NEXT = OUT_HWCB;
BUF_HWCB = OUT_HWCB;
OUT_HWCB = OUT_HWCB_NEXT;
BUF_HWCB_NEXT = NULL;
}
BUF_HWCB_TIMES_LOST += 1;
BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR;
BUF_HWCB_MTO_LOST += BUF_HWCB_MTO;
ALL_HWCB_MTO -= BUF_HWCB_MTO;
ALL_HWCB_CHAR -= BUF_HWCB_CHAR;
retval = prepare_write_hwcb ();
if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"reached my own limit of "
"allowed buffer space for output (%i HWCBs = %li "
"bytes), skipped content of oldest HWCB %i time(s) "
"(%i lines = %i characters)\n",
hwc_data.ioctls.max_hwcb,
hwc_data.ioctls.max_hwcb * PAGE_SIZE,
BUF_HWCB_TIMES_LOST,
BUF_HWCB_MTO_LOST,
BUF_HWCB_CHAR_LOST);
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"page allocation failed, "
"could not expand buffer for output (currently in "
"use: %i HWCBs = %li bytes), skipped content of "
"oldest HWCB %i time(s) (%i lines = %i characters)\n",
hwc_data.hwcb_count,
hwc_data.hwcb_count * PAGE_SIZE,
BUF_HWCB_TIMES_LOST,
BUF_HWCB_MTO_LOST,
BUF_HWCB_CHAR_LOST);
return retval;
}
static int
allocate_write_hwcb (void)
{
unsigned char *page;
int page_nr;
if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
return -ENOMEM;
page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
if (page_nr < hwc_data.ioctls.kmem_hwcb) {
page = (unsigned char *)
(hwc_data.kmem_start + (page_nr << 12));
set_bit (page_nr, &hwc_data.kmem_pages);
} else
page = (unsigned char *) __get_free_page (GFP_ATOMIC | GFP_DMA);
if (!page)
return -ENOMEM;
if (!OUT_HWCB)
OUT_HWCB = page;
else
BUF_HWCB_NEXT = page;
BUF_HWCB = page;
BUF_HWCB_NEXT = NULL;
hwc_data.hwcb_count++;
prepare_write_hwcb ();
BUF_HWCB_TIMES_LOST = 0;
BUF_HWCB_MTO_LOST = 0;
BUF_HWCB_CHAR_LOST = 0;
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
"*** " HWC_RW_PRINT_HEADER
"page #%i at 0x%x for buffering allocated. ***\n",
hwc_data.hwcb_count, page);
#endif
return 0;
}
static int
release_write_hwcb (void)
{
unsigned long page;
int page_nr;
if (!hwc_data.hwcb_count)
return -ENODATA;
if (hwc_data.hwcb_count == 1) {
prepare_write_hwcb ();
ALL_HWCB_CHAR = 0;
ALL_HWCB_MTO = 0;
BUF_HWCB_TIMES_LOST = 0;
BUF_HWCB_MTO_LOST = 0;
BUF_HWCB_CHAR_LOST = 0;
} else {
page = (unsigned long) OUT_HWCB;
ALL_HWCB_MTO -= OUT_HWCB_MTO;
ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
hwc_data.hwcb_count--;
OUT_HWCB = OUT_HWCB_NEXT;
if (page >= hwc_data.kmem_start &&
page <= hwc_data.kmem_end) {
/*memset((void *) page, 0, PAGE_SIZE); */
page_nr = (int) ((page - hwc_data.kmem_start) >> 12);
clear_bit (page_nr, &hwc_data.kmem_pages);
} else
free_page (page);
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
"*** " HWC_RW_PRINT_HEADER
"page at 0x%x released, %i pages still in use ***\n",
page, hwc_data.hwcb_count);
#endif
}
return 0;
}
static int
add_mto (
unsigned char *message,
unsigned short int count)
{
unsigned short int mto_size;
write_hwcb_t *hwcb;
mto_t *mto;
void *dest;
if (!BUF_HWCB)
return -ENOMEM;
if (BUF_HWCB == hwc_data.current_hwcb)
return -ENOMEM;
mto_size = sizeof (mto_t) + count;
hwcb = (write_hwcb_t *) BUF_HWCB;
if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
return -ENOMEM;
mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
memcpy (mto, &mto_template, sizeof (mto_t));
dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
memcpy (dest, message, count);
mto->length += count;
hwcb->length += mto_size;
hwcb->msgbuf.length += mto_size;
hwcb->msgbuf.mdb.length += mto_size;
BUF_HWCB_MTO++;
ALL_HWCB_MTO++;
BUF_HWCB_CHAR += count;
ALL_HWCB_CHAR += count;
return count;
}
static int write_event_data_1 (void);
static void
do_poll_hwc (unsigned long data)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
write_event_data_1 ();
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
void
start_poll_hwc (void)
{
init_timer (&hwc_data.poll_timer);
hwc_data.poll_timer.function = do_poll_hwc;
hwc_data.poll_timer.data = (unsigned long) NULL;
hwc_data.poll_timer.expires = jiffies + 2 * HZ;
add_timer (&hwc_data.poll_timer);
hwc_data.flags |= HWC_PTIMER_RUNS;
}
static int
write_event_data_1 (void)
{
unsigned short int condition_code;
int retval;
write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB;
if ((!hwc_data.write_prio) &&
(!hwc_data.write_nonprio) &&
hwc_data.read_statechange)
return -EOPNOTSUPP;
if (hwc_data.current_servc)
return -EBUSY;
retval = sane_write_hwcb ();
if (retval < 0)
return -EIO;
if (!OUT_HWCB_MTO)
return -ENODATA;
if (!hwc_data.write_nonprio && hwc_data.write_prio)
hwcb->msgbuf.type = ET_PMsgCmd;
else
hwcb->msgbuf.type = ET_Msg;
condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);
#ifdef DUMP_HWC_WRITE_ERROR
if (condition_code != HWC_COMMAND_INITIATED)
__asm__ ("LHI 1,0xe20\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (&condition_code), "a" (OUT_HWCB)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_WRITEDATA;
hwc_data.current_hwcb = OUT_HWCB;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
case HWC_NOT_OPERATIONAL:
start_poll_hwc ();
default:
retval = -EIO;
}
return retval;
}
static void
flush_hwcbs (void)
{
while (hwc_data.hwcb_count > 1)
release_write_hwcb ();
release_write_hwcb ();
hwc_data.flags &= ~HWC_FLUSH;
}
static int
write_event_data_2 (u32 ext_int_param)
{
write_hwcb_t *hwcb;
int retval = 0;
#ifdef DUMP_HWC_WRITE_ERROR
if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
!= (unsigned long) hwc_data.current_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"write_event_data_2 : "
"HWCB address does not fit "
"(expected: 0x%lx, got: 0x%lx).\n",
(unsigned long) hwc_data.current_hwcb,
ext_int_param);
return -EINVAL;
}
#endif
hwcb = (write_hwcb_t *) OUT_HWCB;
#ifdef DUMP_HWC_WRITE_LIST_ERROR
if (((unsigned char *) hwcb) != hwc_data.current_hwcb) {
__asm__ ("LHI 1,0xe22\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LRA 5,0(%3)\n\t"
"J .+0 \n\t"
:
: "a" (OUT_HWCB),
"a" (hwc_data.current_hwcb),
"a" (BUF_HWCB),
"a" (hwcb)
: "1", "2", "3", "4", "5");
}
#endif
#ifdef DUMP_HWC_WRITE_ERROR
if (hwcb->response_code != 0x0020) {
__asm__ ("LHI 1,0xe21\n\t"
"LRA 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LH 5,0(%3)\n\t"
"SRL 5,8\n\t"
"J .+0 \n\t"
:
: "a" (OUT_HWCB), "a" (hwc_data.current_hwcb),
"a" (BUF_HWCB),
"a" (&(hwc_data.hwcb_count))
: "1", "2", "3", "4", "5");
}
#endif
switch (hwcb->response_code) {
case 0x0020:
retval = OUT_HWCB_CHAR;
release_write_hwcb ();
break;
case 0x0040:
case 0x0340:
case 0x40F0:
if (!hwc_data.read_statechange) {
hwcb->response_code = 0;
start_poll_hwc ();
}
retval = -EIO;
break;
default:
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"write_event_data_2 : "
"failed operation "
"(response code: 0x%x "
"HWCB address: 0x%x).\n",
hwcb->response_code,
hwcb);
retval = -EIO;
}
if (retval == -EIO) {
hwcb->control_mask[0] = 0;
hwcb->control_mask[1] = 0;
hwcb->control_mask[2] = 0;
hwcb->response_code = 0;
}
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
if (hwc_data.flags & HWC_FLUSH)
flush_hwcbs ();
return retval;
}
static void
do_put_line (
unsigned char *message,
unsigned short count)
{
if (add_mto (message, count) != count) {
if (allocate_write_hwcb () < 0)
reuse_write_hwcb ();
#ifdef DUMP_HWC_WRITE_LIST_ERROR
if (add_mto (message, count) != count)
__asm__ ("LHI 1,0xe32\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"LRA 4,0(%2)\n\t"
"LRA 5,0(%3)\n\t"
"J .+0 \n\t"
:
: "a" (message), "a" (&hwc_data.kmem_pages),
"a" (BUF_HWCB), "a" (OUT_HWCB)
: "1", "2", "3", "4", "5");
#else
add_mto (message, count);
#endif
}
}
static void
put_line (
unsigned char *message,
unsigned short count)
{
if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_WTIMER_RUNS)) {
del_timer (&hwc_data.write_timer);
hwc_data.flags &= ~HWC_WTIMER_RUNS;
}
hwc_data.obuf_start += count;
do_put_line (message, count);
hwc_data.obuf_start -= count;
}
static void
set_alarm (void)
{
write_hwcb_t *hwcb;
if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb))
allocate_write_hwcb ();
hwcb = (write_hwcb_t *) BUF_HWCB;
hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm;
}
static void
hwc_write_timeout (unsigned long data)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
hwc_data.obuf_start = hwc_data.obuf_count;
if (hwc_data.obuf_count)
put_line (hwc_data.obuf, hwc_data.obuf_count);
hwc_data.obuf_start = 0;
hwc_data.obuf_cursor = 0;
hwc_data.obuf_count = 0;
write_event_data_1 ();
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
static int
do_hwc_write (
int from_user,
unsigned char *msg,
unsigned int count,
unsigned char write_time)
{
unsigned int i_msg = 0;
unsigned short int spaces = 0;
unsigned int processed_characters = 0;
unsigned char ch;
unsigned short int obuf_count;
unsigned short int obuf_cursor;
unsigned short int obuf_columns;
if (hwc_data.obuf_start) {
obuf_cursor = 0;
obuf_count = 0;
obuf_columns = MIN (hwc_data.ioctls.columns,
MAX_MESSAGE_SIZE - hwc_data.obuf_start);
} else {
obuf_cursor = hwc_data.obuf_cursor;
obuf_count = hwc_data.obuf_count;
obuf_columns = hwc_data.ioctls.columns;
}
for (i_msg = 0; i_msg < count; i_msg++) {
if (from_user)
get_user (ch, msg + i_msg);
else
ch = msg[i_msg];
processed_characters++;
if ((obuf_cursor == obuf_columns) &&
(ch != '\n') &&
(ch != '\t')) {
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_columns);
obuf_cursor = 0;
obuf_count = 0;
}
switch (ch) {
case '\n':
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
break;
case '\a':
hwc_data.obuf_start += obuf_count;
set_alarm ();
hwc_data.obuf_start -= obuf_count;
break;
case '\t':
do {
if (obuf_cursor < obuf_columns) {
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor]
= HWC_ASCEBC (' ');
obuf_cursor++;
} else
break;
} while (obuf_cursor % hwc_data.ioctls.width_htab);
break;
case '\f':
case '\v':
spaces = obuf_cursor;
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_count = obuf_cursor;
while (spaces) {
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor - spaces]
= HWC_ASCEBC (' ');
spaces--;
}
break;
case '\b':
if (obuf_cursor)
obuf_cursor--;
break;
case '\r':
obuf_cursor = 0;
break;
case 0x00:
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
goto out;
default:
if (isprint (ch))
hwc_data.obuf[hwc_data.obuf_start +
obuf_cursor++]
= HWC_ASCEBC (ch);
}
if (obuf_cursor > obuf_count)
obuf_count = obuf_cursor;
}
if (obuf_cursor) {
if (hwc_data.obuf_start ||
(hwc_data.ioctls.final_nl == 0)) {
put_line (&hwc_data.obuf[hwc_data.obuf_start],
obuf_count);
obuf_cursor = 0;
obuf_count = 0;
} else {
if (hwc_data.ioctls.final_nl > 0) {
if (hwc_data.flags & HWC_WTIMER_RUNS) {
mod_timer (&hwc_data.write_timer,
jiffies + hwc_data.ioctls.final_nl * HZ / 10);
} else {
init_timer (&hwc_data.write_timer);
hwc_data.write_timer.function =
hwc_write_timeout;
hwc_data.write_timer.data =
(unsigned long) NULL;
hwc_data.write_timer.expires =
jiffies +
hwc_data.ioctls.final_nl * HZ / 10;
add_timer (&hwc_data.write_timer);
hwc_data.flags |= HWC_WTIMER_RUNS;
}
} else;
}
} else;
out:
if (!hwc_data.obuf_start) {
hwc_data.obuf_cursor = obuf_cursor;
hwc_data.obuf_count = obuf_count;
}
if (write_time == IMMEDIATE_WRITE)
write_event_data_1 ();
return processed_characters;
}
signed int
hwc_write (int from_user, const unsigned char *msg, unsigned int count)
{
unsigned long flags;
int retval;
spin_lock_irqsave (&hwc_data.lock, flags);
retval = do_hwc_write (from_user, (unsigned char *) msg,
count, IMMEDIATE_WRITE);
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
unsigned int
hwc_chars_in_buffer (unsigned char flag)
{
unsigned short int number = 0;
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB)
number += ALL_HWCB_CHAR;
if (flag & IN_WRITE_BUF)
number += hwc_data.obuf_cursor;
spin_unlock_irqrestore (&hwc_data.lock, flags);
return number;
}
static inline int
nr_setbits (kmem_pages_t arg)
{
int i;
int nr = 0;
for (i = 0; i < (sizeof (arg) << 3); i++) {
if (arg & 1)
nr++;
arg >>= 1;
}
return nr;
}
unsigned int
hwc_write_room (unsigned char flag)
{
unsigned int number = 0;
unsigned long flags;
write_hwcb_t *hwcb;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB) {
if (BUF_HWCB) {
hwcb = (write_hwcb_t *) BUF_HWCB;
number += MAX_HWCB_ROOM - hwcb->length;
}
number += (hwc_data.ioctls.kmem_hwcb -
nr_setbits (hwc_data.kmem_pages)) *
(MAX_HWCB_ROOM -
(sizeof (write_hwcb_t) + sizeof (mto_t)));
}
if (flag & IN_WRITE_BUF)
number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
spin_unlock_irqrestore (&hwc_data.lock, flags);
return number;
}
void
hwc_flush_buffer (unsigned char flag)
{
unsigned long flags;
spin_lock_irqsave (&hwc_data.lock, flags);
if (flag & IN_HWCB) {
if (hwc_data.current_servc != HWC_CMDW_WRITEDATA)
flush_hwcbs ();
else
hwc_data.flags |= HWC_FLUSH;
}
if (flag & IN_WRITE_BUF) {
hwc_data.obuf_cursor = 0;
hwc_data.obuf_count = 0;
}
spin_unlock_irqrestore (&hwc_data.lock, flags);
}
unsigned short int
seperate_cases (unsigned char *buf, unsigned short int count)
{
unsigned short int i_in;
unsigned short int i_out = 0;
unsigned char _case = 0;
for (i_in = 0; i_in < count; i_in++) {
if (buf[i_in] == hwc_data.ioctls.delim) {
if ((i_in + 1 < count) &&
(buf[i_in + 1] == hwc_data.ioctls.delim)) {
buf[i_out] = hwc_data.ioctls.delim;
i_out++;
i_in++;
} else
_case = ~_case;
} else {
if (_case) {
if (hwc_data.ioctls.tolower)
buf[i_out] = _ebc_toupper[buf[i_in]];
else
buf[i_out] = _ebc_tolower[buf[i_in]];
} else
buf[i_out] = buf[i_in];
i_out++;
}
}
return i_out;
}
#ifdef DUMP_HWCB_INPUT
static int
gds_vector_name (u16 id, unsigned char name[])
{
int retval = 0;
switch (id) {
case GDS_ID_MDSMU:
name = "Multiple Domain Support Message Unit";
break;
case GDS_ID_MDSRouteInfo:
name = "MDS Routing Information";
break;
case GDS_ID_AgUnWrkCorr:
name = "Agent Unit of Work Correlator";
break;
case GDS_ID_SNACondReport:
name = "SNA Condition Report";
break;
case GDS_ID_CPMSU:
name = "CP Management Services Unit";
break;
case GDS_ID_RoutTargInstr:
name = "Routing and Targeting Instructions";
break;
case GDS_ID_OpReq:
name = "Operate Request";
break;
case GDS_ID_TextCmd:
name = "Text Command";
break;
default:
name = "unknown GDS variable";
retval = -EINVAL;
}
return retval;
}
#endif
inline static gds_vector_t *
find_gds_vector (
gds_vector_t * start, void *end, u16 id)
{
gds_vector_t *vec;
gds_vector_t *retval = NULL;
vec = start;
while (((void *) vec) < end) {
if (vec->gds_id == id) {
#ifdef DUMP_HWCB_INPUT
int retval_name;
unsigned char name[64];
retval_name = gds_vector_name (id, name);
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"%s at 0x%x up to 0x%x, length: %d",
name,
(unsigned long) vec,
((unsigned long) vec) + vec->length - 1,
vec->length);
if (retval_name < 0)
internal_print (
IMMEDIATE_WRITE,
", id: 0x%x\n",
vec->gds_id);
else
internal_print (
IMMEDIATE_WRITE,
"\n");
#endif
retval = vec;
break;
}
vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
}
return retval;
}
inline static gds_subvector_t *
find_gds_subvector (
gds_subvector_t * start, void *end, u8 key)
{
gds_subvector_t *subvec;
gds_subvector_t *retval = NULL;
subvec = start;
while (((void *) subvec) < end) {
if (subvec->key == key) {
retval = subvec;
break;
}
subvec = (gds_subvector_t *)
(((unsigned long) subvec) + subvec->length);
}
return retval;
}
inline static int
get_input (void *start, void *end)
{
int count;
count = ((unsigned long) end) - ((unsigned long) start);
if (hwc_data.ioctls.tolower)
EBC_TOLOWER (start, count);
if (hwc_data.ioctls.delim)
count = seperate_cases (start, count);
HWC_EBCASC_STR (start, count);
if (hwc_data.ioctls.echo)
do_hwc_write (0, start, count, IMMEDIATE_WRITE);
if (hwc_data.calls != NULL)
if (hwc_data.calls->move_input != NULL)
(hwc_data.calls->move_input) (start, count);
return count;
}
inline static int
eval_selfdeftextmsg (gds_subvector_t * start, void *end)
{
gds_subvector_t *subvec;
void *subvec_data;
void *subvec_end;
int retval = 0;
subvec = start;
while (((void *) subvec) < end) {
subvec = find_gds_subvector (subvec, end, 0x30);
if (!subvec)
break;
subvec_data = (void *)
(((unsigned long) subvec) +
sizeof (gds_subvector_t));
subvec_end = (void *)
(((unsigned long) subvec) + subvec->length);
retval += get_input (subvec_data, subvec_end);
subvec = (gds_subvector_t *) subvec_end;
}
return retval;
}
inline static int
eval_textcmd (gds_subvector_t * start, void *end)
{
gds_subvector_t *subvec;
gds_subvector_t *subvec_data;
void *subvec_end;
int retval = 0;
subvec = start;
while (((void *) subvec) < end) {
subvec = find_gds_subvector (
subvec, end, GDS_KEY_SelfDefTextMsg);
if (!subvec)
break;
subvec_data = (gds_subvector_t *)
(((unsigned long) subvec) +
sizeof (gds_subvector_t));
subvec_end = (void *)
(((unsigned long) subvec) + subvec->length);
retval += eval_selfdeftextmsg (subvec_data, subvec_end);
subvec = (gds_subvector_t *) subvec_end;
}
return retval;
}
inline static int
eval_cpmsu (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_subvector_t *vec_data;
void *vec_end;
int retval = 0;
vec = start;
while (((void *) vec) < end) {
vec = find_gds_vector (vec, end, GDS_ID_TextCmd);
if (!vec)
break;
vec_data = (gds_subvector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval += eval_textcmd (vec_data, vec_end);
vec = (gds_vector_t *) vec_end;
}
return retval;
}
inline static int
eval_mdsmu (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_vector_t *vec_data;
void *vec_end;
int retval = 0;
vec = find_gds_vector (start, end, GDS_ID_CPMSU);
if (vec) {
vec_data = (gds_vector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval = eval_cpmsu (vec_data, vec_end);
}
return retval;
}
static int
eval_evbuf (gds_vector_t * start, void *end)
{
gds_vector_t *vec;
gds_vector_t *vec_data;
void *vec_end;
int retval = 0;
vec = find_gds_vector (start, end, GDS_ID_MDSMU);
if (vec) {
vec_data = (gds_vector_t *)
(((unsigned long) vec) + sizeof (gds_vector_t));
vec_end = (void *) (((unsigned long) vec) + vec->length);
retval = eval_mdsmu (vec_data, vec_end);
}
return retval;
}
static inline int
eval_hwc_receive_mask (_hwcb_mask_t mask)
{
hwc_data.write_nonprio
= ((mask & ET_Msg_Mask) == ET_Msg_Mask);
hwc_data.write_prio
= ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
if (hwc_data.write_prio || hwc_data.write_nonprio) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can write messages\n");
return 0;
} else {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not write messages\n");
return -1;
}
}
static inline int
eval_hwc_send_mask (_hwcb_mask_t mask)
{
hwc_data.read_statechange
= ((mask & ET_StateChange_Mask) == ET_StateChange_Mask);
if (hwc_data.read_statechange)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read state change notifications\n");
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not read state change notifications\n");
hwc_data.sig_quiesce
= ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask);
if (hwc_data.sig_quiesce)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can receive signal quiesce\n");
else
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not receive signal quiesce\n");
hwc_data.read_nonprio
= ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask);
if (hwc_data.read_nonprio)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read commands\n");
hwc_data.read_prio
= ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
if (hwc_data.read_prio)
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can read priority commands\n");
if (hwc_data.read_prio || hwc_data.read_nonprio) {
return 0;
} else {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"can not read commands from operator\n");
return -1;
}
}
static int
eval_statechangebuf (statechangebuf_t * scbuf)
{
int retval = 0;
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"HWC state change detected\n");
if (scbuf->validity_hwc_active_facility_mask) {
}
if (scbuf->validity_hwc_receive_mask) {
if (scbuf->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe50\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (scbuf)
: "1", "2");
#endif
} else {
retval += eval_hwc_receive_mask
(scbuf->hwc_receive_mask);
}
}
if (scbuf->validity_hwc_send_mask) {
if (scbuf->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe51\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (scbuf)
: "1", "2");
#endif
} else {
retval += eval_hwc_send_mask
(scbuf->hwc_send_mask);
}
}
if (scbuf->validity_read_data_function_mask) {
}
return retval;
}
#ifdef CONFIG_SMP
static volatile unsigned long cpu_quiesce_map;
static void
do_load_quiesce_psw (void)
{
psw_t quiesce_psw;
clear_bit (smp_processor_id (), &cpu_quiesce_map);
if (smp_processor_id () == 0) {
while (cpu_quiesce_map != 0) ;
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw (quiesce_psw);
}
signal_processor (smp_processor_id (), sigp_stop);
}
static void
do_machine_quiesce (void)
{
cpu_quiesce_map = cpu_online_map;
smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
do_load_quiesce_psw ();
}
#else
static void
do_machine_quiesce (void)
{
psw_t quiesce_psw;
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
queisce_psw.addr = 0xfff;
__load_psw (quiesce_psw);
}
#endif
static int
process_evbufs (void *start, void *end)
{
int retval = 0;
evbuf_t *evbuf;
void *evbuf_end;
gds_vector_t *evbuf_data;
evbuf = (evbuf_t *) start;
while (((void *) evbuf) < end) {
evbuf_data = (gds_vector_t *)
(((unsigned long) evbuf) + sizeof (evbuf_t));
evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length);
switch (evbuf->type) {
case ET_OpCmd:
case ET_CntlProgOpCmd:
case ET_PMsgCmd:
#ifdef DUMP_HWCB_INPUT
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"event buffer "
"at 0x%x up to 0x%x, length: %d\n",
(unsigned long) evbuf,
(unsigned long) (evbuf_end - 1),
evbuf->length);
dump_storage_area ((void *) evbuf, evbuf->length);
#endif
retval += eval_evbuf (evbuf_data, evbuf_end);
break;
case ET_StateChange:
retval += eval_statechangebuf
((statechangebuf_t *) evbuf);
break;
case ET_SigQuiesce:
_machine_restart = do_machine_quiesce;
_machine_halt = do_machine_quiesce;
_machine_power_off = do_machine_quiesce;
ctrl_alt_del ();
break;
default:
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"unknown event buffer found, "
"type 0x%x",
evbuf->type);
retval = -ENOSYS;
}
evbuf = (evbuf_t *) evbuf_end;
}
return retval;
}
static int
unconditional_read_1 (void)
{
unsigned short int condition_code;
read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
int retval;
#if 0
if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio))
return -EOPNOTSUPP;
if (hwc_data.current_servc)
return -EBUSY;
#endif
memset (hwcb, 0x00, PAGE_SIZE);
memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
#ifdef DUMP_HWC_READ_ERROR
if (condition_code == HWC_NOT_OPERATIONAL)
__asm__ ("LHI 1,0xe40\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0 \n\t"
:
: "a" (&condition_code), "a" (hwc_data.page)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_READDATA;
hwc_data.current_hwcb = hwc_data.page;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
default:
retval = -EIO;
}
return retval;
}
static int
unconditional_read_2 (u32 ext_int_param)
{
read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
#ifdef DUMP_HWC_READ_ERROR
if ((hwcb->response_code != 0x0020) &&
(hwcb->response_code != 0x0220) &&
(hwcb->response_code != 0x60F0) &&
(hwcb->response_code != 0x62F0))
__asm__ ("LHI 1,0xe41\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (hwc_data.page), "a" (&(hwcb->response_code))
: "1", "2", "3");
#endif
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
switch (hwcb->response_code) {
case 0x0020:
case 0x0220:
return process_evbufs (
(void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
(void *) (((unsigned long) hwcb) + hwcb->length));
case 0x60F0:
case 0x62F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"got interrupt and tried to read input, "
"but nothing found (response code=0x%x).\n",
hwcb->response_code);
return 0;
case 0x0100:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: HWCB boundary violation - this "
"must not occur in a correct driver, please contact "
"author\n");
return -EIO;
case 0x0300:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"insufficient HWCB length - this must not occur in a "
"correct driver, please contact author\n");
return -EIO;
case 0x01F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: "
"invalid command - this must not occur in a correct "
"driver, please contact author\n");
return -EIO;
case 0x40F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid function code\n");
return -EIO;
case 0x70F0:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid selection mask\n");
return -EIO;
case 0x0040:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: HWC equipment check\n");
return -EIO;
default:
internal_print (
IMMEDIATE_WRITE,
HWC_RW_PRINT_HEADER
"unconditional read: invalid response code %x - this "
"must not occur in a correct driver, please contact "
"author\n",
hwcb->response_code);
return -EIO;
}
}
static int
write_event_mask_1 (void)
{
unsigned int condition_code;
int retval;
condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
#ifdef DUMP_HWC_INIT_ERROR
if (condition_code == HWC_NOT_OPERATIONAL)
__asm__ ("LHI 1,0xe10\n\t"
"L 2,0(%0)\n\t"
"LRA 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (&condition_code), "a" (hwc_data.page)
: "1", "2", "3");
#endif
switch (condition_code) {
case HWC_COMMAND_INITIATED:
hwc_data.current_servc = HWC_CMDW_WRITEMASK;
hwc_data.current_hwcb = hwc_data.page;
retval = condition_code;
break;
case HWC_BUSY:
retval = -EBUSY;
break;
default:
retval = -EIO;
}
return retval;
}
static int
write_event_mask_2 (u32 ext_int_param)
{
init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
int retval = 0;
if (hwcb->response_code != 0x0020) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe11\n\t"
"LRA 2,0(%0)\n\t"
"L 3,0(%1)\n\t"
"J .+0\n\t"
:
: "a" (hwcb), "a" (&(hwcb->response_code))
: "1", "2", "3");
#else
retval = -1;
#endif
} else {
if (hwcb->mask_length != 4) {
#ifdef DUMP_HWC_INIT_ERROR
__asm__ ("LHI 1,0xe52\n\t"
"LRA 2,0(%0)\n\t"
"J .+0 \n\t"
:
: "a" (hwcb)
: "1", "2");
#endif
} else {
retval += eval_hwc_receive_mask
(hwcb->hwc_receive_mask);
retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
}
}
hwc_data.current_servc = 0;
hwc_data.current_hwcb = NULL;
return retval;
}
static int
set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
{
int retval = 0;
hwc_ioctls_t tmp;
if (ioctls->width_htab > MAX_MESSAGE_SIZE) {
if (correct)
tmp.width_htab = MAX_MESSAGE_SIZE;
else
retval = -EINVAL;
} else
tmp.width_htab = ioctls->width_htab;
tmp.echo = ioctls->echo;
if (ioctls->columns > MAX_MESSAGE_SIZE) {
if (correct)
tmp.columns = MAX_MESSAGE_SIZE;
else
retval = -EINVAL;
} else
tmp.columns = ioctls->columns;
tmp.final_nl = ioctls->final_nl;
if (ioctls->max_hwcb < 2) {
if (correct)
tmp.max_hwcb = 2;
else
retval = -EINVAL;
} else
tmp.max_hwcb = ioctls->max_hwcb;
tmp.tolower = ioctls->tolower;
if (ioctls->kmem_hwcb > ioctls->max_hwcb) {
if (correct)
tmp.kmem_hwcb = ioctls->max_hwcb;
else
retval = -EINVAL;
} else
tmp.kmem_hwcb = ioctls->kmem_hwcb;
if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
if (correct)
ioctls->kmem_hwcb = MAX_KMEM_PAGES;
else
retval = -EINVAL;
}
if (ioctls->kmem_hwcb < 2) {
if (correct)
ioctls->kmem_hwcb = 2;
else
retval = -EINVAL;
}
tmp.delim = ioctls->delim;
if (!(retval < 0))
hwc_data.ioctls = tmp;
return retval;
}
int
do_hwc_init (void)
{
int retval;
memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
do {
retval = write_event_mask_1 ();
if (retval == -EBUSY) {
hwc_data.flags |= HWC_INIT;
__ctl_store (cr0, 0, 0);
cr0_save = cr0;
cr0 |= 0x00000200;
cr0 &= 0xFFFFF3AC;
__ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01"
:"=m" (psw_mask)::"memory");
while (!(hwc_data.flags & HWC_INTERRUPT))
barrier ();
asm volatile ("STNSM %0,0xFE"
:"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0);
hwc_data.flags &= ~HWC_INIT;
}
} while (retval == -EBUSY);
if (retval == -EIO) {
hwc_data.flags |= HWC_BROKEN;
printk (HWC_RW_PRINT_HEADER "HWC not operational\n");
}
return retval;
}
void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
int
hwc_init (void)
{
int retval;
#ifdef BUFFER_STRESS_TEST
init_hwcb_t *hwcb;
int i;
#endif
if (register_early_external_interrupt (0x2401, hwc_interrupt_handler,
&ext_int_info_hwc) != 0)
panic ("Couldn't request external interrupts 0x2401");
spin_lock_init (&hwc_data.lock);
#ifdef USE_VM_DETECTION
if (MACHINE_IS_VM) {
if (hwc_data.init_ioctls.columns > 76)
hwc_data.init_ioctls.columns = 76;
hwc_data.init_ioctls.tolower = 1;
if (!hwc_data.init_ioctls.delim)
hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER;
} else {
hwc_data.init_ioctls.tolower = 0;
hwc_data.init_ioctls.delim = 0;
}
#endif
retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
hwc_data.kmem_start = (unsigned long)
alloc_bootmem_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
hwc_data.kmem_end = hwc_data.kmem_start +
hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
retval = do_hwc_init ();
ctl_set_bit (0, 9);
#ifdef BUFFER_STRESS_TEST
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"use %i bytes for buffering.\n",
hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
for (i = 0; i < 500; i++) {
hwcb = (init_hwcb_t *) BUF_HWCB;
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"This is stress test message #%i, free: %i bytes\n",
i,
MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t)));
}
#endif
return /*retval */ 0;
}
signed int
hwc_register_calls (hwc_high_level_calls_t * calls)
{
if (calls == NULL)
return -EINVAL;
if (hwc_data.calls != NULL)
return -EBUSY;
hwc_data.calls = calls;
return 0;
}
signed int
hwc_unregister_calls (hwc_high_level_calls_t * calls)
{
if (hwc_data.calls == NULL)
return -EINVAL;
if (calls != hwc_data.calls)
return -EINVAL;
hwc_data.calls = NULL;
return 0;
}
int
hwc_send (hwc_request_t * req)
{
unsigned long flags;
int retval;
int cc;
spin_lock_irqsave (&hwc_data.lock, flags);
if (!req || !req->callback || !req->block) {
retval = -EINVAL;
goto unlock;
}
if (hwc_data.request) {
retval = -ENOTSUPP;
goto unlock;
}
cc = service_call (req->word, req->block);
switch (cc) {
case 0:
hwc_data.request = req;
hwc_data.current_servc = req->word;
hwc_data.current_hwcb = req->block;
retval = 0;
break;
case 2:
retval = -EBUSY;
break;
default:
retval = -ENOSYS;
}
unlock:
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
EXPORT_SYMBOL (hwc_send);
void
do_hwc_callback (u32 ext_int_param)
{
if (!hwc_data.request || !hwc_data.request->callback)
return;
if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
!= (unsigned long) hwc_data.request->block)
return;
hwc_data.request->callback (hwc_data.request);
hwc_data.request = NULL;
hwc_data.current_hwcb = NULL;
hwc_data.current_servc = 0;
}
void
hwc_do_interrupt (u32 ext_int_param)
{
u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR;
u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND;
if (hwc_data.flags & HWC_PTIMER_RUNS) {
del_timer (&hwc_data.poll_timer);
hwc_data.flags &= ~HWC_PTIMER_RUNS;
}
if (finished_hwcb) {
if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"interrupt: mismatch: "
"ext. int param. (0x%x) vs. "
"current HWCB (0x%x)\n",
ext_int_param,
hwc_data.current_hwcb);
} else {
if (hwc_data.request) {
do_hwc_callback (ext_int_param);
} else {
switch (hwc_data.current_servc) {
case HWC_CMDW_WRITEMASK:
write_event_mask_2 (ext_int_param);
break;
case HWC_CMDW_WRITEDATA:
write_event_data_2 (ext_int_param);
break;
case HWC_CMDW_READDATA:
unconditional_read_2 (ext_int_param);
break;
default:
break;
}
}
}
} else {
if (hwc_data.current_hwcb) {
internal_print (
DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"interrupt: mismatch: "
"ext. int. param. (0x%x) vs. "
"current HWCB (0x%x)\n",
ext_int_param,
hwc_data.current_hwcb);
}
}
if (evbuf_pending) {
unconditional_read_1 ();
} else {
write_event_data_1 ();
}
if (!hwc_data.calls || !hwc_data.calls->wake_up)
return;
(hwc_data.calls->wake_up) ();
}
void
hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
{
u32 ext_int_param = hwc_ext_int_param ();
irq_enter ();
if (hwc_data.flags & HWC_INIT) {
hwc_data.flags |= HWC_INTERRUPT;
} else if (hwc_data.flags & HWC_BROKEN) {
if (!do_hwc_init ()) {
hwc_data.flags &= ~HWC_BROKEN;
internal_print (DELAYED_WRITE,
HWC_RW_PRINT_HEADER
"delayed HWC setup after"
" temporary breakdown"
" (ext. int. parameter=0x%x)\n",
ext_int_param);
}
} else {
spin_lock (&hwc_data.lock);
hwc_do_interrupt (ext_int_param);
spin_unlock (&hwc_data.lock);
}
irq_exit ();
}
void
hwc_unblank (void)
{
spin_lock (&hwc_data.lock);
spin_unlock (&hwc_data.lock);
__ctl_store (cr0, 0, 0);
cr0_save = cr0;
cr0 |= 0x00000200;
cr0 &= 0xFFFFF3AC;
__ctl_load (cr0, 0, 0);
asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory");
while (ALL_HWCB_CHAR)
barrier ();
asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory");
__ctl_load (cr0_save, 0, 0);
}
int
hwc_ioctl (unsigned int cmd, unsigned long arg)
{
hwc_ioctls_t tmp = hwc_data.ioctls;
int retval = 0;
unsigned long flags;
unsigned int obuf;
spin_lock_irqsave (&hwc_data.lock, flags);
switch (cmd) {
case TIOCHWCSHTAB:
if (get_user (tmp.width_htab, (ioctl_htab_t *) arg))
goto fault;
break;
case TIOCHWCSECHO:
if (get_user (tmp.echo, (ioctl_echo_t *) arg))
goto fault;
break;
case TIOCHWCSCOLS:
if (get_user (tmp.columns, (ioctl_cols_t *) arg))
goto fault;
break;
case TIOCHWCSNL:
if (get_user (tmp.final_nl, (ioctl_nl_t *) arg))
goto fault;
break;
case TIOCHWCSOBUF:
if (get_user (obuf, (unsigned int *) arg))
goto fault;
if (obuf & 0xFFF)
tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12);
else
tmp.max_hwcb = (obuf >> 12);
break;
case TIOCHWCSCASE:
if (get_user (tmp.tolower, (ioctl_case_t *) arg))
goto fault;
break;
case TIOCHWCSDELIM:
if (get_user (tmp.delim, (ioctl_delim_t *) arg))
goto fault;
break;
case TIOCHWCSINIT:
retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
break;
case TIOCHWCGHTAB:
if (put_user (tmp.width_htab, (ioctl_htab_t *) arg))
goto fault;
break;
case TIOCHWCGECHO:
if (put_user (tmp.echo, (ioctl_echo_t *) arg))
goto fault;
break;
case TIOCHWCGCOLS:
if (put_user (tmp.columns, (ioctl_cols_t *) arg))
goto fault;
break;
case TIOCHWCGNL:
if (put_user (tmp.final_nl, (ioctl_nl_t *) arg))
goto fault;
break;
case TIOCHWCGOBUF:
if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg))
goto fault;
break;
case TIOCHWCGKBUF:
if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg))
goto fault;
break;
case TIOCHWCGCASE:
if (put_user (tmp.tolower, (ioctl_case_t *) arg))
goto fault;
break;
case TIOCHWCGDELIM:
if (put_user (tmp.delim, (ioctl_delim_t *) arg))
goto fault;
break;
#if 0
case TIOCHWCGINIT:
if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg))
goto fault;
break;
case TIOCHWCGCURR:
if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg))
goto fault;
break;
#endif
default:
goto noioctlcmd;
}
if (_IOC_DIR (cmd) == _IOC_WRITE)
retval = set_hwc_ioctls (&tmp, 0);
goto out;
fault:
retval = -EFAULT;
goto out;
noioctlcmd:
retval = -ENOIOCTLCMD;
out:
spin_unlock_irqrestore (&hwc_data.lock, flags);
return retval;
}
/*
* drivers/s390/char/hwc_rw.h
* interface to the HWC-read/write driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*/
#ifndef __HWC_RW_H__
#define __HWC_RW_H__
#include <linux/ioctl.h>
typedef struct {
void (*move_input) (unsigned char *, unsigned int);
void (*wake_up) (void);
} hwc_high_level_calls_t;
struct _hwc_request;
typedef void hwc_callback_t (struct _hwc_request *);
typedef struct _hwc_request {
void *block;
u32 word;
hwc_callback_t *callback;
void *data;
} __attribute__ ((packed))
hwc_request_t;
#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x]))
#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c)))
#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c)))
#define IN_HWCB 1
#define IN_WRITE_BUF 2
#define IN_BUFS_TOTAL (IN_HWCB | IN_WRITE_BUF)
typedef unsigned short int ioctl_htab_t;
typedef unsigned char ioctl_echo_t;
typedef unsigned short int ioctl_cols_t;
typedef signed char ioctl_nl_t;
typedef unsigned short int ioctl_obuf_t;
typedef unsigned char ioctl_case_t;
typedef unsigned char ioctl_delim_t;
typedef struct {
ioctl_htab_t width_htab;
ioctl_echo_t echo;
ioctl_cols_t columns;
ioctl_nl_t final_nl;
ioctl_obuf_t max_hwcb;
ioctl_obuf_t kmem_hwcb;
ioctl_case_t tolower;
ioctl_delim_t delim;
} hwc_ioctls_t;
extern hwc_ioctls_t _hwc_ioctls;
#define HWC_IOCTL_LETTER 'B'
#define TIOCHWCSHTAB _IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
#define TIOCHWCSECHO _IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
#define TIOCHWCSCOLS _IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
#define TIOCHWCSNL _IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
#define TIOCHWCSOBUF _IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
#define TIOCHWCSINIT _IO(HWC_IOCTL_LETTER, 6)
#define TIOCHWCSCASE _IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
#define TIOCHWCSDELIM _IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
#define TIOCHWCGHTAB _IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
#define TIOCHWCGECHO _IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
#define TIOCHWCGCOLS _IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
#define TIOCHWCGNL _IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
#define TIOCHWCGOBUF _IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
#define TIOCHWCGINIT _IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
#define TIOCHWCGCASE _IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
#define TIOCHWCGDELIM _IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
#define TIOCHWCGKBUF _IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
#define TIOCHWCGCURR _IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
#ifndef __HWC_RW_C__
extern int hwc_init (void);
extern int hwc_write (int from_user, const unsigned char *, unsigned int);
extern unsigned int hwc_chars_in_buffer (unsigned char);
extern unsigned int hwc_write_room (unsigned char);
extern void hwc_flush_buffer (unsigned char);
extern void hwc_unblank (void);
extern signed int hwc_ioctl (unsigned int, unsigned long);
extern void do_hwc_interrupt (void);
extern int hwc_printk (const char *,...);
extern signed int hwc_register_calls (hwc_high_level_calls_t *);
extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
extern int hwc_send (hwc_request_t *);
#endif
#endif
/*
* drivers/s390/char/hwc_tty.c
* HWC line mode terminal driver.
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
*
* Thanks to Martin Schwidefsky.
*/
#include <linux/config.h>
#include <linux/major.h>
#include <linux/termios.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "hwc_rw.h"
#include "ctrlchar.h"
#define HWC_TTY_PRINT_HEADER "hwc tty driver: "
#define HWC_TTY_BUF_SIZE 512
typedef struct {
struct tty_struct *tty;
unsigned char buf[HWC_TTY_BUF_SIZE];
unsigned short int buf_count;
spinlock_t lock;
hwc_high_level_calls_t calls;
} hwc_tty_data_struct;
static hwc_tty_data_struct hwc_tty_data =
{ /* NULL/0 */ };
static struct tty_driver hwc_tty_driver;
static struct tty_struct *hwc_tty_table[1];
static struct termios *hwc_tty_termios[1];
static struct termios *hwc_tty_termios_locked[1];
static int hwc_tty_refcount = 0;
extern struct termios tty_std_termios;
void hwc_tty_wake_up (void);
void hwc_tty_input (unsigned char *, unsigned int);
static int
hwc_tty_open (struct tty_struct *tty,
struct file *filp)
{
if (minor (tty->device) - tty->driver.minor_start)
return -ENODEV;
tty->driver_data = &hwc_tty_data;
hwc_tty_data.buf_count = 0;
hwc_tty_data.tty = tty;
tty->low_latency = 0;
hwc_tty_data.calls.wake_up = hwc_tty_wake_up;
hwc_tty_data.calls.move_input = hwc_tty_input;
hwc_register_calls (&(hwc_tty_data.calls));
return 0;
}
static void
hwc_tty_close (struct tty_struct *tty,
struct file *filp)
{
if (minor (tty->device) != tty->driver.minor_start) {
printk (KERN_WARNING HWC_TTY_PRINT_HEADER
"do not close hwc tty because of wrong device number");
return;
}
if (tty->count > 1)
return;
hwc_tty_data.tty = NULL;
hwc_unregister_calls (&(hwc_tty_data.calls));
}
static int
hwc_tty_write_room (struct tty_struct *tty)
{
int retval;
retval = hwc_write_room (IN_BUFS_TOTAL);
return retval;
}
static int
hwc_tty_write (struct tty_struct *tty,
int from_user,
const unsigned char *buf,
int count)
{
int retval;
if (hwc_tty_data.buf_count > 0) {
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
}
retval = hwc_write (from_user, buf, count);
return retval;
}
static void
hwc_tty_put_char (struct tty_struct *tty,
unsigned char ch)
{
unsigned long flags;
spin_lock_irqsave (&hwc_tty_data.lock, flags);
if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) {
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
}
hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
hwc_tty_data.buf_count++;
spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
}
static void
hwc_tty_flush_chars (struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave (&hwc_tty_data.lock, flags);
hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
hwc_tty_data.buf_count = 0;
spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
}
static int
hwc_tty_chars_in_buffer (struct tty_struct *tty)
{
int retval;
retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
return retval;
}
static void
hwc_tty_flush_buffer (struct tty_struct *tty)
{
hwc_tty_wake_up ();
}
static int
hwc_tty_ioctl (
struct tty_struct *tty,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
return hwc_ioctl (cmd, arg);
}
void
hwc_tty_wake_up (void)
{
if (hwc_tty_data.tty == NULL)
return;
if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
hwc_tty_data.tty->ldisc.write_wakeup)
(hwc_tty_data.tty->ldisc.write_wakeup) (hwc_tty_data.tty);
wake_up_interruptible (&hwc_tty_data.tty->write_wait);
}
void
hwc_tty_input (unsigned char *buf, unsigned int count)
{
struct tty_struct *tty = hwc_tty_data.tty;
if (tty != NULL) {
unsigned int cchar = ctrlchar_handle(buf, count, tty);
switch (cchar & CTRLCHAR_MASK) {
case CTRLCHAR_SYSRQ:
return;
case CTRLCHAR_CTRL:
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = cchar;
break;
case CTRLCHAR_NONE:
memcpy (tty->flip.char_buf_ptr, buf, count);
if (count < 2 || (
strncmp (buf + count - 2, "^n", 2) ||
strncmp (buf + count - 2, "\0252n", 2))) {
tty->flip.char_buf_ptr[count] = '\n';
count++;
} else
count -= 2;
memset (tty->flip.flag_buf_ptr, TTY_NORMAL, count);
tty->flip.char_buf_ptr += count;
tty->flip.flag_buf_ptr += count;
tty->flip.count += count;
break;
}
tty_flip_buffer_push (tty);
hwc_tty_wake_up ();
}
}
void
hwc_tty_init (void)
{
if (!CONSOLE_IS_HWC)
return;
memset (&hwc_tty_driver, 0, sizeof (struct tty_driver));
memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct));
hwc_tty_driver.magic = TTY_DRIVER_MAGIC;
hwc_tty_driver.driver_name = "tty_hwc";
hwc_tty_driver.name = "ttyS";
hwc_tty_driver.name_base = 0;
hwc_tty_driver.major = TTY_MAJOR;
hwc_tty_driver.minor_start = 64;
hwc_tty_driver.num = 1;
hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
hwc_tty_driver.subtype = SYSTEM_TYPE_TTY;
hwc_tty_driver.init_termios = tty_std_termios;
hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
hwc_tty_driver.init_termios.c_oflag = ONLCR;
hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO;
hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW;
hwc_tty_driver.refcount = &hwc_tty_refcount;
hwc_tty_driver.table = hwc_tty_table;
hwc_tty_driver.termios = hwc_tty_termios;
hwc_tty_driver.termios_locked = hwc_tty_termios_locked;
hwc_tty_driver.open = hwc_tty_open;
hwc_tty_driver.close = hwc_tty_close;
hwc_tty_driver.write = hwc_tty_write;
hwc_tty_driver.put_char = hwc_tty_put_char;
hwc_tty_driver.flush_chars = hwc_tty_flush_chars;
hwc_tty_driver.write_room = hwc_tty_write_room;
hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer;
hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer;
hwc_tty_driver.ioctl = hwc_tty_ioctl;
hwc_tty_driver.throttle = NULL;
hwc_tty_driver.unthrottle = NULL;
hwc_tty_driver.send_xchar = NULL;
hwc_tty_driver.set_termios = NULL;
hwc_tty_driver.set_ldisc = NULL;
hwc_tty_driver.stop = NULL;
hwc_tty_driver.start = NULL;
hwc_tty_driver.hangup = NULL;
hwc_tty_driver.break_ctl = NULL;
hwc_tty_driver.wait_until_sent = NULL;
hwc_tty_driver.read_proc = NULL;
hwc_tty_driver.write_proc = NULL;
if (tty_register_driver (&hwc_tty_driver))
panic ("Couldn't register hwc_tty driver\n");
}
/*
* drivers/s390/char/sclp.c
* core function to access sclp interface
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/bootmem.h>
#include <linux/err.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/s390_ext.h>
#include "sclp.h"
#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "
/*
* decides wether we make use of the macro MACHINE_IS_VM to
* configure the driver for VM at run time (a little bit
* different behaviour); otherwise we use the default
* settings in sclp_data.init_ioctls
*/
#define USE_VM_DETECTION
/* Structure for register_early_external_interrupt. */
static ext_int_info_t ext_int_info_hwc;
/* spinlock to protect global variables of sclp_core */
static spinlock_t sclp_lock;
/* Mask of valid sclp events */
static sccb_mask_t sclp_receive_mask;
static sccb_mask_t sclp_send_mask;
/* List of registered event types */
static struct list_head sclp_reg_list;
/* sccb queue */
struct list_head sclp_req_queue;
/* sccb for unconditional read */
static struct sclp_req sclp_read_req;
static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
/* sccb for write mask sccb */
static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
static unsigned long sclp_status = 0;
/* some status flags */
#define SCLP_INIT 0
#define SCLP_RUNNING 1
#define SCLP_READING 2
/*
* assembler instruction for Service Call
*/
static int
__service_call(sclp_cmdw_t command, void *sccb)
{
int cc;
/*
* Mnemonic: SERVC Rx, Ry [RRE]
*
* Rx: SCLP command word
* Ry: address of SCCB
*/
__asm__ __volatile__(
" .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
" ipm %0\n"
" srl %0,28"
: "=&d" (cc)
: "d" (command), "a" (__pa(sccb))
: "cc", "memory" );
/*
* cc == 0: Service Call succesful initiated
* cc == 2: SCLP busy, new Service Call not initiated,
* new SCCB unchanged
* cc == 3: SCLP function not operational
*/
if (cc == 3)
return -EIO;
/*
* We set the SCLP_RUNNING bit for cc 2 as well because if
* service_call returns cc 2 some old request is running
* that has to complete first
*/
set_bit(SCLP_RUNNING, &sclp_status);
if (cc == 2)
return -EBUSY;
return 0;
}
static int
__sclp_start_request(void)
{
struct sclp_req *req;
int rc;
/* quick exit if sclp is already in use */
if (test_bit(SCLP_RUNNING, &sclp_status))
return -EBUSY;
/* quick exit if queue is empty */
if (list_empty(&sclp_req_queue))
return -EINVAL;
/* try to start the first request on the request queue. */
req = list_entry(sclp_req_queue.next, struct sclp_req, list);
rc = __service_call(req->command, req->sccb);
switch (rc) {
case 0:
req->status = SCLP_REQ_RUNNING;
break;
case -EIO:
req->status = SCLP_REQ_FAILED;
if (req->callback != NULL)
req->callback(req, req->callback_data);
break;
}
return rc;
}
static int
sclp_process_evbufs(struct sccb_header *sccb)
{
unsigned long flags;
struct evbuf_header *evbuf;
struct list_head *l;
struct sclp_register *t;
spin_lock_irqsave(&sclp_lock, flags);
evbuf = (struct evbuf_header *) (sccb + 1);
while ((void *) evbuf < (void *) sccb + sccb->length) {
/* check registered event */
list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list);
if (t->receive_mask & (1 << (32 - evbuf->type))) {
if (t->receiver_fn != NULL)
t->receiver_fn(evbuf);
break;
}
}
}
spin_unlock_irqrestore(&sclp_lock, flags);
return -ENOSYS;
}
/*
* postprocessing of unconditional read service call
*/
static void
sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
{
static struct {
u16 code; char *msg;
} errors[] = {
{ 0x0040, "SCLP equipment check" },
{ 0x0100, "SCCB boundary violation" },
{ 0x01f0, "invalid command" },
{ 0x0300, "insufficient SCCB length" },
{ 0x40f0, "invalid function code" },
{ 0x60f0, "got interrupt but no data found" },
{ 0x62f0, "got interrupt but no data found" },
{ 0x70f0, "invalid selection mask" }
};
struct sccb_header *sccb;
char *errmsg;
int i;
sccb = read_req->sccb;
if (sccb->response_code == 0x0020 ||
sccb->response_code == 0x0220) {
/* normal read completion, event buffers stored in sccb */
if (sclp_process_evbufs(sccb) == 0) {
clear_bit(SCLP_READING, &sclp_status);
return;
}
errmsg = "invalid response code";
} else {
errmsg = NULL;
for (i = 0; i < sizeof(errors)/sizeof(errors[0]); i++)
if (sccb->response_code == errors[i].code) {
errmsg = errors[i].msg;
break;
}
if (errmsg == NULL)
errmsg = "invalid response code";
}
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"unconditional read: %s (response code =0x%x).\n",
errmsg, sccb->response_code);
clear_bit(SCLP_READING, &sclp_status);
}
/*
* Function to issue Read Event Data/Unconditional Read
*/
static void
__sclp_unconditional_read(void)
{
struct sccb_header *sccb;
struct sclp_req *read_req;
/*
* Dont try to initiate Unconditional Read if we are not able to
* receive anything
*/
if (sclp_receive_mask == 0)
return;
/* Don't try reading if a read is already outstanding */
if (test_and_set_bit(SCLP_READING, &sclp_status))
return;
/* Initialise read sccb */
sccb = (struct sccb_header *) sclp_read_sccb;
clear_page(sccb);
sccb->length = PAGE_SIZE;
sccb->function_code = 0; /* unconditional read */
sccb->control_mask[2] = 0x80; /* variable length response */
/* stick the request structure to the end of the page */
read_req = &sclp_read_req;
read_req->command = SCLP_CMDW_READDATA;
read_req->status = SCLP_REQ_QUEUED;
read_req->callback = sclp_unconditional_read_cb;
read_req->sccb = sccb;
/* Add read request to the head of queue */
list_add(&read_req->list, &sclp_req_queue);
}
/*
* Handler for service-signal external interruptions
*/
static void
sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
{
u32 ext_int_param, finished_sccb, evbuf_pending;
struct list_head *l;
struct sclp_req *req, *tmp;
int cpu;
cpu = smp_processor_id();
ext_int_param = S390_lowcore.ext_params;
finished_sccb = ext_int_param & -8U;
evbuf_pending = ext_int_param & 1;
irq_enter();
/*
* Only do request callsbacks if sclp is initialised.
* This avoids strange effects for a pending request
* from before the last re-ipl.
*/
if (test_bit(SCLP_INIT, &sclp_status)) {
spin_lock(&sclp_lock);
req = NULL;
if (finished_sccb != 0U) {
list_for_each(l, &sclp_req_queue) {
tmp = list_entry(l, struct sclp_req, list);
if (finished_sccb == (u32)(addr_t) tmp->sccb) {
list_del(&tmp->list);
req = tmp;
break;
}
}
}
spin_unlock(&sclp_lock);
if (req != NULL) {
req->status = SCLP_REQ_DONE;
if (req->callback != NULL)
req->callback(req, req->callback_data);
}
}
spin_lock(&sclp_lock);
/* Head queue a read sccb if an event buffer is pending */
if (evbuf_pending)
__sclp_unconditional_read();
/* Now clear the running bit */
clear_bit(SCLP_RUNNING, &sclp_status);
/* and start next request on the queue */
__sclp_start_request();
spin_unlock(&sclp_lock);
irq_exit();
}
/*
* Wait synchronously for external interrupt of sclp. We may not receive
* any other external interrupt, so we disable all other external interrupts
* in control register 0.
*/
void
sclp_sync_wait(void)
{
unsigned long psw_mask;
unsigned long cr0, cr0_sync;
/*
* save cr0
* enable service signal external interruption (cr0.22)
* disable cr0.20-21, cr0.25, cr0.27, cr0.30-31
* don't touch any other bit in cr0
*/
__ctl_store(cr0, 0, 0);
cr0_sync = cr0;
cr0_sync |= 0x00000200;
cr0_sync &= 0xFFFFF3AC;
__ctl_load(cr0_sync, 0, 0);
/* enable external interruptions (PSW-mask.7) */
asm volatile ("STOSM 0(%1),0x01"
: "=m" (psw_mask) : "a" (&psw_mask) : "memory");
/* wait until ISR signals receipt of interrupt */
while (test_bit(SCLP_RUNNING, &sclp_status))
barrier();
/* disable external interruptions */
asm volatile ("SSM 0(%0)"
: : "a" (&psw_mask) : "memory");
/* restore cr0 */
__ctl_load(cr0, 0, 0);
}
/*
* Queue a request to the tail of the ccw_queue. Start the I/O if
* possible.
*/
void
sclp_add_request(struct sclp_req *req)
{
unsigned long flags;
if (!test_bit(SCLP_INIT, &sclp_status))
return;
spin_lock_irqsave(&sclp_lock, flags);
/* queue the request */
req->status = SCLP_REQ_QUEUED;
list_add_tail(&req->list, &sclp_req_queue);
/* try to start the first request on the queue */
__sclp_start_request();
spin_unlock_irqrestore(&sclp_lock, flags);
}
/* state change notification */
struct sclp_statechangebuf {
struct evbuf_header header;
u8 validity_sclp_active_facility_mask : 1;
u8 validity_sclp_receive_mask : 1;
u8 validity_sclp_send_mask : 1;
u8 validity_read_data_function_mask : 1;
u16 _zeros : 12;
u16 mask_length;
u64 sclp_active_facility_mask;
sccb_mask_t sclp_receive_mask;
sccb_mask_t sclp_send_mask;
u32 read_data_function_mask;
} __attribute__((packed));
static inline void
__sclp_notify_state_change(void)
{
struct list_head *l;
struct sclp_register *t;
sccb_mask_t receive_mask, send_mask;
list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list);
receive_mask = t->receive_mask & sclp_receive_mask;
send_mask = t->send_mask & sclp_send_mask;
if (t->sclp_receive_mask != receive_mask ||
t->sclp_send_mask != send_mask) {
t->sclp_receive_mask = receive_mask;
t->sclp_send_mask = send_mask;
if (t->state_change_fn != NULL)
t->state_change_fn(t);
}
}
}
static void
sclp_state_change(struct evbuf_header *evbuf)
{
unsigned long flags;
struct sclp_statechangebuf *scbuf;
spin_lock_irqsave(&sclp_lock, flags);
scbuf = (struct sclp_statechangebuf *) evbuf;
if (scbuf->validity_sclp_receive_mask) {
if (scbuf->mask_length != 4)
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"state change event with mask length %i\n",
scbuf->mask_length);
else
/* set new send mask */
sclp_receive_mask = scbuf->sclp_receive_mask;
}
if (scbuf->validity_sclp_send_mask) {
if (scbuf->mask_length != 4)
printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
"state change event with mask length %i\n",
scbuf->mask_length);
else
/* set new send mask */
sclp_send_mask = scbuf->sclp_send_mask;
}
__sclp_notify_state_change();
spin_unlock_irqrestore(&sclp_lock, flags);
}
struct sclp_register sclp_state_change_event = {
.receive_mask = EvTyp_StateChange_Mask,
.receiver_fn = sclp_state_change
};
/*
* SCLP quiesce event handler
*/
#ifdef CONFIG_SMP
static volatile unsigned long cpu_quiesce_map;
static void
do_load_quiesce_psw(void * __unused)
{
psw_t quiesce_psw;
clear_bit(smp_processor_id(), &cpu_quiesce_map);
if (smp_processor_id() == 0) {
/* Wait for all other cpus to enter do_load_quiesce_psw */
while (cpu_quiesce_map != 0);
/* Quiesce the last cpu with the special psw */
quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
quiesce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
signal_processor(smp_processor_id(), sigp_stop);
}
static void
do_machine_quiesce(void)
{
cpu_quiesce_map = cpu_online_map;
smp_call_function(do_load_quiesce_psw, NULL, 0, 0);
do_load_quiesce_psw(NULL);
}
#else
static void
do_machine_quiesce(void)
{
psw_t quiesce_psw;
quiesce_psw.mask = _DW_PSW_MASK;
queisce_psw.addr = 0xfff;
__load_psw(quiesce_psw);
}
#endif
extern void ctrl_alt_del(void);
static void
sclp_quiesce(struct evbuf_header *evbuf)
{
/*
* We got a "shutdown" request.
* Add a call to an appropriate "shutdown" routine here. This
* routine should set all PSWs to 'disabled-wait', 'stopped'
* or 'check-stopped' - except 1 PSW which needs to carry a
* special bit pattern called 'quiesce PSW'.
*/
_machine_restart = (void *) do_machine_quiesce;
_machine_halt = do_machine_quiesce;
_machine_power_off = do_machine_quiesce;
ctrl_alt_del();
}
struct sclp_register sclp_quiesce_event = {
.receive_mask = EvTyp_SigQuiesce_Mask,
.receiver_fn = sclp_quiesce
};
/* initialisation of SCLP */
struct init_sccb {
struct sccb_header header;
u16 _reserved;
u16 mask_length;
sccb_mask_t receive_mask;
sccb_mask_t send_mask;
sccb_mask_t sclp_send_mask;
sccb_mask_t sclp_receive_mask;
} __attribute__((packed));
static int
sclp_init_mask(void)
{
unsigned long flags;
struct init_sccb *sccb;
struct sclp_req *req;
struct list_head *l;
struct sclp_register *t;
int rc;
sccb = (struct init_sccb *) sclp_init_sccb;
/* stick the request structure to the end of the init sccb page */
req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;
/* SCLP setup concerning receiving and sending Event Buffers */
req->command = SCLP_CMDW_WRITEMASK;
req->status = SCLP_REQ_QUEUED;
req->callback = NULL;
req->sccb = sccb;
/* setup sccb for writemask command */
memset(sccb, 0, sizeof(struct init_sccb));
sccb->header.length = sizeof(struct init_sccb);
sccb->mask_length = sizeof(sccb_mask_t);
/* copy in the sccb mask of the registered event types */
spin_lock_irqsave(&sclp_lock, flags);
list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list);
sccb->receive_mask |= t->receive_mask;
sccb->send_mask |= t->send_mask;
}
sccb->sclp_receive_mask = 0;
sccb->sclp_send_mask = 0;
if (test_bit(SCLP_INIT, &sclp_status)) {
/* add request to sclp queue */
list_add_tail(&req->list, &sclp_req_queue);
/* and start if SCLP is idle */
if (!test_bit(SCLP_RUNNING, &sclp_status))
__sclp_start_request();
spin_unlock_irqrestore(&sclp_lock, flags);
/* now wait for completion */
while (req->status != SCLP_REQ_DONE &&
req->status != SCLP_REQ_FAILED)
sclp_sync_wait();
spin_lock_irqsave(&sclp_lock, flags);
} else {
/*
* Special case for the very first write mask command.
* The interrupt handler is not removing request from
* the request queue and doesn't call callbacks yet
* because there might be an pending old interrupt
* after a Re-IPL. We have to receive and ignore it.
*/
do {
rc = __service_call(req->command, req->sccb);
spin_unlock_irqrestore(&sclp_lock, flags);
if (rc == -EIO)
return -ENOSYS;
sclp_sync_wait();
spin_lock_irqsave(&sclp_lock, flags);
} while (rc == -EBUSY);
}
sclp_receive_mask = sccb->sclp_receive_mask;
sclp_send_mask = sccb->sclp_send_mask;
__sclp_notify_state_change();
spin_unlock_irqrestore(&sclp_lock, flags);
return 0;
}
/*
* sclp setup function. Called early (no kmalloc!) from sclp_console_init().
*/
static int
sclp_init(void)
{
int rc;
if (test_bit(SCLP_INIT, &sclp_status))
/* Already initialised. */
return 0;
/*
* request the 0x2401 external interrupt
* The sclp driver is initialized early (before kmalloc works). We
* need to use register_early_external_interrupt.
*/
if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,
&ext_int_info_hwc) != 0)
return -EBUSY;
spin_lock_init(&sclp_lock);
INIT_LIST_HEAD(&sclp_req_queue);
/* init event list */
INIT_LIST_HEAD(&sclp_reg_list);
list_add(&sclp_state_change_event.list, &sclp_reg_list);
list_add(&sclp_quiesce_event.list, &sclp_reg_list);
/* enable service-signal external interruptions,
* Control Register 0 bit 22 := 1
* (besides PSW bit 7 must be set to 1 sometimes for external
* interruptions)
*/
ctl_set_bit(0, 9);
/* do the initial write event mask */
rc = sclp_init_mask();
if (rc == 0) {
/* Ok, now everything is setup right. */
set_bit(SCLP_INIT, &sclp_status);
return 0;
}
/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */
ctl_clear_bit(0,9);
unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,
&ext_int_info_hwc);
return rc;
}
int
sclp_register(struct sclp_register *reg)
{
unsigned long flags;
struct list_head *l;
struct sclp_register *t;
if (!MACHINE_HAS_SCLP)
return -ENODEV;
if (!test_bit(SCLP_INIT, &sclp_status))
sclp_init();
spin_lock_irqsave(&sclp_lock, flags);
/* check already registered event masks for collisions */
list_for_each(l, &sclp_reg_list) {
t = list_entry(l, struct sclp_register, list);
if (t->receive_mask & reg->receive_mask ||
t->send_mask & reg->send_mask) {
spin_unlock_irqrestore(&sclp_lock, flags);
return -EBUSY;
}
}
/*
* set present mask to 0 to trigger state change
* callback in sclp_init_mask
*/
reg->sclp_receive_mask = 0;
reg->sclp_send_mask = 0;
list_add(&reg->list, &sclp_reg_list);
spin_unlock_irqrestore(&sclp_lock, flags);
sclp_init_mask();
return 0;
}
void
sclp_unregister(struct sclp_register *reg)
{
unsigned long flags;
spin_lock_irqsave(&sclp_lock, flags);
list_del(&reg->list);
spin_unlock_irqrestore(&sclp_lock, flags);
sclp_init_mask();
}
/*
* drivers/s390/char/sclp.h
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __SCLP_H__
#define __SCLP_H__
#include <linux/types.h>
#include <linux/list.h>
#include <asm/ebcdic.h>
/* maximum number of pages concerning our own memory management */
#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
#define MAX_CONSOLE_PAGES 4
#define EvTyp_OpCmd 0x01
#define EvTyp_Msg 0x02
#define EvTyp_StateChange 0x08
#define EvTyp_PMsgCmd 0x09
#define EvTyp_CntlProgOpCmd 0x20
#define EvTyp_CntlProgIdent 0x0B
#define EvTyp_SigQuiesce 0x1D
#define EvTyp_OpCmd_Mask 0x80000000
#define EvTyp_Msg_Mask 0x40000000
#define EvTyp_StateChange_Mask 0x01000000
#define EvTyp_PMsgCmd_Mask 0x00800000
#define EvTyp_CtlProgOpCmd_Mask 0x00000001
#define EvTyp_CtlProgIdent_Mask 0x00200000
#define EvTyp_SigQuiesce_Mask 0x00000008
#define GnrlMsgFlgs_DOM 0x8000
#define GnrlMsgFlgs_SndAlrm 0x4000
#define GnrlMsgFlgs_HoldMsg 0x2000
#define LnTpFlgs_CntlText 0x8000
#define LnTpFlgs_LabelText 0x4000
#define LnTpFlgs_DataText 0x2000
#define LnTpFlgs_EndText 0x1000
#define LnTpFlgs_PromptText 0x0800
#define SCLP_COMMAND_INITIATED 0
#define SCLP_BUSY 2
#define SCLP_NOT_OPERATIONAL 3
typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READDATA 0x00770005
#define SCLP_CMDW_WRITEDATA 0x00760005
#define SCLP_CMDW_WRITEMASK 0x00780005
#define GDS_ID_MDSMU 0x1310
#define GDS_ID_MDSRouteInfo 0x1311
#define GDS_ID_AgUnWrkCorr 0x1549
#define GDS_ID_SNACondReport 0x1532
#define GDS_ID_CPMSU 0x1212
#define GDS_ID_RoutTargInstr 0x154D
#define GDS_ID_OpReq 0x8070
#define GDS_ID_TextCmd 0x1320
#define GDS_KEY_SelfDefTextMsg 0x31
typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */
struct sccb_header {
u16 length;
u8 function_code;
u8 control_mask[3];
u16 response_code;
} __attribute__((packed));
struct gds_subvector {
u8 length;
u8 key;
} __attribute__((packed));
struct gds_vector {
u16 length;
u16 gds_id;
} __attribute__((packed));
struct evbuf_header {
u16 length;
u8 type;
u8 flags;
u16 _reserved;
} __attribute__((packed));
struct sclp_req {
struct list_head list; /* list_head for request queueing. */
sclp_cmdw_t command; /* sclp command to execute */
void *sccb; /* pointer to the sccb to execute */
char status; /* status of this request */
/* Callback that is called after reaching final status. */
void (*callback)(struct sclp_req *, void *data);
void *callback_data;
};
#define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */
#define SCLP_REQ_QUEUED 0x01 /* request is queued to be processed */
#define SCLP_REQ_RUNNING 0x02 /* request is currently running */
#define SCLP_REQ_DONE 0x03 /* request is completed successfully */
#define SCLP_REQ_FAILED 0x05 /* request is finally failed */
/* function pointers that a high level driver has to use for registration */
/* of some routines it wants to be called from the low level driver */
struct sclp_register {
struct list_head list;
/* event masks this user is registered for */
sccb_mask_t receive_mask;
sccb_mask_t send_mask;
/* actually present events */
sccb_mask_t sclp_receive_mask;
sccb_mask_t sclp_send_mask;
/* called if event type availability changes */
void (*state_change_fn)(struct sclp_register *);
/* called for events in cp_receive_mask/sclp_receive_mask */
void (*receiver_fn)(struct evbuf_header *);
};
/* externals from sclp.c */
void sclp_add_request(struct sclp_req *req);
void sclp_sync_wait(void);
int sclp_register(struct sclp_register *reg);
void sclp_unregister(struct sclp_register *reg);
/* useful inlines */
/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */
/* translate single character from ASCII to EBCDIC */
static inline unsigned char
sclp_ascebc(unsigned char ch)
{
return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch];
}
/* translate string from EBCDIC to ASCII */
static inline void
sclp_ebcasc_str(unsigned char *str, int nr)
{
(MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr);
}
/* translate string from ASCII to EBCDIC */
static inline void
sclp_ascebc_str(unsigned char *str, int nr)
{
(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
}
#endif /* __SCLP_H__ */
/*
* drivers/s390/char/sclp_con.c
* SCLP line mode console driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bootmem.h>
#include "sclp.h"
#include "sclp_rw.h"
#define SCLP_CON_PRINT_HEADER "sclp console driver: "
#define sclp_console_major 4 /* TTYAUX_MAJOR */
#define sclp_console_minor 64
#define sclp_console_name "console"
/* Lock to guard over changes to global variables */
static spinlock_t sclp_con_lock;
/* List of free pages that can be used for console output buffering */
static struct list_head sclp_con_pages;
/* List of full struct sclp_buffer structures ready for output */
static struct list_head sclp_con_outqueue;
/* Counter how many buffers are emitted (max 1) and how many */
/* are on the output queue. */
static int sclp_con_buffer_count;
/* Pointer to current console buffer */
static struct sclp_buffer *sclp_conbuf;
/* Timer for delayed output of console messages */
static struct timer_list sclp_con_timer;
/* Output format for console messages */
static unsigned short sclp_con_columns;
static unsigned short sclp_con_width_htab;
static void
sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
{
unsigned long flags;
struct sclp_buffer *next;
void *page;
/* FIXME: what if rc != 0x0020 */
page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_con_lock, flags);
list_add_tail((struct list_head *) page, &sclp_con_pages);
sclp_con_buffer_count--;
/* Remove buffer from outqueue */
list_del(&buffer->list);
/* Check if there is a pending buffer on the out queue. */
next = NULL;
if (!list_empty(&sclp_con_outqueue))
next = list_entry(sclp_con_outqueue.next,
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_con_lock, flags);
if (next != NULL)
sclp_emit_buffer(next, sclp_conbuf_callback);
}
static inline void
__sclp_conbuf_emit(struct sclp_buffer *buffer)
{
list_add_tail(&buffer->list, &sclp_con_outqueue);
if (sclp_con_buffer_count++ == 0)
sclp_emit_buffer(buffer, sclp_conbuf_callback);
}
/*
* When this routine is called from the timer then we flush the
* temporary write buffer without further waiting on a final new line.
*/
static void
sclp_console_timeout(unsigned long data)
{
unsigned long flags;
spin_lock_irqsave(&sclp_con_lock, flags);
if (sclp_conbuf != NULL) {
__sclp_conbuf_emit(sclp_conbuf);
sclp_conbuf = NULL;
}
spin_unlock_irqrestore(&sclp_con_lock, flags);
}
/*
* Writes the given message to S390 system console
*/
void
sclp_console_write(struct console *console, const char *message,
unsigned int count)
{
unsigned long flags;
void *page;
int written;
if (count <= 0)
return;
spin_lock_irqsave(&sclp_con_lock, flags);
/*
* process escape characters, write message into buffer,
* send buffer to SCLP
*/
do {
/* make sure we have a console output buffer */
if (sclp_conbuf == NULL) {
while (list_empty(&sclp_con_pages)) {
spin_unlock_irqrestore(&sclp_con_lock, flags);
sclp_sync_wait();
spin_lock_irqsave(&sclp_con_lock, flags);
}
page = sclp_con_pages.next;
list_del((struct list_head *) page);
sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
sclp_con_width_htab);
}
/* try to write the string to the current output buffer */
written = sclp_write(sclp_conbuf, message, count, 0);
if (written == -EFAULT || written == count)
break;
/*
* Not all characters could be written to the current
* output buffer. Emit the buffer, create a new buffer
* and then output the rest of the string.
*/
__sclp_conbuf_emit(sclp_conbuf);
sclp_conbuf = NULL;
message += written;
count -= written;
} while (count > 0);
/* Setup timer to output current console buffer after 1/10 second */
if (sclp_conbuf != NULL && !timer_pending(&sclp_con_timer)) {
init_timer(&sclp_con_timer);
sclp_con_timer.function = sclp_console_timeout;
sclp_con_timer.data = 0UL;
sclp_con_timer.expires = jiffies + HZ/10;
add_timer(&sclp_con_timer);
}
spin_unlock_irqrestore(&sclp_con_lock, flags);
}
/* returns the device number of the SCLP console */
kdev_t
sclp_console_device(struct console *c)
{
return mk_kdev(sclp_console_major, sclp_console_minor);
}
/*
* This routine is called from panic when the kernel
* is going to give up. We have to make sure that all buffers
* will be flushed to the SCLP.
*/
void
sclp_console_unblank(void)
{
unsigned long flags;
spin_lock_irqsave(&sclp_con_lock, flags);
if (timer_pending(&sclp_con_timer))
del_timer(&sclp_con_timer);
if (sclp_conbuf != NULL) {
__sclp_conbuf_emit(sclp_conbuf);
sclp_conbuf = NULL;
}
while (sclp_con_buffer_count > 0) {
spin_unlock_irqrestore(&sclp_con_lock, flags);
sclp_sync_wait();
spin_lock_irqsave(&sclp_con_lock, flags);
}
spin_unlock_irqrestore(&sclp_con_lock, flags);
}
/*
* used to register the SCLP console to the kernel and to
* give printk necessary information
*/
struct console sclp_console =
{
.name = sclp_console_name,
.write = sclp_console_write,
.device = sclp_console_device,
.unblank = sclp_console_unblank,
.flags = CON_PRINTBUFFER
};
/*
* called by console_init() in drivers/char/tty_io.c at boot-time.
*/
void __init
sclp_console_init(void)
{
void *page;
int i;
if (!CONSOLE_IS_SCLP)
return;
if (sclp_rw_init() != 0)
return;
/* Allocate pages for output buffering */
INIT_LIST_HEAD(&sclp_con_pages);
for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
page = alloc_bootmem_low_pages(PAGE_SIZE);
if (page == NULL)
return;
list_add_tail((struct list_head *) page, &sclp_con_pages);
}
INIT_LIST_HEAD(&sclp_con_outqueue);
spin_lock_init(&sclp_con_lock);
sclp_con_buffer_count = 0;
sclp_conbuf = NULL;
init_timer(&sclp_con_timer);
/* Set output format */
if (MACHINE_IS_VM)
/*
* save 4 characters for the CPU number
* written at start of each line by VM/CP
*/
sclp_con_columns = 76;
else
sclp_con_columns = 80;
sclp_con_width_htab = 8;
/* enable printks access to this driver */
register_console(&sclp_console);
}
/*
* Author: Martin Peschke <mpeschke@de.ibm.com>
* Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
*
* SCLP Control-Program Identification.
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <asm/ebcdic.h>
#include <asm/semaphore.h>
#include "sclp.h"
#include "sclp_rw.h"
#define CPI_LENGTH_SYSTEM_TYPE 8
#define CPI_LENGTH_SYSTEM_NAME 8
#define CPI_LENGTH_SYSPLEX_NAME 8
struct cpi_evbuf {
struct evbuf_header header;
u8 id_format;
u8 reserved0;
u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
u64 reserved1;
u8 system_name[CPI_LENGTH_SYSTEM_NAME];
u64 reserved2;
u64 system_level;
u64 reserved3;
u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
u8 reserved4[16];
} __attribute__((packed));
struct cpi_sccb {
struct sccb_header header;
struct cpi_evbuf cpi_evbuf;
} __attribute__((packed));
/* Event type structure for write message and write priority message */
static struct sclp_register sclp_cpi_event =
{
.send_mask = EvTyp_CtlProgIdent_Mask
};
MODULE_AUTHOR(
"Martin Peschke, IBM Deutschland Entwicklung GmbH "
"<mpeschke@de.ibm.com>");
MODULE_DESCRIPTION(
"identify this operating system instance to the S/390 "
"or zSeries hardware");
static char *system_name = NULL;
MODULE_PARM(system_name, "s");
MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
static char *sysplex_name = NULL;
#ifdef ALLOW_SYSPLEX_NAME
MODULE_PARM(sysplex_name, "s");
MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
#endif
/* use default value for this field (as well as for system level) */
static char *system_type = "LINUX";
static int
cpi_check_parms(void)
{
/* reject if no system type specified */
if (!system_type) {
printk("cpi: bug: no system type specified\n");
return -EINVAL;
}
/* reject if system type larger than 8 characters */
if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) {
printk("cpi: bug: system type has length of %li characters - "
"only %i characters supported\n",
strlen(system_type), CPI_LENGTH_SYSTEM_TYPE);
return -EINVAL;
}
/* reject if no system name specified */
if (!system_name) {
printk("cpi: no system name specified\n");
return -EINVAL;
}
/* reject if system name larger than 8 characters */
if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) {
printk("cpi: system name has length of %li characters - "
"only %i characters supported\n",
strlen(system_name), CPI_LENGTH_SYSTEM_NAME);
return -EINVAL;
}
/* reject if specified sysplex name larger than 8 characters */
if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) {
printk("cpi: sysplex name has length of %li characters"
" - only %i characters supported\n",
strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME);
return -EINVAL;
}
return 0;
}
static void
cpi_callback(struct sclp_req *req, void *data)
{
struct semaphore *sem;
sem = (struct semaphore *) data;
up(sem);
}
static struct sclp_req *
cpi_prepare_req(void)
{
struct sclp_req *req;
struct cpi_sccb *sccb;
struct cpi_evbuf *evb;
req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
if (req == NULL)
return ERR_PTR(-ENOMEM);
sccb = (struct cpi_sccb *) get_free_page(GFP_KERNEL);
if (sccb == NULL) {
kfree(req);
return ERR_PTR(-ENOMEM);
}
memset(sccb, 0, sizeof(struct cpi_sccb));
/* setup SCCB for Control-Program Identification */
sccb->header.length = sizeof(struct cpi_sccb);
sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
sccb->cpi_evbuf.header.type = 0x0B;
evb = &sccb->cpi_evbuf;
/* set system type */
memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
memcpy(evb->system_type, system_type, strlen(system_type));
sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
/* set system name */
memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
memcpy(evb->system_name, system_name, strlen(system_name));
sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
/* set sytem level */
evb->system_level = LINUX_VERSION_CODE;
/* set sysplex name */
if (sysplex_name) {
memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name));
sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
}
/* prepare request data structure presented to SCLP driver */
req->command = SCLP_CMDW_WRITEDATA;
req->sccb = sccb;
req->status = SCLP_REQ_FILLED;
req->callback = cpi_callback;
return req;
}
static void
cpi_free_req(struct sclp_req *req)
{
free_page((unsigned long) req->sccb);
kfree(req);
}
static int __init
cpi_module_init(void)
{
struct semaphore sem;
struct sclp_req *req;
int rc;
rc = cpi_check_parms();
if (rc)
return rc;
rc = sclp_register(&sclp_cpi_event);
if (rc) {
/* could not register sclp event. Die. */
printk("cpi: could not register to hardware console.\n");
return -EINVAL;
}
if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
printk("cpi: no control program identification support\n");
sclp_unregister(&sclp_cpi_event);
return -ENOTSUPP;
}
req = cpi_prepare_req();
if (IS_ERR(req)) {
printk("cpi: couldn't allocate request\n");
sclp_unregister(&sclp_cpi_event);
return PTR_ERR(req);
}
/* Prepare semaphore */
sema_init(&sem, 0);
req->callback_data = &sem;
/* Add request to sclp queue */
sclp_add_request(req);
/* make "insmod" sleep until callback arrives */
down(&sem);
rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
if (rc != 0x0020) {
printk("cpi: failed with response code 0x%x\n", rc);
rc = -ECOMM;
} else
rc = 0;
cpi_free_req(req);
return rc;
}
static void __exit cpi_module_exit(void)
{
}
/* declare driver module init/cleanup functions */
module_init(cpi_module_init);
module_exit(cpi_module_exit);
/*
* drivers/s390/char/sclp_rw.c
* driver: reading from and writing to system console on S/390 via SCLP
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
#include <asm/uaccess.h>
#include "sclp.h"
#include "sclp_rw.h"
#define SCLP_RW_PRINT_HEADER "sclp low level driver: "
/*
* The room for the SCCB (only for writing) is not equal to a pages size
* (as it is specified as the maximum size in the the SCLP ducumentation)
* because of the additional data structure described above.
*/
#define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
/* Event type structure for write message and write priority message */
struct sclp_register sclp_rw_event = {
.send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
};
/*
* Setup a sclp write buffer. Gets a page as input (4K) and returns
* a pointer to a struct sclp_buffer structure that is located at the
* end of the input page. This reduces the buffer space by a few
* bytes but simplifies things.
*/
struct sclp_buffer *
sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
{
struct sclp_buffer *buffer;
struct write_sccb *sccb;
sccb = (struct write_sccb *) page;
/*
* We keep the struct sclp_buffer structure at the end
* of the sccb page.
*/
buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
buffer->sccb = sccb;
buffer->mto_number = 0;
buffer->mto_char_sum = 0;
buffer->current_line = NULL;
buffer->current_length = 0;
buffer->columns = columns;
buffer->htab = htab;
/* initialize sccb */
memset(sccb, 0, sizeof(struct write_sccb));
sccb->header.length = sizeof(struct write_sccb);
sccb->msg_buf.header.length = sizeof(struct msg_buf);
sccb->msg_buf.header.type = EvTyp_Msg;
sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
sccb->msg_buf.mdb.header.type = 1;
sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */
sccb->msg_buf.mdb.header.revision_code = 1;
sccb->msg_buf.mdb.go.length = sizeof(struct go);
sccb->msg_buf.mdb.go.type = 1;
return buffer;
}
/*
* Return a pointer to the orignal page that has been used to create
* the buffer.
*/
void *
sclp_unmake_buffer(struct sclp_buffer *buffer)
{
return buffer->sccb;
}
/*
* Creates a new Message Text Object with enough room for str_len
* characters. This function will create a new sccb for buffering
* the mto if the current buffer sccb is full. A full buffer sccb
* is submitted for output.
* Returns a pointer to the start of the string in the mto.
*/
static int
sclp_create_mto(struct sclp_buffer *buffer, int max_len)
{
struct write_sccb *sccb;
struct mto *mto;
int mto_size;
/* max size of new Message Text Object including message text */
mto_size = sizeof(struct mto) + max_len;
/* check if current buffer sccb can contain the mto */
sccb = buffer->sccb;
if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size)
return -ENOMEM;
/* find address of new message text object */
mto = (struct mto *)(((unsigned long) sccb) + sccb->header.length);
/*
* fill the new Message-Text Object,
* starting behind the former last byte of the SCCB
*/
memset(mto, 0, sizeof(struct mto));
mto->length = sizeof(struct mto);
mto->type = 4; /* message text object */
mto->line_type_flags = LnTpFlgs_EndText; /* end text */
/* set pointer to first byte after struct mto. */
buffer->current_line = (char *) (mto + 1);
buffer->current_length = 0;
return 0;
}
/*
* Add the mto created with sclp_create_mto to the buffer sccb using
* the str_len parameter as the real length of the string.
*/
static void
sclp_add_mto(struct sclp_buffer *buffer)
{
struct write_sccb *sccb;
struct mto *mto;
int str_len, mto_size;
str_len = buffer->current_length;
buffer->current_line = NULL;
buffer->current_length = 0;
/* real size of new Message Text Object including message text */
mto_size = sizeof(struct mto) + str_len;
/* find address of new message text object */
sccb = buffer->sccb;
mto = (struct mto *)(((unsigned long) sccb) + sccb->header.length);
/* set size of message text object */
mto->length = mto_size;
/*
* update values of sizes
* (SCCB, Event(Message) Buffer, Message Data Block)
*/
sccb->header.length += mto_size;
sccb->msg_buf.header.length += mto_size;
sccb->msg_buf.mdb.header.length += mto_size;
/*
* count number of buffered messages (= number of Message Text
* Objects) and number of buffered characters
* for the SCCB currently used for buffering and at all
*/
buffer->mto_number++;
buffer->mto_char_sum += str_len;
}
void
sclp_move_current_line(struct sclp_buffer *to, struct sclp_buffer *from)
{
if (from->current_line == NULL)
return;
if (sclp_create_mto(to, from->columns) != 0)
return;
if (from->current_length > 0) {
memcpy(to->current_line,
from->current_line - from->current_length,
from->current_length);
to->current_line += from->current_length;
to->current_length = from->current_length;
}
from->current_line = NULL;
}
/*
* processing of a message including escape characters,
* returns number of characters written to the output sccb
* ("processed" means that is not guaranteed that the character have already
* been sent to the SCLP but that it will be done at least next time the SCLP
* is not busy)
*/
int
sclp_write(struct sclp_buffer *buffer,
const char *msg, int count, int from_user)
{
int spaces, i_msg;
char ch;
int rc;
/*
* parse msg for escape sequences (\t,\v ...) and put formated
* msg into an mto (created by sclp_create_mto).
*
* We have to do this work ourselfs because there is no support for
* these characters on the native machine and only partial support
* under VM (Why does VM interpret \n but the native machine doesn't ?)
*
* Depending on i/o-control setting the message is always written
* immediatly or we wait for a final new line maybe coming with the
* next message. Besides we avoid a buffer overrun by writing its
* content.
*
* RESTRICTIONS:
*
* \r and \b work within one line because we are not able to modify
* previous output that have already been accepted by the SCLP.
*
* \t combined with following \r is not correctly represented because
* \t is expanded to some spaces but \r does not know about a
* previous \t and decreases the current position by one column.
* This is in order to a slim and quick implementation.
*/
for (i_msg = 0; i_msg < count; i_msg++) {
if (from_user) {
if (get_user(ch, msg + i_msg) != 0)
return -EFAULT;
} else
ch = msg[i_msg];
switch (ch) {
case '\n': /* new line, line feed (ASCII) */
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, 0);
if (rc)
return i_msg;
}
sclp_add_mto(buffer);
break;
case '\a': /* bell, one for several times */
/* set SCLP sound alarm bit in General Object */
buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
GnrlMsgFlgs_SndAlrm;
break;
case '\t': /* horizontal tabulator */
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns);
if (rc)
return i_msg;
}
/* "go to (next htab-boundary + 1, same line)" */
do {
if (buffer->current_length >= buffer->columns)
break;
/* ok, add a blank */
*buffer->current_line++ = 0x40;
buffer->current_length++;
} while (buffer->current_length % buffer->htab);
break;
case '\f': /* form feed */
case '\v': /* vertical tabulator */
/* "go to (actual column, actual line + 1)" */
/* = new line, leading spaces */
if (buffer->current_line != NULL) {
spaces = buffer->current_length;
sclp_add_mto(buffer);
rc = sclp_create_mto(buffer, buffer->columns);
if (rc)
return i_msg;
memset(buffer->current_line, 0x40, spaces);
buffer->current_line += spaces;
buffer->current_length = spaces;
} else {
/* one an empty line this is the same as \n */
rc = sclp_create_mto(buffer, buffer->columns);
if (rc)
return i_msg;
sclp_add_mto(buffer);
}
break;
case '\b': /* backspace */
/* "go to (actual column - 1, actual line)" */
/* decrement counter indicating position, */
/* do not remove last character */
if (buffer->current_line != NULL &&
buffer->current_length > 0) {
buffer->current_length--;
buffer->current_line--;
}
break;
case 0x00: /* end of string */
/* transfer current line to SCCB */
if (buffer->current_line != NULL)
sclp_add_mto(buffer);
/* skip the rest of the message including the 0 byte */
i_msg = count;
break;
default: /* no escape character */
/* do not output unprintable characters */
if (!isprint(ch))
break;
/* check if new mto needs to be created */
if (buffer->current_line == NULL) {
rc = sclp_create_mto(buffer, buffer->columns);
if (rc)
return i_msg;
}
*buffer->current_line++ = sclp_ascebc(ch);
buffer->current_length++;
break;
}
/* check if current mto is full */
if (buffer->current_line != NULL &&
buffer->current_length >= buffer->columns)
sclp_add_mto(buffer);
}
/* return number of processed characters */
return i_msg;
}
/*
* Return the number of free bytes in the sccb
*/
int
sclp_buffer_space(struct sclp_buffer *buffer)
{
int count;
count = MAX_SCCB_ROOM - buffer->sccb->header.length;
if (buffer->current_line != NULL)
count -= sizeof(struct mto) + buffer->current_length;
return count;
}
/*
* Return number of characters in buffer
*/
int
sclp_chars_in_buffer(struct sclp_buffer *buffer)
{
int count;
count = buffer->mto_char_sum;
if (buffer->current_line != NULL)
count += buffer->current_length;
return count;
}
/*
* sets or provides some values that influence the drivers behaviour
*/
void
sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
{
buffer->columns = columns;
if (buffer->current_line != NULL &&
buffer->current_length > buffer->columns)
sclp_add_mto(buffer);
}
void
sclp_set_htab(struct sclp_buffer *buffer, unsigned short htab)
{
buffer->htab = htab;
}
/*
* called by sclp_console_init and/or sclp_tty_init
*/
int
sclp_rw_init(void)
{
static int init_done = 0;
if (init_done)
return 0;
init_done = 1;
return sclp_register(&sclp_rw_event);
}
/*
* second half of Write Event Data-function that has to be done after
* interruption indicating completion of Service Call.
*/
static void
sclp_writedata_callback(struct sclp_req *request, void *data)
{
struct sclp_buffer *buffer;
struct write_sccb *sccb;
buffer = (struct sclp_buffer *) data;
sccb = buffer->sccb;
/* FIXME: proper error handling */
/* check SCLP response code and choose suitable action */
switch (sccb->header.response_code) {
case 0x0020 :
/* normal completion, buffer processed, message(s) sent */
break;
case 0x0340: /* contained SCLP equipment check */
case 0x40F0: /* function code disabled in SCLP receive mask */
break;
default:
/* sclp_free_sccb(sccb); */
printk(KERN_WARNING SCLP_RW_PRINT_HEADER
"write event data failed "
"(response code: 0x%x SCCB address %p).\n",
sccb->header.response_code, sccb);
break;
}
if (buffer->callback != NULL)
buffer->callback(buffer, sccb->header.response_code);
}
/*
* Setup the request structure in the struct sclp_buffer to do SCLP Write
* Event Data and pass the request to the core SCLP loop.
*/
void
sclp_emit_buffer(struct sclp_buffer *buffer,
void (*callback)(struct sclp_buffer *, int))
{
struct write_sccb *sccb;
/* add current line if there is one */
if (buffer->current_line != NULL)
sclp_add_mto(buffer);
/* Are there messages in the output buffer ? */
if (buffer->mto_number <= 0)
return;
sccb = buffer->sccb;
if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
/* Use normal write message */
sccb->msg_buf.header.type = EvTyp_Msg;
else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
/* Use write priority message */
sccb->msg_buf.header.type = EvTyp_PMsgCmd;
else {
callback(buffer, -ENOSYS);
return;
}
buffer->request.command = SCLP_CMDW_WRITEDATA;
buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback;
buffer->request.callback_data = buffer;
buffer->request.sccb = sccb;
buffer->callback = callback;
sclp_add_request(&buffer->request);
}
/*
* drivers/s390/char/sclp_rw.h
* interface to the SCLP-read/write driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __SCLP_RW_H__
#define __SCLP_RW_H__
struct mto {
u16 length;
u16 type;
u16 line_type_flags;
u8 alarm_control;
u8 _reserved[3];
} __attribute__((packed));
struct go {
u16 length;
u16 type;
u32 domid;
u8 hhmmss_time[8];
u8 th_time[3];
u8 reserved_0;
u8 dddyyyy_date[7];
u8 _reserved_1;
u16 general_msg_flags;
u8 _reserved_2[10];
u8 originating_system_name[8];
u8 job_guest_name[8];
} __attribute__((packed));
struct mdb_header {
u16 length;
u16 type;
u32 tag;
u32 revision_code;
} __attribute__((packed));
struct mdb {
struct mdb_header header;
struct go go;
} __attribute__((packed));
struct msg_buf {
struct evbuf_header header;
struct mdb mdb;
} __attribute__((packed));
struct write_sccb {
struct sccb_header header;
struct msg_buf msg_buf;
} __attribute__((packed));
/* The number of empty mto buffers that can be contained in a single sccb. */
#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
sizeof(struct write_sccb)) / sizeof(struct mto))
/*
* data structure for information about list of SCCBs (only for writing),
* will be located at the end of a SCCBs page
*/
struct sclp_buffer {
struct list_head list; /* list_head for sccb_info chain */
struct sclp_req request;
struct write_sccb *sccb;
char *current_line;
int current_length;
/* output format settings */
unsigned short columns;
unsigned short htab;
/* statistics about this buffer */
unsigned int mto_char_sum; /* # chars in sccb */
unsigned int mto_number; /* # mtos in sccb */
/* Callback that is called after reaching final status. */
void (*callback)(struct sclp_buffer *, int);
};
int sclp_rw_init(void);
struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short);
void *sclp_unmake_buffer(struct sclp_buffer *);
int sclp_buffer_space(struct sclp_buffer *);
int sclp_write(struct sclp_buffer *buffer, const char *, int, int);
void sclp_move_current_line(struct sclp_buffer *, struct sclp_buffer *);
void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
void sclp_set_columns(struct sclp_buffer *, unsigned short);
void sclp_set_htab(struct sclp_buffer *, unsigned short);
int sclp_chars_in_buffer(struct sclp_buffer *);
#endif /* __SCLP_RW_H__ */
/*
* drivers/s390/char/sclp_tty.c
* SCLP line mode terminal driver.
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include "ctrlchar.h"
#include "sclp.h"
#include "sclp_rw.h"
#include "sclp_tty.h"
#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "
/*
* size of a buffer that collects single characters coming in
* via sclp_tty_put_char()
*/
#define SCLP_TTY_BUF_SIZE 512
/*
* There is excatly one SCLP terminal, so we can keep things simple
* and allocate all variables statically.
*/
/* Lock to guard over changes to global variables. */
static spinlock_t sclp_tty_lock;
/* List of free pages that can be used for console output buffering. */
static struct list_head sclp_tty_pages;
/* List of full struct sclp_buffer structures ready for output. */
static struct list_head sclp_tty_outqueue;
/* Counter how many buffers are emitted. */
static int sclp_tty_buffer_count;
/* Pointer to current console buffer. */
static struct sclp_buffer *sclp_ttybuf;
/* Timer for delayed output of console messages. */
static struct timer_list sclp_tty_timer;
/* Waitqueue to wait for buffers to get empty. */
static wait_queue_head_t sclp_tty_waitq;
static struct tty_struct *sclp_tty;
static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
static unsigned short int sclp_tty_chars_count;
static struct tty_driver sclp_tty_driver;
static struct tty_struct * sclp_tty_table[1];
static struct termios * sclp_tty_termios[1];
static struct termios * sclp_tty_termios_locked[1];
static int sclp_tty_refcount = 0;
extern struct termios tty_std_termios;
static struct sclp_ioctls sclp_ioctls;
static struct sclp_ioctls sclp_ioctls_init =
{
8, /* 1 hor. tab. = 8 spaces */
0, /* no echo of input by this driver */
80, /* 80 characters/line */
1, /* write after 1/10 s without final new line */
MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */
MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */
0, /* do not convert to lower case */
0x6c /* to seprate upper and lower case */
/* ('%' in EBCDIC) */
};
/* This routine is called whenever we try to open a SCLP terminal. */
static int
sclp_tty_open(struct tty_struct *tty, struct file *filp)
{
/* only 1 SCLP terminal supported */
if (minor(tty->device) != tty->driver.minor_start)
return -ENODEV;
sclp_tty = tty;
tty->driver_data = NULL;
tty->low_latency = 0;
return 0;
}
/* This routine is called when the SCLP terminal is closed. */
static void
sclp_tty_close(struct tty_struct *tty, struct file *filp)
{
/* only 1 SCLP terminal supported */
if (minor(tty->device) != tty->driver.minor_start)
return;
if (tty->count > 1)
return;
sclp_tty = NULL;
}
/* execute commands to control the i/o behaviour of the SCLP tty at runtime */
static int
sclp_tty_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
unsigned long flags;
unsigned int obuf;
int check;
int rc;
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
rc = 0;
check = 0;
switch (cmd) {
case TIOCSCLPSHTAB:
/* set width of horizontal tab */
if (get_user(sclp_ioctls.htab, (unsigned short *) arg))
rc = -EFAULT;
else
check = 1;
break;
case TIOCSCLPGHTAB:
/* get width of horizontal tab */
if (put_user(sclp_ioctls.htab, (unsigned short *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSECHO:
/* enable/disable echo of input */
if (get_user(sclp_ioctls.echo, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPGECHO:
/* Is echo of input enabled ? */
if (put_user(sclp_ioctls.echo, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSCOLS:
/* set number of columns for output */
if (get_user(sclp_ioctls.columns, (unsigned short *) arg))
rc = -EFAULT;
else
check = 1;
break;
case TIOCSCLPGCOLS:
/* get number of columns for output */
if (put_user(sclp_ioctls.columns, (unsigned short *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSNL:
/* enable/disable writing without final new line character */
if (get_user(sclp_ioctls.final_nl, (signed char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPGNL:
/* Is writing without final new line character enabled ? */
if (put_user(sclp_ioctls.final_nl, (signed char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSOBUF:
/*
* set the maximum buffers size for output, will be rounded
* up to next 4kB boundary and stored as number of SCCBs
* (4kB Buffers) limitation: 256 x 4kB
*/
if (get_user(obuf, (unsigned int *) arg) == 0) {
if (obuf & 0xFFF)
sclp_ioctls.max_sccb = (obuf >> 12) + 1;
else
sclp_ioctls.max_sccb = (obuf >> 12);
} else
rc = -EFAULT;
break;
case TIOCSCLPGOBUF:
/* get the maximum buffers size for output */
obuf = sclp_ioctls.max_sccb << 12;
if (put_user(obuf, (unsigned int *) arg))
rc = -EFAULT;
break;
case TIOCSCLPGKBUF:
/* get the number of buffers got from kernel at startup */
if (put_user(sclp_ioctls.kmem_sccb, (unsigned short *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSCASE:
/* enable/disable conversion from upper to lower case */
if (get_user(sclp_ioctls.tolower, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPGCASE:
/* Is conversion from upper to lower case of input enabled? */
if (put_user(sclp_ioctls.tolower, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSDELIM:
/*
* set special character used for seperating upper and
* lower case, 0x00 disables this feature
*/
if (get_user(sclp_ioctls.delim, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPGDELIM:
/*
* get special character used for seperating upper and
* lower case, 0x00 disables this feature
*/
if (put_user(sclp_ioctls.delim, (unsigned char *) arg))
rc = -EFAULT;
break;
case TIOCSCLPSINIT:
/* set initial (default) sclp ioctls */
sclp_ioctls = sclp_ioctls_init;
check = 1;
break;
default:
rc = -ENOIOCTLCMD;
break;
}
if (check) {
spin_lock_irqsave(&sclp_tty_lock, flags);
if (sclp_ttybuf != NULL) {
sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab);
sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns);
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
}
return rc;
}
/*
* This routine returns the numbers of characters the tty driver
* will accept for queuing to be written. This number is subject
* to change as output buffers get emptied, or if the output flow
* control is acted. This is not an exact number because not every
* character needs the same space in the sccb. The worst case is
* a string of newlines. Every newlines creates a new mto which
* needs 8 bytes.
*/
static int
sclp_tty_write_room (struct tty_struct *tty)
{
unsigned long flags;
struct list_head *l;
int count;
spin_lock_irqsave(&sclp_tty_lock, flags);
count = 0;
if (sclp_ttybuf != NULL)
count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto);
list_for_each(l, &sclp_tty_pages)
count += NR_EMPTY_MTO_PER_SCCB;
spin_unlock_irqrestore(&sclp_tty_lock, flags);
return count;
}
static void
sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
{
unsigned long flags;
struct sclp_buffer *next;
void *page;
/* FIXME: what if rc != 0x0020 */
page = sclp_unmake_buffer(buffer);
spin_lock_irqsave(&sclp_tty_lock, flags);
list_add_tail((struct list_head *) page, &sclp_tty_pages);
sclp_tty_buffer_count--;
/* Remove buffer from outqueue */
list_del(&buffer->list);
/* Check if there is a pending buffer on the out queue. */
next = NULL;
if (!list_empty(&sclp_tty_outqueue))
next = list_entry(sclp_tty_outqueue.next,
struct sclp_buffer, list);
spin_unlock_irqrestore(&sclp_tty_lock, flags);
if (next != NULL)
sclp_emit_buffer(next, sclp_ttybuf_callback);
wake_up(&sclp_tty_waitq);
/* check if the tty needs a wake up call */
if (sclp_tty != NULL) {
if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
sclp_tty->ldisc.write_wakeup)
(sclp_tty->ldisc.write_wakeup)(sclp_tty);
wake_up_interruptible(&sclp_tty->write_wait);
}
}
static inline void
__sclp_ttybuf_emit(struct sclp_buffer *buffer)
{
list_add_tail(&buffer->list, &sclp_tty_outqueue);
if (sclp_tty_buffer_count++ == 0)
sclp_emit_buffer(buffer, sclp_ttybuf_callback);
}
/*
* When this routine is called from the timer then we flush the
* temporary write buffer.
*/
static void
sclp_tty_timeout(unsigned long data)
{
unsigned long flags;
spin_lock_irqsave(&sclp_tty_lock, flags);
if (sclp_ttybuf != NULL) {
__sclp_ttybuf_emit(sclp_ttybuf);
sclp_ttybuf = NULL;
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
}
/*
* Write a string to the sclp tty.
*/
static void
sclp_tty_write_string(const unsigned char *str, int count, int from_user)
{
unsigned long flags;
void *page;
int written;
if (count <= 0)
return;
spin_lock_irqsave(&sclp_tty_lock, flags);
do {
/* Create a sclp output buffer if none exists yet */
if (sclp_ttybuf == NULL) {
while (list_empty(&sclp_tty_pages)) {
spin_unlock_irqrestore(&sclp_tty_lock, flags);
wait_event(sclp_tty_waitq,
!list_empty(&sclp_tty_pages));
spin_lock_irqsave(&sclp_tty_lock, flags);
}
page = sclp_tty_pages.next;
list_del((struct list_head *) page);
sclp_ttybuf = sclp_make_buffer(page,
sclp_ioctls.columns,
sclp_ioctls.htab);
}
/* try to write the string to the current output buffer */
written = sclp_write(sclp_ttybuf, str, count, from_user);
if (written == -EFAULT || written == count)
break;
/*
* Not all characters could be written to the current
* output buffer. Emit the buffer, create a new buffer
* and then output the rest of the string.
*/
__sclp_ttybuf_emit(sclp_ttybuf);
sclp_ttybuf = NULL;
str += written;
count -= written;
} while (count > 0);
/* Setup timer to output current console buffer after 1/10 second */
if (sclp_ioctls.final_nl) {
if (sclp_ttybuf != NULL && !timer_pending(&sclp_tty_timer)) {
init_timer(&sclp_tty_timer);
sclp_tty_timer.function = sclp_tty_timeout;
sclp_tty_timer.data = 0UL;
sclp_tty_timer.expires = jiffies + HZ/10;
add_timer(&sclp_tty_timer);
}
} else {
__sclp_ttybuf_emit(sclp_ttybuf);
sclp_ttybuf = NULL;
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
}
/*
* This routine is called by the kernel to write a series of characters to the
* tty device. The characters may come from user space or kernel space. This
* routine will return the number of characters actually accepted for writing.
*/
static int
sclp_tty_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count)
{
if (sclp_tty_chars_count > 0) {
sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
sclp_tty_write_string(buf, count, from_user);
return count;
}
/*
* This routine is called by the kernel to write a single character to the tty
* device. If the kernel uses this routine, it must call the flush_chars()
* routine (if defined) when it is done stuffing characters into the driver.
*
* Characters provided to sclp_tty_put_char() are buffered by the SCLP driver.
* If the given character is a '\n' the contents of the SCLP write buffer
* - including previous characters from sclp_tty_put_char() and strings from
* sclp_write() without final '\n' - will be written.
*/
static void
sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
{
sclp_tty_chars[sclp_tty_chars_count++] = ch;
if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
/*
* This routine is called by the kernel after it has written a series of
* characters to the tty device using put_char().
*/
static void
sclp_tty_flush_chars(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
/*
* This routine returns the number of characters in the write buffer of the
* SCLP driver. The provided number includes all characters that are stored
* in the SCCB (will be written next time the SCLP is not busy) as well as
* characters in the write buffer (will not be written as long as there is a
* final line feed missing).
*/
static int
sclp_tty_chars_in_buffer(struct tty_struct *tty)
{
unsigned long flags;
struct list_head *l;
struct sclp_buffer *t;
int count;
spin_lock_irqsave(&sclp_tty_lock, flags);
count = 0;
if (sclp_ttybuf != NULL)
count = sclp_chars_in_buffer(sclp_ttybuf);
list_for_each(l, &sclp_tty_outqueue) {
t = list_entry(l, struct sclp_buffer, list);
count += sclp_chars_in_buffer(sclp_ttybuf);
}
spin_unlock_irqrestore(&sclp_tty_lock, flags);
return count;
}
/*
* removes all content from buffers of low level driver
*/
static void
sclp_tty_flush_buffer(struct tty_struct *tty)
{
if (sclp_tty_chars_count > 0) {
sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
sclp_tty_chars_count = 0;
}
}
/*
* push input to tty
*/
void sclp_tty_input(unsigned char* buf, unsigned int count)
{
unsigned int cchar;
/*
* If this tty driver is currently closed
* then throw the received input away.
*/
if (sclp_tty == NULL)
return;
cchar = ctrlchar_handle(buf, count, sclp_tty);
switch (cchar & CTRLCHAR_MASK) {
case CTRLCHAR_SYSRQ:
break;
case CTRLCHAR_CTRL:
sclp_tty->flip.count++;
*sclp_tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*sclp_tty->flip.char_buf_ptr++ = cchar;
tty_flip_buffer_push(sclp_tty);
break;
case CTRLCHAR_NONE:
/* send (normal) input to line discipline */
memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
if (count < 2 ||
strncmp (buf + count - 2, "^n", 2) ||
strncmp (buf + count - 2, "\0252n", 2)) {
sclp_tty->flip.char_buf_ptr[count] = '\n';
count++;
} else
count -= 2;
memset(sclp_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
sclp_tty->flip.char_buf_ptr += count;
sclp_tty->flip.flag_buf_ptr += count;
sclp_tty->flip.count += count;
tty_flip_buffer_push(sclp_tty);
break;
}
}
/*
* get a EBCDIC string in upper/lower case,
* find out characters in lower/upper case seperated by a special character,
* modifiy original string,
* returns length of resulting string
*/
static int
sclp_switch_cases(unsigned char *buf, int count,
unsigned char delim, int tolower)
{
unsigned char *ip, *op;
int toggle;
/* initially changing case is off */
toggle = 0;
ip = op = buf;
while (count-- > 0) {
/* compare with special character */
if (*ip == delim) {
/* followed by another special character? */
if (count && ip[1] == delim) {
/*
* ... then put a single copy of the special
* character to the output string
*/
*op++ = *ip++;
count--;
} else
/*
* ... special character follower by a normal
* character toggles the case change behaviour
*/
toggle = ~toggle;
/* skip special character */
ip++;
} else
/* not the special character */
if (toggle)
/* but case switching is on */
if (tolower)
/* switch to uppercase */
*op++ = _ebc_toupper[(int) *ip++];
else
/* switch to lowercase */
*op++ = _ebc_tolower[(int) *ip++];
else
/* no case switching, copy the character */
*op++ = *ip++;
}
/* return length of reformatted string. */
return op - buf;
}
static void
sclp_get_input(char *start, char *end)
{
int count;
count = end - start;
/*
* if set in ioctl convert EBCDIC to lower case
* (modify original input in SCCB)
*/
if (sclp_ioctls.tolower)
EBC_TOLOWER(start, count);
/*
* if set in ioctl find out characters in lower or upper case
* (depends on current case) seperated by a special character,
* works on EBCDIC
*/
if (sclp_ioctls.delim)
count = sclp_switch_cases(start, count,
sclp_ioctls.delim,
sclp_ioctls.tolower);
/* convert EBCDIC to ASCII (modify original input in SCCB) */
sclp_ebcasc_str(start, count);
/* if set in ioctl write operators input to console */
if (sclp_ioctls.echo)
sclp_tty_write(sclp_tty, 0, start, count);
/* transfer input to high level driver */
sclp_tty_input(start, count);
}
static inline struct gds_vector *
find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
{
struct gds_vector *vec;
for (vec = start; vec < end; (void *) vec += vec->length)
if (vec->gds_id == id)
return vec;
return NULL;
}
static inline struct gds_subvector *
find_gds_subvector(struct gds_subvector *start,
struct gds_subvector *end, u8 key)
{
struct gds_subvector *subvec;
for (subvec = start; subvec < end; (void *) subvec += subvec->length)
if (subvec->key == key)
return subvec;
return NULL;
}
static inline void
sclp_eval_selfdeftextmsg(struct gds_subvector *start,
struct gds_subvector *end)
{
struct gds_subvector *subvec;
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end, 0x30);
if (!subvec)
break;
sclp_get_input((void *)(subvec + 1),
(void *) subvec + subvec->length);
(void *) subvec += subvec->length;
}
}
static inline void
sclp_eval_textcmd(struct gds_subvector *start,
struct gds_subvector *end)
{
struct gds_subvector *subvec;
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end,
GDS_KEY_SelfDefTextMsg);
if (!subvec)
break;
sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
(void *)subvec + subvec->length);
(void *) subvec += subvec->length;
}
}
static inline void
sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
{
struct gds_vector *vec;
vec = start;
while (vec < end) {
vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
if (!vec)
break;
sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
(void *) vec + vec->length);
(void *) vec += vec->length;
}
}
static inline void
sclp_eval_mdsmu(struct gds_vector *start, void *end)
{
struct gds_vector *vec;
vec = find_gds_vector(start, end, GDS_ID_CPMSU);
if (vec)
sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length);
}
static void
sclp_tty_receiver(struct evbuf_header *evbuf)
{
struct gds_vector *start, *end, *vec;
start = (struct gds_vector *)(evbuf + 1);
end = (void *) evbuf + evbuf->length;
vec = find_gds_vector(start, end, GDS_ID_MDSMU);
if (vec)
sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length);
}
static void
sclp_tty_state_change(struct sclp_register *reg)
{
}
struct sclp_register sclp_input_event =
{
.receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
.state_change_fn = sclp_tty_state_change,
.receiver_fn = sclp_tty_receiver
};
void
sclp_tty_init(void)
{
void *page;
int i;
if (!CONSOLE_IS_SCLP)
return;
if (sclp_rw_init() != 0)
return;
/* Allocate pages for output buffering */
INIT_LIST_HEAD(&sclp_tty_pages);
for (i = 0; i < MAX_KMEM_PAGES; i++) {
page = (void *) get_zeroed_page(GFP_KERNEL);
if (page == NULL)
return;
list_add_tail((struct list_head *) page, &sclp_tty_pages);
}
INIT_LIST_HEAD(&sclp_tty_outqueue);
spin_lock_init(&sclp_tty_lock);
init_waitqueue_head(&sclp_tty_waitq);
init_timer(&sclp_tty_timer);
sclp_ttybuf = NULL;
sclp_tty_buffer_count = 0;
if (MACHINE_IS_VM) {
/*
* save 4 characters for the CPU number
* written at start of each line by VM/CP
*/
sclp_ioctls_init.columns = 76;
/* case input lines to lowercase */
sclp_ioctls_init.tolower = 1;
}
sclp_ioctls = sclp_ioctls_init;
sclp_tty_chars_count = 0;
sclp_tty = NULL;
if (sclp_register(&sclp_input_event) != 0)
return;
memset (&sclp_tty_driver, 0, sizeof(struct tty_driver));
sclp_tty_driver.magic = TTY_DRIVER_MAGIC;
sclp_tty_driver.driver_name = "tty_sclp";
sclp_tty_driver.name = "ttyS";
sclp_tty_driver.name_base = 0;
sclp_tty_driver.major = TTY_MAJOR;
sclp_tty_driver.minor_start = 64;
sclp_tty_driver.num = 1;
sclp_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
sclp_tty_driver.subtype = SYSTEM_TYPE_TTY;
sclp_tty_driver.init_termios = tty_std_termios;
sclp_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
sclp_tty_driver.init_termios.c_oflag = ONLCR | XTABS;
sclp_tty_driver.init_termios.c_lflag = ISIG | ECHO;
sclp_tty_driver.flags = TTY_DRIVER_REAL_RAW;
sclp_tty_driver.refcount = &sclp_tty_refcount;
/* sclp_tty_driver.proc_entry ? */
sclp_tty_driver.table = sclp_tty_table;
sclp_tty_driver.termios = sclp_tty_termios;
sclp_tty_driver.termios_locked = sclp_tty_termios_locked;
sclp_tty_driver.open = sclp_tty_open;
sclp_tty_driver.close = sclp_tty_close;
sclp_tty_driver.write = sclp_tty_write;
sclp_tty_driver.put_char = sclp_tty_put_char;
sclp_tty_driver.flush_chars = sclp_tty_flush_chars;
sclp_tty_driver.write_room = sclp_tty_write_room;
sclp_tty_driver.chars_in_buffer = sclp_tty_chars_in_buffer;
sclp_tty_driver.flush_buffer = sclp_tty_flush_buffer;
sclp_tty_driver.ioctl = sclp_tty_ioctl;
/*
* No need for these function because they would be only called when
* the line discipline is close to full. That means that there must be
* collected nearly 4kB of input data. I suppose it is very difficult
* for the operator to enter lines quickly enough to let overrun the
* line discipline. Besides the n_tty line discipline does not try to
* call such functions if the pointers are set to NULL. Finally I have
* no idea what to do within these function. I can not prevent the
* operator and the SCLP to deliver input. Because of the reasons
* above it seems not worth to implement a buffer mechanism.
*/
sclp_tty_driver.throttle = NULL;
sclp_tty_driver.unthrottle = NULL;
sclp_tty_driver.send_xchar = NULL;
sclp_tty_driver.set_termios = NULL;
sclp_tty_driver.set_ldisc = NULL;
sclp_tty_driver.stop = NULL;
sclp_tty_driver.start = NULL;
sclp_tty_driver.hangup = NULL;
sclp_tty_driver.break_ctl = NULL;
sclp_tty_driver.wait_until_sent = NULL;
sclp_tty_driver.read_proc = NULL;
sclp_tty_driver.write_proc = NULL;
if (tty_register_driver(&sclp_tty_driver))
panic("Couldn't register sclp_tty driver\n");
}
/*
* drivers/s390/char/sclp_tty.h
* interface to the SCLP-read/write driver
*
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#ifndef __SCLP_TTY_H__
#define __SCLP_TTY_H__
#include <linux/ioctl.h>
/* This is the type of data structures storing sclp ioctl setting. */
struct sclp_ioctls {
unsigned short htab;
unsigned char echo;
unsigned short columns;
signed char final_nl;
unsigned short max_sccb;
unsigned short kmem_sccb; /* cant be modified at run time */
unsigned char tolower;
unsigned char delim;
};
/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */
#define SCLP_IOCTL_LETTER 'B'
/* set width of horizontal tabulator */
#define TIOCSCLPSHTAB _IOW(SCLP_IOCTL_LETTER, 0, unsigned short)
/* enable/disable echo of input (independent from line discipline) */
#define TIOCSCLPSECHO _IOW(SCLP_IOCTL_LETTER, 1, unsigned char)
/* set number of colums for output */
#define TIOCSCLPSCOLS _IOW(SCLP_IOCTL_LETTER, 2, unsigned short)
/* enable/disable writing without final new line character */
#define TIOCSCLPSNL _IOW(SCLP_IOCTL_LETTER, 4, signed char)
/* set the maximum buffers size for output, rounded up to next 4kB boundary */
#define TIOCSCLPSOBUF _IOW(SCLP_IOCTL_LETTER, 5, unsigned short)
/* set initial (default) sclp ioctls */
#define TIOCSCLPSINIT _IO(SCLP_IOCTL_LETTER, 6)
/* enable/disable conversion from upper to lower case of input */
#define TIOCSCLPSCASE _IOW(SCLP_IOCTL_LETTER, 7, unsigned char)
/* set special character used for seperating upper and lower case, */
/* 0x00 disables this feature */
#define TIOCSCLPSDELIM _IOW(SCLP_IOCTL_LETTER, 9, unsigned char)
/* get width of horizontal tabulator */
#define TIOCSCLPGHTAB _IOR(SCLP_IOCTL_LETTER, 10, unsigned short)
/* Is echo of input enabled ? (independent from line discipline) */
#define TIOCSCLPGECHO _IOR(SCLP_IOCTL_LETTER, 11, unsigned char)
/* get number of colums for output */
#define TIOCSCLPGCOLS _IOR(SCLP_IOCTL_LETTER, 12, unsigned short)
/* Is writing without final new line character enabled ? */
#define TIOCSCLPGNL _IOR(SCLP_IOCTL_LETTER, 14, signed char)
/* get the maximum buffers size for output */
#define TIOCSCLPGOBUF _IOR(SCLP_IOCTL_LETTER, 15, unsigned short)
/* Is conversion from upper to lower case of input enabled ? */
#define TIOCSCLPGCASE _IOR(SCLP_IOCTL_LETTER, 17, unsigned char)
/* get special character used for seperating upper and lower case, */
/* 0x00 disables this feature */
#define TIOCSCLPGDELIM _IOR(SCLP_IOCTL_LETTER, 19, unsigned char)
/* get the number of buffers/pages got from kernel at startup */
#define TIOCSCLPGKBUF _IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
#endif /* __SCLP_TTY_H__ */
......@@ -9,6 +9,8 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <linux/string.h>
/* Note:
* struct __debug_entry must be defined outside of #ifdef __KERNEL__
* in order to allow a user program to analyze the 'raw'-view.
......
......@@ -32,7 +32,7 @@ void codepage_convert(const __u8 *codepage, volatile __u8 * addr, int nr)
"0: tr 0(256,%0),0(%2)\n"
" la %0,256(%0)\n"
"1: ahi %1,-256\n"
" jp 0b\n"
" jnm 0b\n"
" ex %1,0(1)"
: "+&a" (addr), "+&a" (nr)
: "a" (codepage) : "cc", "memory", "1" );
......
......@@ -136,7 +136,7 @@ struct _lowcore
/* SMP info area: defined by DJB */
__u64 jiffy_timer; /* 0xc80 */
atomic_t ext_call_fast; /* 0xc88 */
__u32 ext_call_fast; /* 0xc88 */
__u8 pad11[0xe00-0xc8c]; /* 0xc8c */
/* 0xe00 is used as indicator for dump tools */
......
......@@ -26,6 +26,8 @@
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
#define MAP_LOCKED 0x2000 /* pages are locked */
#define MAP_NORESERVE 0x4000 /* don't check for reservations */
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MS_ASYNC 1 /* sync memory asynchronously */
#define MS_INVALIDATE 2 /* invalidate the caches */
......
......@@ -4,9 +4,22 @@
* This file contains the s390 architecture specific module code.
*/
#define module_map(x) vmalloc(x)
#define module_unmap(x) vfree(x)
#define module_arch_init(x) (0)
#define arch_init_modules(x) do { } while (0)
struct mod_arch_specific
{
void *module_got, *module_plt;
unsigned long got_size, plt_size;
};
#ifdef CONFIG_ARCH_S390X
#define ElfW(x) Elf64_ ## x
#define ELFW(x) ELF64_ ## x
#else
#define ElfW(x) Elf32_ ## x
#define ELFW(x) ELF32_ ## x
#endif
#define Elf_Shdr ElfW(Shdr)
#define Elf_Sym ElfW(Sym)
#define Elf_Ehdr ElfW(Ehdr)
#define ELF_R_TYPE ELFW(R_TYPE)
#endif /* _ASM_S390_MODULE_H */
......@@ -320,7 +320,7 @@ extern inline pte_t pte_wrprotect(pte_t pte)
extern inline pte_t pte_mkwrite(pte_t pte)
{
pte_val(pte) &= ~_PAGE_RO;
pte_val(pte) &= ~(_PAGE_RO | _PAGE_ISCLEAN);
return pte;
}
......
......@@ -23,6 +23,7 @@
#define POLLWRNORM 0x0100
#define POLLWRBAND 0x0200
#define POLLMSG 0x0400
#define POLLREMOVE 0x1000
struct pollfd {
int fd;
......
......@@ -31,7 +31,7 @@ extern unsigned long machine_flags;
#define MACHINE_HAS_CSP (machine_flags & 8)
#define MACHINE_HAS_MVPG (machine_flags & 16)
#define MACHINE_HAS_HWC (!MACHINE_IS_P390)
#define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
/*
* Console mode. Override with conmode=
......@@ -40,10 +40,10 @@ extern unsigned int console_mode;
extern unsigned int console_device;
#define CONSOLE_IS_UNDEFINED (console_mode == 0)
#define CONSOLE_IS_HWC (console_mode == 1)
#define CONSOLE_IS_SCLP (console_mode == 1)
#define CONSOLE_IS_3215 (console_mode == 2)
#define CONSOLE_IS_3270 (console_mode == 3)
#define SET_CONSOLE_HWC do { console_mode = 1; } while (0)
#define SET_CONSOLE_SCLP do { console_mode = 1; } while (0)
#define SET_CONSOLE_3215 do { console_mode = 2; } while (0)
#define SET_CONSOLE_3270 do { console_mode = 3; } while (0)
......
......@@ -29,7 +29,7 @@ typedef struct
} sigp_info;
extern volatile unsigned long cpu_online_map;
extern unsigned long cpu_possible_map;
extern volatile unsigned long cpu_possible_map;
#define NO_PROC_ID 0xFF /* No processor magic marker */
......
......@@ -91,13 +91,15 @@ static inline void global_flush_tlb(void)
static inline void __flush_tlb_mm(struct mm_struct * mm)
{
if (((atomic_read(&mm->mm_count) != 1) ||
(mm->cpu_vm_mask != (1UL << smp_processor_id())))) {
mm->cpu_vm_mask = (1UL << smp_processor_id());
if (mm->cpu_vm_mask != (1UL << smp_processor_id())) {
/* mm was active on more than one cpu. */
if (mm == current->active_mm &&
atomic_read(&mm->mm_count) == 1)
/* this cpu is the only one using the mm. */
mm->cpu_vm_mask = 1UL << smp_processor_id();
global_flush_tlb();
} else {
} else
local_flush_tlb();
}
}
static inline void flush_tlb(void)
......
......@@ -63,5 +63,10 @@ typedef union {
} subreg;
} register_pair;
#ifdef CONFIG_LBD
typedef u64 sector_t;
#define HAVE_SECTOR_T
#endif
#endif /* __KERNEL__ */
#endif
......@@ -199,24 +199,23 @@ extern inline int __put_user_asm_1(__u8 x, void *ptr)
*/
#define __put_user(x, ptr) \
({ \
__typeof__(*(ptr)) *__pu_addr = (ptr); \
__typeof__(*(ptr)) __x = (x); \
int __pu_err; \
switch (sizeof (*(ptr))) { \
case 1: \
__pu_err = __put_user_asm_1((__u8)(__u32) __x, \
__pu_addr); \
ptr); \
break; \
case 2: \
__pu_err = __put_user_asm_2((__u16)(__u32) __x, \
__pu_addr); \
ptr); \
break; \
case 4: \
__pu_err = __put_user_asm_4((__u32) __x, \
__pu_addr); \
ptr); \
break; \
case 8: \
__pu_err = __put_user_asm_8(&__x, __pu_addr); \
__pu_err = __put_user_asm_8(&__x, ptr); \
break; \
default: \
__pu_err = __put_user_bad(); \
......@@ -333,21 +332,20 @@ extern int __put_user_bad(void);
#define __get_user(x, ptr) \
({ \
__typeof__(ptr) __gu_addr = (ptr); \
__typeof__(*(ptr)) __x; \
int __gu_err; \
switch (sizeof(*(__gu_addr))) { \
switch (sizeof(*(ptr))) { \
case 1: \
__get_user_asm_1(__x, __gu_addr, __gu_err); \
__get_user_asm_1(__x, ptr, __gu_err); \
break; \
case 2: \
__get_user_asm_2(__x, __gu_addr, __gu_err); \
__get_user_asm_2(__x, ptr, __gu_err); \
break; \
case 4: \
__get_user_asm_4(__x, __gu_addr, __gu_err); \
__get_user_asm_4(__x, ptr, __gu_err); \
break; \
case 8: \
__get_user_asm_8(__x, __gu_addr, __gu_err); \
__get_user_asm_8(__x, ptr, __gu_err); \
break; \
default: \
__x = 0; \
......
......@@ -241,6 +241,10 @@
#define __NR_io_submit 246
#define __NR_io_cancel 247
#define __NR_exit_group 248
#define __NR_epoll_create 249
#define __NR_epoll_ctl 250
#define __NR_epoll_wait 251
#define __NR_set_tid_address 252
/* user-visible error numbers are in the range -1 - -122: see <asm-s390/errno.h> */
......@@ -254,11 +258,11 @@ do { \
return (type) (res); \
} while (0)
#define _svc_clobber "2", "cc", "memory"
#define _svc_clobber "cc", "memory"
#define _syscall0(type,name) \
type name(void) { \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
......@@ -271,13 +275,13 @@ type name(void) { \
#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) { \
register type1 __arg1 asm("2") = arg1; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1) \
"0" (__arg1) \
: _svc_clobber ); \
__syscall_return(type,__res); \
}
......@@ -286,13 +290,13 @@ type name(type1 arg1) { \
type name(type1 arg1, type2 arg2) { \
register type1 __arg1 asm("2") = arg1; \
register type2 __arg2 asm("3") = arg2; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2) \
: _svc_clobber ); \
__syscall_return(type,__res); \
......@@ -303,13 +307,13 @@ type name(type1 arg1, type2 arg2, type3 arg3) { \
register type1 __arg1 asm("2") = arg1; \
register type2 __arg2 asm("3") = arg2; \
register type3 __arg3 asm("4") = arg3; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3) \
: _svc_clobber ); \
......@@ -323,13 +327,13 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
register type2 __arg2 asm("3") = arg2; \
register type3 __arg3 asm("4") = arg3; \
register type4 __arg4 asm("5") = arg4; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3), \
"d" (__arg4) \
......@@ -346,13 +350,13 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
register type3 __arg3 asm("4") = arg3; \
register type4 __arg4 asm("5") = arg4; \
register type5 __arg5 asm("6") = arg5; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3), \
"d" (__arg4), \
......
......@@ -9,6 +9,8 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <linux/string.h>
/* Note:
* struct __debug_entry must be defined outside of #ifdef __KERNEL__
* in order to allow a user program to analyze the 'raw'-view.
......
......@@ -32,7 +32,7 @@ codepage_convert(const __u8 *codepage, volatile __u8 * addr, unsigned long nr)
"0: tr 0(256,%0),0(%2)\n"
" la %0,256(%0)\n"
"1: ahi %1,-256\n"
" jp 0b\n"
" jnm 0b\n"
" ex %1,0(1)"
: "+&a" (addr), "+&a" (nr)
: "a" (codepage) : "cc", "memory", "1" );
......
......@@ -26,6 +26,8 @@
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
#define MAP_LOCKED 0x2000 /* pages are locked */
#define MAP_NORESERVE 0x4000 /* don't check for reservations */
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MS_ASYNC 1 /* sync memory asynchronously */
#define MS_INVALIDATE 2 /* invalidate the caches */
......
......@@ -4,9 +4,22 @@
* This file contains the s390 architecture specific module code.
*/
#define module_map(x) vmalloc(x)
#define module_unmap(x) vfree(x)
#define module_arch_init(x) (0)
#define arch_init_modules(x) do { } while (0)
struct mod_arch_specific
{
void *module_got, *module_plt;
unsigned long got_size, plt_size;
};
#ifdef CONFIG_ARCH_S390X
#define ElfW(x) Elf64_ ## x
#define ELFW(x) ELF64_ ## x
#else
#define ElfW(x) Elf32_ ## x
#define ELFW(x) ELF64_ ## x
#endif
#define Elf_Shdr ElfW(Shdr)
#define Elf_Sym ElfW(Sym)
#define Elf_Ehdr ElfW(Ehdr)
#define ELF_R_TYPE ELFW(R_TYPE)
#endif /* _ASM_S390_MODULE_H */
......@@ -339,7 +339,7 @@ extern inline pte_t pte_wrprotect(pte_t pte)
extern inline pte_t pte_mkwrite(pte_t pte)
{
pte_val(pte) &= ~_PAGE_RO;
pte_val(pte) &= ~(_PAGE_RO | _PAGE_ISCLEAN);
return pte;
}
......
......@@ -23,6 +23,7 @@
#define POLLWRNORM 0x0100
#define POLLWRBAND 0x0200
#define POLLMSG 0x0400
#define POLLREMOVE 0x1000
struct pollfd {
int fd;
......
......@@ -30,7 +30,7 @@ extern unsigned long machine_flags;
#define MACHINE_HAS_MVPG (machine_flags & 16)
#define MACHINE_HAS_DIAG44 (machine_flags & 32)
#define MACHINE_HAS_HWC (!MACHINE_IS_P390)
#define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
/*
* Console mode. Override with conmode=
......@@ -39,10 +39,10 @@ extern unsigned int console_mode;
extern unsigned int console_device;
#define CONSOLE_IS_UNDEFINED (console_mode == 0)
#define CONSOLE_IS_HWC (console_mode == 1)
#define CONSOLE_IS_SCLP (console_mode == 1)
#define CONSOLE_IS_3215 (console_mode == 2)
#define CONSOLE_IS_3270 (console_mode == 3)
#define SET_CONSOLE_HWC do { console_mode = 1; } while (0)
#define SET_CONSOLE_SCLP do { console_mode = 1; } while (0)
#define SET_CONSOLE_3215 do { console_mode = 2; } while (0)
#define SET_CONSOLE_3270 do { console_mode = 3; } while (0)
......
......@@ -88,13 +88,15 @@ static inline void global_flush_tlb(void)
static inline void __flush_tlb_mm(struct mm_struct * mm)
{
if (((atomic_read(&mm->mm_count) != 1) ||
(mm->cpu_vm_mask != (1UL << smp_processor_id())))) {
mm->cpu_vm_mask = (1UL << smp_processor_id());
if (mm->cpu_vm_mask != (1UL << smp_processor_id())) {
/* mm was active on more than one cpu. */
if (mm == current->active_mm &&
atomic_read(&mm->mm_count) == 1)
/* this cpu is the only one using the mm. */
mm->cpu_vm_mask = 1UL << smp_processor_id();
global_flush_tlb();
} else {
} else
local_flush_tlb();
}
}
static inline void flush_tlb(void)
......
......@@ -177,25 +177,24 @@ extern inline int __put_user_asm_1(__u8 x, void *ptr)
*/
#define __put_user(x, ptr) \
({ \
__typeof__(*(ptr)) *__pu_addr = (ptr); \
__typeof__(*(ptr)) __x = (x); \
int __pu_err; \
switch (sizeof (*(__pu_addr))) { \
switch (sizeof (*(ptr))) { \
case 1: \
__pu_err = __put_user_asm_1((__u8)(__u64)(__x), \
__pu_addr); \
ptr); \
break; \
case 2: \
__pu_err = __put_user_asm_2((__u16)(__u64)(__x),\
__pu_addr); \
ptr); \
break; \
case 4: \
__pu_err = __put_user_asm_4((__u32)(__u64)(__x),\
__pu_addr); \
ptr); \
break; \
case 8: \
__pu_err = __put_user_asm_8((__u64)(__x), \
__pu_addr); \
ptr); \
break; \
default: \
__pu_err = __put_user_bad(); \
......@@ -290,21 +289,20 @@ extern int __put_user_bad(void);
#define __get_user(x, ptr) \
({ \
__typeof__(ptr) __gu_addr = (ptr); \
__typeof__(*(ptr)) __x; \
int __gu_err; \
switch (sizeof(*(ptr))) { \
case 1: \
__get_user_asm_1(__x,__gu_addr,__gu_err); \
__get_user_asm_1(__x,ptr,__gu_err); \
break; \
case 2: \
__get_user_asm_2(__x,__gu_addr,__gu_err); \
__get_user_asm_2(__x,ptr,__gu_err); \
break; \
case 4: \
__get_user_asm_4(__x,__gu_addr,__gu_err); \
__get_user_asm_4(__x,ptr,__gu_err); \
break; \
case 8: \
__get_user_asm_8(__x,__gu_addr,__gu_err); \
__get_user_asm_8(__x,ptr,__gu_err); \
break; \
default: \
__x = 0; \
......
......@@ -208,6 +208,10 @@
#define __NR_io_submit 246
#define __NR_io_cancel 247
#define __NR_exit_group 248
#define __NR_epoll_create 249
#define __NR_epoll_ctl 250
#define __NR_epoll_wait 251
#define __NR_set_tid_address 252
/* user-visible error numbers are in the range -1 - -122: see <asm-s390/errno.h> */
......@@ -221,11 +225,11 @@ do { \
return (type) (res); \
} while (0)
#define _svc_clobber "2", "cc", "memory"
#define _svc_clobber "cc", "memory"
#define _syscall0(type,name) \
type name(void) { \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
......@@ -238,13 +242,13 @@ type name(void) { \
#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) { \
register type1 __arg1 asm("2") = arg1; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1) \
"0" (__arg1) \
: _svc_clobber ); \
__syscall_return(type,__res); \
}
......@@ -253,13 +257,13 @@ type name(type1 arg1) { \
type name(type1 arg1, type2 arg2) { \
register type1 __arg1 asm("2") = arg1; \
register type2 __arg2 asm("3") = arg2; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2) \
: _svc_clobber ); \
__syscall_return(type,__res); \
......@@ -270,13 +274,13 @@ type name(type1 arg1, type2 arg2, type3 arg3) { \
register type1 __arg1 asm("2") = arg1; \
register type2 __arg2 asm("3") = arg2; \
register type3 __arg3 asm("4") = arg3; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3) \
: _svc_clobber ); \
......@@ -290,13 +294,13 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
register type2 __arg2 asm("3") = arg2; \
register type3 __arg3 asm("4") = arg3; \
register type4 __arg4 asm("5") = arg4; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3), \
"d" (__arg4) \
......@@ -313,13 +317,13 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
register type3 __arg3 asm("4") = arg3; \
register type4 __arg4 asm("5") = arg4; \
register type5 __arg5 asm("6") = arg5; \
long __res; \
register long __res asm("2"); \
__asm__ __volatile__ ( \
" svc %b1\n" \
" lgr %0,2" \
: "=d" (__res) \
: "i" (__NR_##name), \
"d" (__arg1), \
"0" (__arg1), \
"d" (__arg2), \
"d" (__arg3), \
"d" (__arg4), \
......
......@@ -413,6 +413,37 @@ typedef struct {
/* Keep this the last entry. */
#define R_PPC_NUM 37
/* s390 relocations defined by the ABIs */
#define R_390_NONE 0 /* No reloc. */
#define R_390_8 1 /* Direct 8 bit. */
#define R_390_12 2 /* Direct 12 bit. */
#define R_390_16 3 /* Direct 16 bit. */
#define R_390_32 4 /* Direct 32 bit. */
#define R_390_PC32 5 /* PC relative 32 bit. */
#define R_390_GOT12 6 /* 12 bit GOT offset. */
#define R_390_GOT32 7 /* 32 bit GOT offset. */
#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
#define R_390_COPY 9 /* Copy symbol at runtime. */
#define R_390_GLOB_DAT 10 /* Create GOT entry. */
#define R_390_JMP_SLOT 11 /* Create PLT entry. */
#define R_390_RELATIVE 12 /* Adjust by program base. */
#define R_390_GOTOFF 13 /* 32 bit offset to GOT. */
#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
#define R_390_GOT16 15 /* 16 bit GOT offset. */
#define R_390_PC16 16 /* PC relative 16 bit. */
#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
#define R_390_64 22 /* Direct 64 bit. */
#define R_390_PC64 23 /* PC relative 64 bit. */
#define R_390_GOT64 24 /* 64 bit GOT offset. */
#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
/* Keep this the last entry. */
#define R_390_NUM 27
/* Legal values for e_flags field of Elf64_Ehdr. */
#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
......
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