提交 c7708fac 编写于 作者: L Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 update from Martin Schwidefsky:
 "Add support to generate code for the latest machine zEC12, MOD and XOR
  instruction support for the BPF jit compiler, the dasd safe offline
  feature and the big one: the s390 architecture gets PCI support!!
  Right before the world ends on the 21st ;-)"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (41 commits)
  s390/qdio: rename the misleading PCI flag of qdio devices
  s390/pci: remove obsolete email addresses
  s390/pci: speed up __iowrite64_copy by using pci store block insn
  s390/pci: enable NEED_DMA_MAP_STATE
  s390/pci: no msleep in potential IRQ context
  s390/pci: fix potential NULL pointer dereference in dma_free_seg_table()
  s390/pci: use kmem_cache_zalloc instead of kmem_cache_alloc/memset
  s390/bpf,jit: add support for XOR instruction
  s390/bpf,jit: add support MOD instruction
  s390/cio: fix pgid reserved check
  vga: compile fix, disable vga for s390
  s390/pci: add PCI Kconfig options
  s390/pci: s390 specific PCI sysfs attributes
  s390/pci: PCI hotplug support via SCLP
  s390/pci: CHSC PCI support for error and availability events
  s390/pci: DMA support
  s390/pci: PCI adapter interrupts for MSI/MSI-X
  s390/bitops: find leftmost bit instruction support
  s390/pci: CLP interface
  s390/pci: base support
  ...
......@@ -6,3 +6,4 @@ obj-$(CONFIG_S390_HYPFS_FS) += hypfs/
obj-$(CONFIG_APPLDATA_BASE) += appldata/
obj-$(CONFIG_MATHEMU) += math-emu/
obj-y += net/
obj-$(CONFIG_PCI) += pci/
......@@ -34,12 +34,6 @@ config GENERIC_BUG
config GENERIC_BUG_RELATIVE_POINTERS
def_bool y
config NO_IOMEM
def_bool y
config NO_DMA
def_bool y
config ARCH_DMA_ADDR_T_64BIT
def_bool 64BIT
......@@ -58,6 +52,12 @@ config KEXEC
config AUDIT_ARCH
def_bool y
config NO_IOPORT
def_bool y
config PCI_QUIRKS
def_bool n
config S390
def_bool y
select USE_GENERIC_SMP_HELPERS if SMP
......@@ -171,6 +171,10 @@ config HAVE_MARCH_Z196_FEATURES
def_bool n
select HAVE_MARCH_Z10_FEATURES
config HAVE_MARCH_ZEC12_FEATURES
def_bool n
select HAVE_MARCH_Z196_FEATURES
choice
prompt "Processor type"
default MARCH_G5
......@@ -222,6 +226,13 @@ config MARCH_Z196
(2818 and 2817 series). The kernel will be slightly faster but will
not work on older machines.
config MARCH_ZEC12
bool "IBM zEC12"
select HAVE_MARCH_ZEC12_FEATURES if 64BIT
help
Select this to enable optimizations for IBM zEC12 (2827 series). The
kernel will be slightly faster but will not work on older machines.
endchoice
config 64BIT
......@@ -426,6 +437,53 @@ config QDIO
If unsure, say Y.
menuconfig PCI
bool "PCI support"
default n
depends on 64BIT
select ARCH_SUPPORTS_MSI
select PCI_MSI
help
Enable PCI support.
if PCI
config PCI_NR_FUNCTIONS
int "Maximum number of PCI functions (1-4096)"
range 1 4096
default "64"
help
This allows you to specify the maximum number of PCI functions which
this kernel will support.
source "drivers/pci/Kconfig"
source "drivers/pci/pcie/Kconfig"
source "drivers/pci/hotplug/Kconfig"
endif # PCI
config PCI_DOMAINS
def_bool PCI
config HAS_IOMEM
def_bool PCI
config IOMMU_HELPER
def_bool PCI
config HAS_DMA
def_bool PCI
select HAVE_DMA_API_DEBUG
config NEED_SG_DMA_LENGTH
def_bool PCI
config HAVE_DMA_ATTRS
def_bool PCI
config NEED_DMA_MAP_STATE
def_bool PCI
config CHSC_SCH
def_tristate m
prompt "Support for CHSC subchannels"
......
......@@ -41,6 +41,7 @@ cflags-$(CONFIG_MARCH_Z990) += $(call cc-option,-march=z990)
cflags-$(CONFIG_MARCH_Z9_109) += $(call cc-option,-march=z9-109)
cflags-$(CONFIG_MARCH_Z10) += $(call cc-option,-march=z10)
cflags-$(CONFIG_MARCH_Z196) += $(call cc-option,-march=z196)
cflags-$(CONFIG_MARCH_ZEC12) += $(call cc-option,-march=zEC12)
#KBUILD_IMAGE is necessary for make rpm
KBUILD_IMAGE :=arch/s390/boot/image
......
......@@ -325,7 +325,8 @@ static int ecb_aes_crypt(struct blkcipher_desc *desc, long func, void *param,
u8 *in = walk->src.virt.addr;
ret = crypt_s390_km(func, param, out, in, n);
BUG_ON((ret < 0) || (ret != n));
if (ret < 0 || ret != n)
return -EIO;
nbytes &= AES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, walk, nbytes);
......@@ -457,7 +458,8 @@ static int cbc_aes_crypt(struct blkcipher_desc *desc, long func, void *param,
u8 *in = walk->src.virt.addr;
ret = crypt_s390_kmc(func, param, out, in, n);
BUG_ON((ret < 0) || (ret != n));
if (ret < 0 || ret != n)
return -EIO;
nbytes &= AES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, walk, nbytes);
......@@ -625,7 +627,8 @@ static int xts_aes_crypt(struct blkcipher_desc *desc, long func,
memcpy(xts_ctx->pcc.tweak, walk->iv, sizeof(xts_ctx->pcc.tweak));
param = xts_ctx->pcc.key + offset;
ret = crypt_s390_pcc(func, param);
BUG_ON(ret < 0);
if (ret < 0)
return -EIO;
memcpy(xts_ctx->xts_param, xts_ctx->pcc.xts, 16);
param = xts_ctx->key + offset;
......@@ -636,7 +639,8 @@ static int xts_aes_crypt(struct blkcipher_desc *desc, long func,
in = walk->src.virt.addr;
ret = crypt_s390_km(func, param, out, in, n);
BUG_ON(ret < 0 || ret != n);
if (ret < 0 || ret != n)
return -EIO;
nbytes &= AES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, walk, nbytes);
......@@ -769,7 +773,8 @@ static int ctr_aes_crypt(struct blkcipher_desc *desc, long func,
crypto_inc(ctrblk + i, AES_BLOCK_SIZE);
}
ret = crypt_s390_kmctr(func, sctx->key, out, in, n, ctrblk);
BUG_ON(ret < 0 || ret != n);
if (ret < 0 || ret != n)
return -EIO;
if (n > AES_BLOCK_SIZE)
memcpy(ctrblk, ctrblk + n - AES_BLOCK_SIZE,
AES_BLOCK_SIZE);
......@@ -788,7 +793,8 @@ static int ctr_aes_crypt(struct blkcipher_desc *desc, long func,
in = walk->src.virt.addr;
ret = crypt_s390_kmctr(func, sctx->key, buf, in,
AES_BLOCK_SIZE, ctrblk);
BUG_ON(ret < 0 || ret != AES_BLOCK_SIZE);
if (ret < 0 || ret != AES_BLOCK_SIZE)
return -EIO;
memcpy(out, buf, nbytes);
crypto_inc(ctrblk, AES_BLOCK_SIZE);
ret = blkcipher_walk_done(desc, walk, 0);
......
......@@ -94,7 +94,8 @@ static int ecb_desall_crypt(struct blkcipher_desc *desc, long func,
u8 *in = walk->src.virt.addr;
ret = crypt_s390_km(func, key, out, in, n);
BUG_ON((ret < 0) || (ret != n));
if (ret < 0 || ret != n)
return -EIO;
nbytes &= DES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, walk, nbytes);
......@@ -120,7 +121,8 @@ static int cbc_desall_crypt(struct blkcipher_desc *desc, long func,
u8 *in = walk->src.virt.addr;
ret = crypt_s390_kmc(func, iv, out, in, n);
BUG_ON((ret < 0) || (ret != n));
if (ret < 0 || ret != n)
return -EIO;
nbytes &= DES_BLOCK_SIZE - 1;
ret = blkcipher_walk_done(desc, walk, nbytes);
......@@ -386,7 +388,8 @@ static int ctr_desall_crypt(struct blkcipher_desc *desc, long func,
crypto_inc(ctrblk + i, DES_BLOCK_SIZE);
}
ret = crypt_s390_kmctr(func, ctx->key, out, in, n, ctrblk);
BUG_ON((ret < 0) || (ret != n));
if (ret < 0 || ret != n)
return -EIO;
if (n > DES_BLOCK_SIZE)
memcpy(ctrblk, ctrblk + n - DES_BLOCK_SIZE,
DES_BLOCK_SIZE);
......@@ -404,7 +407,8 @@ static int ctr_desall_crypt(struct blkcipher_desc *desc, long func,
in = walk->src.virt.addr;
ret = crypt_s390_kmctr(func, ctx->key, buf, in,
DES_BLOCK_SIZE, ctrblk);
BUG_ON(ret < 0 || ret != DES_BLOCK_SIZE);
if (ret < 0 || ret != DES_BLOCK_SIZE)
return -EIO;
memcpy(out, buf, nbytes);
crypto_inc(ctrblk, DES_BLOCK_SIZE);
ret = blkcipher_walk_done(desc, walk, 0);
......
......@@ -72,14 +72,16 @@ static int ghash_update(struct shash_desc *desc,
if (!dctx->bytes) {
ret = crypt_s390_kimd(KIMD_GHASH, ctx, buf,
GHASH_BLOCK_SIZE);
BUG_ON(ret != GHASH_BLOCK_SIZE);
if (ret != GHASH_BLOCK_SIZE)
return -EIO;
}
}
n = srclen & ~(GHASH_BLOCK_SIZE - 1);
if (n) {
ret = crypt_s390_kimd(KIMD_GHASH, ctx, src, n);
BUG_ON(ret != n);
if (ret != n)
return -EIO;
src += n;
srclen -= n;
}
......@@ -92,7 +94,7 @@ static int ghash_update(struct shash_desc *desc,
return 0;
}
static void ghash_flush(struct ghash_ctx *ctx, struct ghash_desc_ctx *dctx)
static int ghash_flush(struct ghash_ctx *ctx, struct ghash_desc_ctx *dctx)
{
u8 *buf = dctx->buffer;
int ret;
......@@ -103,21 +105,24 @@ static void ghash_flush(struct ghash_ctx *ctx, struct ghash_desc_ctx *dctx)
memset(pos, 0, dctx->bytes);
ret = crypt_s390_kimd(KIMD_GHASH, ctx, buf, GHASH_BLOCK_SIZE);
BUG_ON(ret != GHASH_BLOCK_SIZE);
if (ret != GHASH_BLOCK_SIZE)
return -EIO;
}
dctx->bytes = 0;
return 0;
}
static int ghash_final(struct shash_desc *desc, u8 *dst)
{
struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm);
int ret;
ghash_flush(ctx, dctx);
memcpy(dst, ctx->icv, GHASH_BLOCK_SIZE);
return 0;
ret = ghash_flush(ctx, dctx);
if (!ret)
memcpy(dst, ctx->icv, GHASH_BLOCK_SIZE);
return ret;
}
static struct shash_alg ghash_alg = {
......
......@@ -36,7 +36,8 @@ int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len)
if (index) {
memcpy(ctx->buf + index, data, bsize - index);
ret = crypt_s390_kimd(ctx->func, ctx->state, ctx->buf, bsize);
BUG_ON(ret != bsize);
if (ret != bsize)
return -EIO;
data += bsize - index;
len -= bsize - index;
index = 0;
......@@ -46,7 +47,8 @@ int s390_sha_update(struct shash_desc *desc, const u8 *data, unsigned int len)
if (len >= bsize) {
ret = crypt_s390_kimd(ctx->func, ctx->state, data,
len & ~(bsize - 1));
BUG_ON(ret != (len & ~(bsize - 1)));
if (ret != (len & ~(bsize - 1)))
return -EIO;
data += ret;
len -= ret;
}
......@@ -88,7 +90,8 @@ int s390_sha_final(struct shash_desc *desc, u8 *out)
memcpy(ctx->buf + end - 8, &bits, sizeof(bits));
ret = crypt_s390_kimd(ctx->func, ctx->state, ctx->buf, end);
BUG_ON(ret != end);
if (ret != end)
return -EIO;
/* copy digest to out */
memcpy(out, ctx->state, crypto_shash_digestsize(desc->tfm));
......
......@@ -640,6 +640,87 @@ static inline unsigned long find_first_bit(const unsigned long * addr,
}
#define find_first_bit find_first_bit
/*
* Big endian variant whichs starts bit counting from left using
* the flogr (find leftmost one) instruction.
*/
static inline unsigned long __flo_word(unsigned long nr, unsigned long val)
{
register unsigned long bit asm("2") = val;
register unsigned long out asm("3");
asm volatile (
" .insn rre,0xb9830000,%[bit],%[bit]\n"
: [bit] "+d" (bit), [out] "=d" (out) : : "cc");
return nr + bit;
}
/*
* 64 bit special left bitops format:
* order in memory:
* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
* 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
* 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
* 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
* after that follows the next long with bit numbers
* 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
* 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
* 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
* 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
* The reason for this bit ordering is the fact that
* the hardware sets bits in a bitmap starting at bit 0
* and we don't want to scan the bitmap from the 'wrong
* end'.
*/
static inline unsigned long find_first_bit_left(const unsigned long *addr,
unsigned long size)
{
unsigned long bytes, bits;
if (!size)
return 0;
bytes = __ffs_word_loop(addr, size);
bits = __flo_word(bytes * 8, __load_ulong_be(addr, bytes));
return (bits < size) ? bits : size;
}
static inline int find_next_bit_left(const unsigned long *addr,
unsigned long size,
unsigned long offset)
{
const unsigned long *p;
unsigned long bit, set;
if (offset >= size)
return size;
bit = offset & (__BITOPS_WORDSIZE - 1);
offset -= bit;
size -= offset;
p = addr + offset / __BITOPS_WORDSIZE;
if (bit) {
set = __flo_word(0, *p & (~0UL << bit));
if (set >= size)
return size + offset;
if (set < __BITOPS_WORDSIZE)
return set + offset;
offset += __BITOPS_WORDSIZE;
size -= __BITOPS_WORDSIZE;
p++;
}
return offset + find_first_bit_left(p, size);
}
#define for_each_set_bit_left(bit, addr, size) \
for ((bit) = find_first_bit_left((addr), (size)); \
(bit) < (size); \
(bit) = find_next_bit_left((addr), (size), (bit) + 1))
/* same as for_each_set_bit() but use bit as value to start with */
#define for_each_set_bit_left_cont(bit, addr, size) \
for ((bit) = find_next_bit_left((addr), (size), (bit)); \
(bit) < (size); \
(bit) = find_next_bit_left((addr), (size), (bit) + 1))
/**
* find_next_zero_bit - find the first zero bit in a memory region
* @addr: The address to base the search on
......
......@@ -18,6 +18,9 @@ struct irb;
struct ccw1;
struct ccw_dev_id;
/* from asm/schid.h */
struct subchannel_id;
/* simplified initializers for struct ccw_device:
* CCW_DEVICE and CCW_DEVICE_DEVTYPE initialize one
* entry in your MODULE_DEVICE_TABLE and set the match_flag correctly */
......@@ -223,8 +226,7 @@ extern int ccw_device_force_console(void);
int ccw_device_siosl(struct ccw_device *);
// FIXME: these have to go
extern int _ccw_device_get_subchannel_number(struct ccw_device *);
extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
extern void *ccw_device_get_chp_desc(struct ccw_device *, int);
#endif /* _S390_CCWDEV_H_ */
......@@ -59,6 +59,9 @@ extern void ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver);
int ccwgroup_create_dev(struct device *root, struct ccwgroup_driver *gdrv,
int num_devices, const char *buf);
extern int ccwgroup_set_online(struct ccwgroup_device *gdev);
extern int ccwgroup_set_offline(struct ccwgroup_device *gdev);
extern int ccwgroup_probe_ccwdev(struct ccw_device *cdev);
extern void ccwgroup_remove_ccwdev(struct ccw_device *cdev);
......
#ifndef _ASM_S390_CLP_H
#define _ASM_S390_CLP_H
/* CLP common request & response block size */
#define CLP_BLK_SIZE (PAGE_SIZE * 2)
struct clp_req_hdr {
u16 len;
u16 cmd;
} __packed;
struct clp_rsp_hdr {
u16 len;
u16 rsp;
} __packed;
/* CLP Response Codes */
#define CLP_RC_OK 0x0010 /* Command request successfully */
#define CLP_RC_CMD 0x0020 /* Command code not recognized */
#define CLP_RC_PERM 0x0030 /* Command not authorized */
#define CLP_RC_FMT 0x0040 /* Invalid command request format */
#define CLP_RC_LEN 0x0050 /* Invalid command request length */
#define CLP_RC_8K 0x0060 /* Command requires 8K LPCB */
#define CLP_RC_RESNOT0 0x0070 /* Reserved field not zero */
#define CLP_RC_NODATA 0x0080 /* No data available */
#define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */
#endif
#ifndef _ASM_S390_DMA_MAPPING_H
#define _ASM_S390_DMA_MAPPING_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/dma-attrs.h>
#include <linux/dma-debug.h>
#include <linux/io.h>
#define DMA_ERROR_CODE (~(dma_addr_t) 0x0)
extern struct dma_map_ops s390_dma_ops;
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
{
return &s390_dma_ops;
}
extern int dma_set_mask(struct device *dev, u64 mask);
extern int dma_is_consistent(struct device *dev, dma_addr_t dma_handle);
extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction);
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
#include <asm-generic/dma-mapping-common.h>
static inline int dma_supported(struct device *dev, u64 mask)
{
struct dma_map_ops *dma_ops = get_dma_ops(dev);
if (dma_ops->dma_supported == NULL)
return 1;
return dma_ops->dma_supported(dev, mask);
}
static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
{
if (!dev->dma_mask)
return 0;
return addr + size - 1 <= *dev->dma_mask;
}
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
struct dma_map_ops *dma_ops = get_dma_ops(dev);
if (dma_ops->mapping_error)
return dma_ops->mapping_error(dev, dma_addr);
return (dma_addr == 0UL);
}
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
{
struct dma_map_ops *ops = get_dma_ops(dev);
void *ret;
ret = ops->alloc(dev, size, dma_handle, flag, NULL);
debug_dma_alloc_coherent(dev, size, *dma_handle, ret);
return ret;
}
static inline void dma_free_coherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle)
{
struct dma_map_ops *dma_ops = get_dma_ops(dev);
dma_ops->free(dev, size, cpu_addr, dma_handle, NULL);
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
}
#endif /* _ASM_S390_DMA_MAPPING_H */
/*
* S390 version
*/
#ifndef _ASM_DMA_H
#define _ASM_DMA_H
#ifndef _ASM_S390_DMA_H
#define _ASM_S390_DMA_H
#include <asm/io.h> /* need byte IO */
#include <asm/io.h>
/*
* MAX_DMA_ADDRESS is ambiguous because on s390 its completely unrelated
* to DMA. It _is_ used for the s390 memory zone split at 2GB caused
* by the 31 bit heritage.
*/
#define MAX_DMA_ADDRESS 0x80000000
#define free_dma(x) do { } while (0)
#endif /* _ASM_DMA_H */
#endif /* _ASM_S390_DMA_H */
#ifndef _HW_IRQ_H
#define _HW_IRQ_H
#include <linux/msi.h>
#include <linux/pci.h>
static inline struct msi_desc *irq_get_msi_desc(unsigned int irq)
{
return __irq_get_msi_desc(irq);
}
/* Must be called with msi map lock held */
static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *msi)
{
if (!msi)
return -EINVAL;
msi->irq = irq;
return 0;
}
#endif
......@@ -9,9 +9,9 @@
#ifndef _S390_IO_H
#define _S390_IO_H
#include <linux/kernel.h>
#include <asm/page.h>
#define IO_SPACE_LIMIT 0xffffffff
#include <asm/pci_io.h>
/*
* Change virtual addresses to physical addresses and vv.
......@@ -24,10 +24,11 @@ static inline unsigned long virt_to_phys(volatile void * address)
" lra %0,0(%1)\n"
" jz 0f\n"
" la %0,0\n"
"0:"
"0:"
: "=a" (real_address) : "a" (address) : "cc");
return real_address;
return real_address;
}
#define virt_to_phys virt_to_phys
static inline void * phys_to_virt(unsigned long address)
{
......@@ -42,4 +43,50 @@ void unxlate_dev_mem_ptr(unsigned long phys, void *addr);
*/
#define xlate_dev_kmem_ptr(p) p
#define IO_SPACE_LIMIT 0
#ifdef CONFIG_PCI
#define ioremap_nocache(addr, size) ioremap(addr, size)
#define ioremap_wc ioremap_nocache
/* TODO: s390 cannot support io_remap_pfn_range... */
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
remap_pfn_range(vma, vaddr, pfn, size, prot)
static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
{
return (void __iomem *) offset;
}
static inline void iounmap(volatile void __iomem *addr)
{
}
/*
* s390 needs a private implementation of pci_iomap since ioremap with its
* offset parameter isn't sufficient. That's because BAR spaces are not
* disjunctive on s390 so we need the bar parameter of pci_iomap to find
* the corresponding device and create the mapping cookie.
*/
#define pci_iomap pci_iomap
#define pci_iounmap pci_iounmap
#define memcpy_fromio(dst, src, count) zpci_memcpy_fromio(dst, src, count)
#define memcpy_toio(dst, src, count) zpci_memcpy_toio(dst, src, count)
#define memset_io(dst, val, count) zpci_memset_io(dst, val, count)
#define __raw_readb zpci_read_u8
#define __raw_readw zpci_read_u16
#define __raw_readl zpci_read_u32
#define __raw_readq zpci_read_u64
#define __raw_writeb zpci_write_u8
#define __raw_writew zpci_write_u16
#define __raw_writel zpci_write_u32
#define __raw_writeq zpci_write_u64
#endif /* CONFIG_PCI */
#include <asm-generic/io.h>
#endif
......@@ -33,6 +33,8 @@ enum interruption_class {
IOINT_APB,
IOINT_ADM,
IOINT_CSC,
IOINT_PCI,
IOINT_MSI,
NMI_NMI,
NR_IRQS,
};
......@@ -51,4 +53,14 @@ void service_subclass_irq_unregister(void);
void measurement_alert_subclass_register(void);
void measurement_alert_subclass_unregister(void);
#ifdef CONFIG_LOCKDEP
# define disable_irq_nosync_lockdep(irq) disable_irq_nosync(irq)
# define disable_irq_nosync_lockdep_irqsave(irq, flags) \
disable_irq_nosync(irq)
# define disable_irq_lockdep(irq) disable_irq(irq)
# define enable_irq_lockdep(irq) enable_irq(irq)
# define enable_irq_lockdep_irqrestore(irq, flags) \
enable_irq(irq)
#endif
#endif /* _ASM_IRQ_H */
......@@ -18,6 +18,7 @@
#define CHSC_SCH_ISC 7 /* CHSC subchannels */
/* Adapter interrupts. */
#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
#define PCI_ISC 2 /* PCI I/O subchannels */
#define AP_ISC 6 /* adjunct processor (crypto) devices */
/* Functions for registration of I/O interruption subclasses */
......
......@@ -30,6 +30,8 @@
#include <asm/setup.h>
#ifndef __ASSEMBLY__
void storage_key_init_range(unsigned long start, unsigned long end);
static unsigned long pfmf(unsigned long function, unsigned long address)
{
asm volatile(
......
#ifndef __ASM_S390_PCI_H
#define __ASM_S390_PCI_H
/* S/390 systems don't have a PCI bus. This file is just here because some stupid .c code
* includes it even if CONFIG_PCI is not set.
*/
/* must be set before including asm-generic/pci.h */
#define PCI_DMA_BUS_IS_PHYS (0)
/* must be set before including pci_clp.h */
#define PCI_BAR_COUNT 6
#endif /* __ASM_S390_PCI_H */
#include <asm-generic/pci.h>
#include <asm-generic/pci-dma-compat.h>
#include <asm/pci_clp.h>
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
#define pcibios_assign_all_busses() (0)
void __iomem *pci_iomap(struct pci_dev *, int, unsigned long);
void pci_iounmap(struct pci_dev *, void __iomem *);
int pci_domain_nr(struct pci_bus *);
int pci_proc_domain(struct pci_bus *);
/* MSI arch hooks */
#define arch_setup_msi_irqs arch_setup_msi_irqs
#define arch_teardown_msi_irqs arch_teardown_msi_irqs
#define ZPCI_BUS_NR 0 /* default bus number */
#define ZPCI_DEVFN 0 /* default device number */
/* PCI Function Controls */
#define ZPCI_FC_FN_ENABLED 0x80
#define ZPCI_FC_ERROR 0x40
#define ZPCI_FC_BLOCKED 0x20
#define ZPCI_FC_DMA_ENABLED 0x10
struct msi_map {
unsigned long irq;
struct msi_desc *msi;
struct hlist_node msi_chain;
};
#define ZPCI_NR_MSI_VECS 64
#define ZPCI_MSI_MASK (ZPCI_NR_MSI_VECS - 1)
enum zpci_state {
ZPCI_FN_STATE_RESERVED,
ZPCI_FN_STATE_STANDBY,
ZPCI_FN_STATE_CONFIGURED,
ZPCI_FN_STATE_ONLINE,
NR_ZPCI_FN_STATES,
};
struct zpci_bar_struct {
u32 val; /* bar start & 3 flag bits */
u8 size; /* order 2 exponent */
u16 map_idx; /* index into bar mapping array */
};
/* Private data per function */
struct zpci_dev {
struct pci_dev *pdev;
struct pci_bus *bus;
struct list_head entry; /* list of all zpci_devices, needed for hotplug, etc. */
enum zpci_state state;
u32 fid; /* function ID, used by sclp */
u32 fh; /* function handle, used by insn's */
u16 pchid; /* physical channel ID */
u8 pfgid; /* function group ID */
u16 domain;
/* IRQ stuff */
u64 msi_addr; /* MSI address */
struct zdev_irq_map *irq_map;
struct msi_map *msi_map[ZPCI_NR_MSI_VECS];
unsigned int aisb; /* number of the summary bit */
/* DMA stuff */
unsigned long *dma_table;
spinlock_t dma_table_lock;
int tlb_refresh;
spinlock_t iommu_bitmap_lock;
unsigned long *iommu_bitmap;
unsigned long iommu_size;
unsigned long iommu_pages;
unsigned int next_bit;
struct zpci_bar_struct bars[PCI_BAR_COUNT];
u64 start_dma; /* Start of available DMA addresses */
u64 end_dma; /* End of available DMA addresses */
u64 dma_mask; /* DMA address space mask */
enum pci_bus_speed max_bus_speed;
};
struct pci_hp_callback_ops {
int (*create_slot) (struct zpci_dev *zdev);
void (*remove_slot) (struct zpci_dev *zdev);
};
static inline bool zdev_enabled(struct zpci_dev *zdev)
{
return (zdev->fh & (1UL << 31)) ? true : false;
}
/* -----------------------------------------------------------------------------
Prototypes
----------------------------------------------------------------------------- */
/* Base stuff */
struct zpci_dev *zpci_alloc_device(void);
int zpci_create_device(struct zpci_dev *);
int zpci_enable_device(struct zpci_dev *);
void zpci_stop_device(struct zpci_dev *);
void zpci_free_device(struct zpci_dev *);
int zpci_scan_device(struct zpci_dev *);
int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64);
int zpci_unregister_ioat(struct zpci_dev *, u8);
/* CLP */
int clp_find_pci_devices(void);
int clp_add_pci_device(u32, u32, int);
int clp_enable_fh(struct zpci_dev *, u8);
int clp_disable_fh(struct zpci_dev *);
/* MSI */
struct msi_desc *__irq_get_msi_desc(unsigned int);
int zpci_msi_set_mask_bits(struct msi_desc *, u32, u32);
int zpci_setup_msi_irq(struct zpci_dev *, struct msi_desc *, unsigned int, int);
void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *);
int zpci_msihash_init(void);
void zpci_msihash_exit(void);
/* Error handling and recovery */
void zpci_event_error(void *);
void zpci_event_availability(void *);
/* Helpers */
struct zpci_dev *get_zdev(struct pci_dev *);
struct zpci_dev *get_zdev_by_fid(u32);
bool zpci_fid_present(u32);
/* sysfs */
int zpci_sysfs_add_device(struct device *);
void zpci_sysfs_remove_device(struct device *);
/* DMA */
int zpci_dma_init(void);
void zpci_dma_exit(void);
/* Hotplug */
extern struct mutex zpci_list_lock;
extern struct list_head zpci_list;
extern struct pci_hp_callback_ops hotplug_ops;
extern unsigned int pci_probe;
#endif
#ifndef _ASM_S390_PCI_CLP_H
#define _ASM_S390_PCI_CLP_H
#include <asm/clp.h>
/*
* Call Logical Processor - Command Codes
*/
#define CLP_LIST_PCI 0x0002
#define CLP_QUERY_PCI_FN 0x0003
#define CLP_QUERY_PCI_FNGRP 0x0004
#define CLP_SET_PCI_FN 0x0005
/* PCI function handle list entry */
struct clp_fh_list_entry {
u16 device_id;
u16 vendor_id;
u32 config_state : 1;
u32 : 31;
u32 fid; /* PCI function id */
u32 fh; /* PCI function handle */
} __packed;
#define CLP_RC_SETPCIFN_FH 0x0101 /* Invalid PCI fn handle */
#define CLP_RC_SETPCIFN_FHOP 0x0102 /* Fn handle not valid for op */
#define CLP_RC_SETPCIFN_DMAAS 0x0103 /* Invalid DMA addr space */
#define CLP_RC_SETPCIFN_RES 0x0104 /* Insufficient resources */
#define CLP_RC_SETPCIFN_ALRDY 0x0105 /* Fn already in requested state */
#define CLP_RC_SETPCIFN_ERR 0x0106 /* Fn in permanent error state */
#define CLP_RC_SETPCIFN_RECPND 0x0107 /* Error recovery pending */
#define CLP_RC_SETPCIFN_BUSY 0x0108 /* Fn busy */
#define CLP_RC_LISTPCI_BADRT 0x010a /* Resume token not recognized */
#define CLP_RC_QUERYPCIFG_PFGID 0x010b /* Unrecognized PFGID */
/* request or response block header length */
#define LIST_PCI_HDR_LEN 32
/* Number of function handles fitting in response block */
#define CLP_FH_LIST_NR_ENTRIES \
((CLP_BLK_SIZE - 2 * LIST_PCI_HDR_LEN) \
/ sizeof(struct clp_fh_list_entry))
#define CLP_SET_ENABLE_PCI_FN 0 /* Yes, 0 enables it */
#define CLP_SET_DISABLE_PCI_FN 1 /* Yes, 1 disables it */
#define CLP_UTIL_STR_LEN 64
/* List PCI functions request */
struct clp_req_list_pci {
struct clp_req_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u64 resume_token;
u64 reserved2;
} __packed;
/* List PCI functions response */
struct clp_rsp_list_pci {
struct clp_rsp_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u64 resume_token;
u32 reserved2;
u16 max_fn;
u8 reserved3;
u8 entry_size;
struct clp_fh_list_entry fh_list[CLP_FH_LIST_NR_ENTRIES];
} __packed;
/* Query PCI function request */
struct clp_req_query_pci {
struct clp_req_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u32 fh; /* function handle */
u32 reserved2;
u64 reserved3;
} __packed;
/* Query PCI function response */
struct clp_rsp_query_pci {
struct clp_rsp_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u16 vfn; /* virtual fn number */
u16 : 7;
u16 util_str_avail : 1; /* utility string available? */
u16 pfgid : 8; /* pci function group id */
u32 fid; /* pci function id */
u8 bar_size[PCI_BAR_COUNT];
u16 pchid;
u32 bar[PCI_BAR_COUNT];
u64 reserved2;
u64 sdma; /* start dma as */
u64 edma; /* end dma as */
u64 reserved3[6];
u8 util_str[CLP_UTIL_STR_LEN]; /* utility string */
} __packed;
/* Query PCI function group request */
struct clp_req_query_pci_grp {
struct clp_req_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u32 : 24;
u32 pfgid : 8; /* function group id */
u32 reserved2;
u64 reserved3;
} __packed;
/* Query PCI function group response */
struct clp_rsp_query_pci_grp {
struct clp_rsp_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u16 : 4;
u16 noi : 12; /* number of interrupts */
u8 version;
u8 : 6;
u8 frame : 1;
u8 refresh : 1; /* TLB refresh mode */
u16 reserved2;
u16 mui;
u64 reserved3;
u64 dasm; /* dma address space mask */
u64 msia; /* MSI address */
u64 reserved4;
u64 reserved5;
} __packed;
/* Set PCI function request */
struct clp_req_set_pci {
struct clp_req_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u32 fh; /* function handle */
u16 reserved2;
u8 oc; /* operation controls */
u8 ndas; /* number of dma spaces */
u64 reserved3;
} __packed;
/* Set PCI function response */
struct clp_rsp_set_pci {
struct clp_rsp_hdr hdr;
u32 fmt : 4; /* cmd request block format */
u32 : 28;
u64 reserved1;
u32 fh; /* function handle */
u32 reserved3;
u64 reserved4;
} __packed;
/* Combined request/response block structures used by clp insn */
struct clp_req_rsp_list_pci {
struct clp_req_list_pci request;
struct clp_rsp_list_pci response;
} __packed;
struct clp_req_rsp_set_pci {
struct clp_req_set_pci request;
struct clp_rsp_set_pci response;
} __packed;
struct clp_req_rsp_query_pci {
struct clp_req_query_pci request;
struct clp_rsp_query_pci response;
} __packed;
struct clp_req_rsp_query_pci_grp {
struct clp_req_query_pci_grp request;
struct clp_rsp_query_pci_grp response;
} __packed;
#endif
#ifndef _ASM_S390_PCI_DMA_H
#define _ASM_S390_PCI_DMA_H
/* I/O Translation Anchor (IOTA) */
enum zpci_ioat_dtype {
ZPCI_IOTA_STO = 0,
ZPCI_IOTA_RTTO = 1,
ZPCI_IOTA_RSTO = 2,
ZPCI_IOTA_RFTO = 3,
ZPCI_IOTA_PFAA = 4,
ZPCI_IOTA_IOPFAA = 5,
ZPCI_IOTA_IOPTO = 7
};
#define ZPCI_IOTA_IOT_ENABLED 0x800UL
#define ZPCI_IOTA_DT_ST (ZPCI_IOTA_STO << 2)
#define ZPCI_IOTA_DT_RT (ZPCI_IOTA_RTTO << 2)
#define ZPCI_IOTA_DT_RS (ZPCI_IOTA_RSTO << 2)
#define ZPCI_IOTA_DT_RF (ZPCI_IOTA_RFTO << 2)
#define ZPCI_IOTA_DT_PF (ZPCI_IOTA_PFAA << 2)
#define ZPCI_IOTA_FS_4K 0
#define ZPCI_IOTA_FS_1M 1
#define ZPCI_IOTA_FS_2G 2
#define ZPCI_KEY (PAGE_DEFAULT_KEY << 5)
#define ZPCI_IOTA_STO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST)
#define ZPCI_IOTA_RTTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT)
#define ZPCI_IOTA_RSTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RS)
#define ZPCI_IOTA_RFTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RF)
#define ZPCI_IOTA_RFAA_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_PF | ZPCI_IOTA_FS_2G)
/* I/O Region and segment tables */
#define ZPCI_INDEX_MASK 0x7ffUL
#define ZPCI_TABLE_TYPE_MASK 0xc
#define ZPCI_TABLE_TYPE_RFX 0xc
#define ZPCI_TABLE_TYPE_RSX 0x8
#define ZPCI_TABLE_TYPE_RTX 0x4
#define ZPCI_TABLE_TYPE_SX 0x0
#define ZPCI_TABLE_LEN_RFX 0x3
#define ZPCI_TABLE_LEN_RSX 0x3
#define ZPCI_TABLE_LEN_RTX 0x3
#define ZPCI_TABLE_OFFSET_MASK 0xc0
#define ZPCI_TABLE_SIZE 0x4000
#define ZPCI_TABLE_ALIGN ZPCI_TABLE_SIZE
#define ZPCI_TABLE_ENTRY_SIZE (sizeof(unsigned long))
#define ZPCI_TABLE_ENTRIES (ZPCI_TABLE_SIZE / ZPCI_TABLE_ENTRY_SIZE)
#define ZPCI_TABLE_BITS 11
#define ZPCI_PT_BITS 8
#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT)
#define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS)
#define ZPCI_RTE_FLAG_MASK 0x3fffUL
#define ZPCI_RTE_ADDR_MASK (~ZPCI_RTE_FLAG_MASK)
#define ZPCI_STE_FLAG_MASK 0x7ffUL
#define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK)
/* I/O Page tables */
#define ZPCI_PTE_VALID_MASK 0x400
#define ZPCI_PTE_INVALID 0x400
#define ZPCI_PTE_VALID 0x000
#define ZPCI_PT_SIZE 0x800
#define ZPCI_PT_ALIGN ZPCI_PT_SIZE
#define ZPCI_PT_ENTRIES (ZPCI_PT_SIZE / ZPCI_TABLE_ENTRY_SIZE)
#define ZPCI_PT_MASK (ZPCI_PT_ENTRIES - 1)
#define ZPCI_PTE_FLAG_MASK 0xfffUL
#define ZPCI_PTE_ADDR_MASK (~ZPCI_PTE_FLAG_MASK)
/* Shared bits */
#define ZPCI_TABLE_VALID 0x00
#define ZPCI_TABLE_INVALID 0x20
#define ZPCI_TABLE_PROTECTED 0x200
#define ZPCI_TABLE_UNPROTECTED 0x000
#define ZPCI_TABLE_VALID_MASK 0x20
#define ZPCI_TABLE_PROT_MASK 0x200
static inline unsigned int calc_rtx(dma_addr_t ptr)
{
return ((unsigned long) ptr >> ZPCI_RT_SHIFT) & ZPCI_INDEX_MASK;
}
static inline unsigned int calc_sx(dma_addr_t ptr)
{
return ((unsigned long) ptr >> ZPCI_ST_SHIFT) & ZPCI_INDEX_MASK;
}
static inline unsigned int calc_px(dma_addr_t ptr)
{
return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK;
}
static inline void set_pt_pfaa(unsigned long *entry, void *pfaa)
{
*entry &= ZPCI_PTE_FLAG_MASK;
*entry |= ((unsigned long) pfaa & ZPCI_PTE_ADDR_MASK);
}
static inline void set_rt_sto(unsigned long *entry, void *sto)
{
*entry &= ZPCI_RTE_FLAG_MASK;
*entry |= ((unsigned long) sto & ZPCI_RTE_ADDR_MASK);
*entry |= ZPCI_TABLE_TYPE_RTX;
}
static inline void set_st_pto(unsigned long *entry, void *pto)
{
*entry &= ZPCI_STE_FLAG_MASK;
*entry |= ((unsigned long) pto & ZPCI_STE_ADDR_MASK);
*entry |= ZPCI_TABLE_TYPE_SX;
}
static inline void validate_rt_entry(unsigned long *entry)
{
*entry &= ~ZPCI_TABLE_VALID_MASK;
*entry &= ~ZPCI_TABLE_OFFSET_MASK;
*entry |= ZPCI_TABLE_VALID;
*entry |= ZPCI_TABLE_LEN_RTX;
}
static inline void validate_st_entry(unsigned long *entry)
{
*entry &= ~ZPCI_TABLE_VALID_MASK;
*entry |= ZPCI_TABLE_VALID;
}
static inline void invalidate_table_entry(unsigned long *entry)
{
*entry &= ~ZPCI_TABLE_VALID_MASK;
*entry |= ZPCI_TABLE_INVALID;
}
static inline void invalidate_pt_entry(unsigned long *entry)
{
WARN_ON_ONCE((*entry & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_INVALID);
*entry &= ~ZPCI_PTE_VALID_MASK;
*entry |= ZPCI_PTE_INVALID;
}
static inline void validate_pt_entry(unsigned long *entry)
{
WARN_ON_ONCE((*entry & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID);
*entry &= ~ZPCI_PTE_VALID_MASK;
*entry |= ZPCI_PTE_VALID;
}
static inline void entry_set_protected(unsigned long *entry)
{
*entry &= ~ZPCI_TABLE_PROT_MASK;
*entry |= ZPCI_TABLE_PROTECTED;
}
static inline void entry_clr_protected(unsigned long *entry)
{
*entry &= ~ZPCI_TABLE_PROT_MASK;
*entry |= ZPCI_TABLE_UNPROTECTED;
}
static inline int reg_entry_isvalid(unsigned long entry)
{
return (entry & ZPCI_TABLE_VALID_MASK) == ZPCI_TABLE_VALID;
}
static inline int pt_entry_isvalid(unsigned long entry)
{
return (entry & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID;
}
static inline int entry_isprotected(unsigned long entry)
{
return (entry & ZPCI_TABLE_PROT_MASK) == ZPCI_TABLE_PROTECTED;
}
static inline unsigned long *get_rt_sto(unsigned long entry)
{
return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_RTX)
? (unsigned long *) (entry & ZPCI_RTE_ADDR_MASK)
: NULL;
}
static inline unsigned long *get_st_pto(unsigned long entry)
{
return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_SX)
? (unsigned long *) (entry & ZPCI_STE_ADDR_MASK)
: NULL;
}
/* Prototypes */
int zpci_dma_init_device(struct zpci_dev *);
void zpci_dma_exit_device(struct zpci_dev *);
#endif
#ifndef _ASM_S390_PCI_INSN_H
#define _ASM_S390_PCI_INSN_H
#include <linux/delay.h>
#define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */
/* Load/Store status codes */
#define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4
#define ZPCI_PCI_ST_FUNC_IN_ERR 8
#define ZPCI_PCI_ST_BLOCKED 12
#define ZPCI_PCI_ST_INSUF_RES 16
#define ZPCI_PCI_ST_INVAL_AS 20
#define ZPCI_PCI_ST_FUNC_ALREADY_ENABLED 24
#define ZPCI_PCI_ST_DMA_AS_NOT_ENABLED 28
#define ZPCI_PCI_ST_2ND_OP_IN_INV_AS 36
#define ZPCI_PCI_ST_FUNC_NOT_AVAIL 40
#define ZPCI_PCI_ST_ALREADY_IN_RQ_STATE 44
/* Load/Store return codes */
#define ZPCI_PCI_LS_OK 0
#define ZPCI_PCI_LS_ERR 1
#define ZPCI_PCI_LS_BUSY 2
#define ZPCI_PCI_LS_INVAL_HANDLE 3
/* Load/Store address space identifiers */
#define ZPCI_PCIAS_MEMIO_0 0
#define ZPCI_PCIAS_MEMIO_1 1
#define ZPCI_PCIAS_MEMIO_2 2
#define ZPCI_PCIAS_MEMIO_3 3
#define ZPCI_PCIAS_MEMIO_4 4
#define ZPCI_PCIAS_MEMIO_5 5
#define ZPCI_PCIAS_CFGSPC 15
/* Modify PCI Function Controls */
#define ZPCI_MOD_FC_REG_INT 2
#define ZPCI_MOD_FC_DEREG_INT 3
#define ZPCI_MOD_FC_REG_IOAT 4
#define ZPCI_MOD_FC_DEREG_IOAT 5
#define ZPCI_MOD_FC_REREG_IOAT 6
#define ZPCI_MOD_FC_RESET_ERROR 7
#define ZPCI_MOD_FC_RESET_BLOCK 9
#define ZPCI_MOD_FC_SET_MEASURE 10
/* FIB function controls */
#define ZPCI_FIB_FC_ENABLED 0x80
#define ZPCI_FIB_FC_ERROR 0x40
#define ZPCI_FIB_FC_LS_BLOCKED 0x20
#define ZPCI_FIB_FC_DMAAS_REG 0x10
/* FIB function controls */
#define ZPCI_FIB_FC_ENABLED 0x80
#define ZPCI_FIB_FC_ERROR 0x40
#define ZPCI_FIB_FC_LS_BLOCKED 0x20
#define ZPCI_FIB_FC_DMAAS_REG 0x10
/* Function Information Block */
struct zpci_fib {
u32 fmt : 8; /* format */
u32 : 24;
u32 reserved1;
u8 fc; /* function controls */
u8 reserved2;
u16 reserved3;
u32 reserved4;
u64 pba; /* PCI base address */
u64 pal; /* PCI address limit */
u64 iota; /* I/O Translation Anchor */
u32 : 1;
u32 isc : 3; /* Interrupt subclass */
u32 noi : 12; /* Number of interrupts */
u32 : 2;
u32 aibvo : 6; /* Adapter interrupt bit vector offset */
u32 sum : 1; /* Adapter int summary bit enabled */
u32 : 1;
u32 aisbo : 6; /* Adapter int summary bit offset */
u32 reserved5;
u64 aibv; /* Adapter int bit vector address */
u64 aisb; /* Adapter int summary bit address */
u64 fmb_addr; /* Function measurement block address and key */
u64 reserved6;
u64 reserved7;
} __packed;
/* Modify PCI Function Controls */
static inline u8 __mpcifc(u64 req, struct zpci_fib *fib, u8 *status)
{
u8 cc;
asm volatile (
" .insn rxy,0xe300000000d0,%[req],%[fib]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [req] "+d" (req), [fib] "+Q" (*fib)
: : "cc");
*status = req >> 24 & 0xff;
return cc;
}
static inline int mpcifc_instr(u64 req, struct zpci_fib *fib)
{
u8 cc, status;
do {
cc = __mpcifc(req, fib, &status);
if (cc == 2)
msleep(ZPCI_INSN_BUSY_DELAY);
} while (cc == 2);
if (cc)
printk_once(KERN_ERR "%s: error cc: %d status: %d\n",
__func__, cc, status);
return (cc) ? -EIO : 0;
}
/* Refresh PCI Translations */
static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status)
{
register u64 __addr asm("2") = addr;
register u64 __range asm("3") = range;
u8 cc;
asm volatile (
" .insn rre,0xb9d30000,%[fn],%[addr]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [fn] "+d" (fn)
: [addr] "d" (__addr), "d" (__range)
: "cc");
*status = fn >> 24 & 0xff;
return cc;
}
static inline int rpcit_instr(u64 fn, u64 addr, u64 range)
{
u8 cc, status;
do {
cc = __rpcit(fn, addr, range, &status);
if (cc == 2)
udelay(ZPCI_INSN_BUSY_DELAY);
} while (cc == 2);
if (cc)
printk_once(KERN_ERR "%s: error cc: %d status: %d dma_addr: %Lx size: %Lx\n",
__func__, cc, status, addr, range);
return (cc) ? -EIO : 0;
}
/* Store PCI function controls */
static inline u8 __stpcifc(u32 handle, u8 space, struct zpci_fib *fib, u8 *status)
{
u64 fn = (u64) handle << 32 | space << 16;
u8 cc;
asm volatile (
" .insn rxy,0xe300000000d4,%[fn],%[fib]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [fn] "+d" (fn), [fib] "=m" (*fib)
: : "cc");
*status = fn >> 24 & 0xff;
return cc;
}
/* Set Interruption Controls */
static inline void sic_instr(u16 ctl, char *unused, u8 isc)
{
asm volatile (
" .insn rsy,0xeb00000000d1,%[ctl],%[isc],%[u]\n"
: : [ctl] "d" (ctl), [isc] "d" (isc << 27), [u] "Q" (*unused));
}
/* PCI Load */
static inline u8 __pcilg(u64 *data, u64 req, u64 offset, u8 *status)
{
register u64 __req asm("2") = req;
register u64 __offset asm("3") = offset;
u64 __data;
u8 cc;
asm volatile (
" .insn rre,0xb9d20000,%[data],%[req]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [data] "=d" (__data), [req] "+d" (__req)
: "d" (__offset)
: "cc");
*status = __req >> 24 & 0xff;
*data = __data;
return cc;
}
static inline int pcilg_instr(u64 *data, u64 req, u64 offset)
{
u8 cc, status;
do {
cc = __pcilg(data, req, offset, &status);
if (cc == 2)
udelay(ZPCI_INSN_BUSY_DELAY);
} while (cc == 2);
if (cc) {
printk_once(KERN_ERR "%s: error cc: %d status: %d req: %Lx offset: %Lx\n",
__func__, cc, status, req, offset);
/* TODO: on IO errors set data to 0xff...
* here or in users of pcilg (le conversion)?
*/
}
return (cc) ? -EIO : 0;
}
/* PCI Store */
static inline u8 __pcistg(u64 data, u64 req, u64 offset, u8 *status)
{
register u64 __req asm("2") = req;
register u64 __offset asm("3") = offset;
u8 cc;
asm volatile (
" .insn rre,0xb9d00000,%[data],%[req]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [req] "+d" (__req)
: "d" (__offset), [data] "d" (data)
: "cc");
*status = __req >> 24 & 0xff;
return cc;
}
static inline int pcistg_instr(u64 data, u64 req, u64 offset)
{
u8 cc, status;
do {
cc = __pcistg(data, req, offset, &status);
if (cc == 2)
udelay(ZPCI_INSN_BUSY_DELAY);
} while (cc == 2);
if (cc)
printk_once(KERN_ERR "%s: error cc: %d status: %d req: %Lx offset: %Lx\n",
__func__, cc, status, req, offset);
return (cc) ? -EIO : 0;
}
/* PCI Store Block */
static inline u8 __pcistb(const u64 *data, u64 req, u64 offset, u8 *status)
{
u8 cc;
asm volatile (
" .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [req] "+d" (req)
: [offset] "d" (offset), [data] "Q" (*data)
: "cc");
*status = req >> 24 & 0xff;
return cc;
}
static inline int pcistb_instr(const u64 *data, u64 req, u64 offset)
{
u8 cc, status;
do {
cc = __pcistb(data, req, offset, &status);
if (cc == 2)
udelay(ZPCI_INSN_BUSY_DELAY);
} while (cc == 2);
if (cc)
printk_once(KERN_ERR "%s: error cc: %d status: %d req: %Lx offset: %Lx\n",
__func__, cc, status, req, offset);
return (cc) ? -EIO : 0;
}
#endif
#ifndef _ASM_S390_PCI_IO_H
#define _ASM_S390_PCI_IO_H
#ifdef CONFIG_PCI
#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/pci_insn.h>
/* I/O Map */
#define ZPCI_IOMAP_MAX_ENTRIES 0x7fff
#define ZPCI_IOMAP_ADDR_BASE 0x8000000000000000ULL
#define ZPCI_IOMAP_ADDR_IDX_MASK 0x7fff000000000000ULL
#define ZPCI_IOMAP_ADDR_OFF_MASK 0x0000ffffffffffffULL
struct zpci_iomap_entry {
u32 fh;
u8 bar;
};
extern struct zpci_iomap_entry *zpci_iomap_start;
#define ZPCI_IDX(addr) \
(((__force u64) addr & ZPCI_IOMAP_ADDR_IDX_MASK) >> 48)
#define ZPCI_OFFSET(addr) \
((__force u64) addr & ZPCI_IOMAP_ADDR_OFF_MASK)
#define ZPCI_CREATE_REQ(handle, space, len) \
((u64) handle << 32 | space << 16 | len)
#define zpci_read(LENGTH, RETTYPE) \
static inline RETTYPE zpci_read_##RETTYPE(const volatile void __iomem *addr) \
{ \
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; \
u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, LENGTH); \
u64 data; \
int rc; \
\
rc = pcilg_instr(&data, req, ZPCI_OFFSET(addr)); \
if (rc) \
data = -1ULL; \
return (RETTYPE) data; \
}
#define zpci_write(LENGTH, VALTYPE) \
static inline void zpci_write_##VALTYPE(VALTYPE val, \
const volatile void __iomem *addr) \
{ \
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(addr)]; \
u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, LENGTH); \
u64 data = (VALTYPE) val; \
\
pcistg_instr(data, req, ZPCI_OFFSET(addr)); \
}
zpci_read(8, u64)
zpci_read(4, u32)
zpci_read(2, u16)
zpci_read(1, u8)
zpci_write(8, u64)
zpci_write(4, u32)
zpci_write(2, u16)
zpci_write(1, u8)
static inline int zpci_write_single(u64 req, const u64 *data, u64 offset, u8 len)
{
u64 val;
switch (len) {
case 1:
val = (u64) *((u8 *) data);
break;
case 2:
val = (u64) *((u16 *) data);
break;
case 4:
val = (u64) *((u32 *) data);
break;
case 8:
val = (u64) *((u64 *) data);
break;
default:
val = 0; /* let FW report error */
break;
}
return pcistg_instr(val, req, offset);
}
static inline int zpci_read_single(u64 req, u64 *dst, u64 offset, u8 len)
{
u64 data;
u8 cc;
cc = pcilg_instr(&data, req, offset);
switch (len) {
case 1:
*((u8 *) dst) = (u8) data;
break;
case 2:
*((u16 *) dst) = (u16) data;
break;
case 4:
*((u32 *) dst) = (u32) data;
break;
case 8:
*((u64 *) dst) = (u64) data;
break;
}
return cc;
}
static inline int zpci_write_block(u64 req, const u64 *data, u64 offset)
{
return pcistb_instr(data, req, offset);
}
static inline u8 zpci_get_max_write_size(u64 src, u64 dst, int len, int max)
{
int count = len > max ? max : len, size = 1;
while (!(src & 0x1) && !(dst & 0x1) && ((size << 1) <= count)) {
dst = dst >> 1;
src = src >> 1;
size = size << 1;
}
return size;
}
static inline int zpci_memcpy_fromio(void *dst,
const volatile void __iomem *src,
unsigned long n)
{
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(src)];
u64 req, offset = ZPCI_OFFSET(src);
int size, rc = 0;
while (n > 0) {
size = zpci_get_max_write_size((u64) src, (u64) dst, n, 8);
req = ZPCI_CREATE_REQ(entry->fh, entry->bar, size);
rc = zpci_read_single(req, dst, offset, size);
if (rc)
break;
offset += size;
dst += size;
n -= size;
}
return rc;
}
static inline int zpci_memcpy_toio(volatile void __iomem *dst,
const void *src, unsigned long n)
{
struct zpci_iomap_entry *entry = &zpci_iomap_start[ZPCI_IDX(dst)];
u64 req, offset = ZPCI_OFFSET(dst);
int size, rc = 0;
if (!src)
return -EINVAL;
while (n > 0) {
size = zpci_get_max_write_size((u64) dst, (u64) src, n, 128);
req = ZPCI_CREATE_REQ(entry->fh, entry->bar, size);
if (size > 8) /* main path */
rc = zpci_write_block(req, src, offset);
else
rc = zpci_write_single(req, src, offset, size);
if (rc)
break;
offset += size;
src += size;
n -= size;
}
return rc;
}
static inline int zpci_memset_io(volatile void __iomem *dst,
unsigned char val, size_t count)
{
u8 *src = kmalloc(count, GFP_KERNEL);
int rc;
if (src == NULL)
return -ENOMEM;
memset(src, val, count);
rc = zpci_memcpy_toio(dst, src, count);
kfree(src);
return rc;
}
#endif /* CONFIG_PCI */
#endif /* _ASM_S390_PCI_IO_H */
......@@ -35,7 +35,6 @@
extern pgd_t swapper_pg_dir[] __attribute__ ((aligned (4096)));
extern void paging_init(void);
extern void vmem_map_init(void);
extern void fault_init(void);
/*
* The S390 doesn't have any external MMU info: the kernel page
......@@ -336,6 +335,8 @@ extern unsigned long MODULES_END;
#define _REGION3_ENTRY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_LENGTH)
#define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INV)
#define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */
/* Bits in the segment table entry */
#define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */
#define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */
......@@ -435,6 +436,7 @@ static inline int pgd_bad(pgd_t pgd) { return 0; }
static inline int pud_present(pud_t pud) { return 1; }
static inline int pud_none(pud_t pud) { return 0; }
static inline int pud_large(pud_t pud) { return 0; }
static inline int pud_bad(pud_t pud) { return 0; }
#else /* CONFIG_64BIT */
......@@ -480,6 +482,13 @@ static inline int pud_none(pud_t pud)
return (pud_val(pud) & _REGION_ENTRY_INV) != 0UL;
}
static inline int pud_large(pud_t pud)
{
if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) != _REGION_ENTRY_TYPE_R3)
return 0;
return !!(pud_val(pud) & _REGION3_ENTRY_LARGE);
}
static inline int pud_bad(pud_t pud)
{
/*
......
......@@ -55,5 +55,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info);
void sclp_get_ipl_info(struct sclp_ipl_info *info);
bool sclp_has_linemode(void);
bool sclp_has_vt220(void);
int sclp_pci_configure(u32 fid);
int sclp_pci_deconfigure(u32 fid);
#endif /* _ASM_S390_SCLP_H */
......@@ -8,32 +8,34 @@ struct cpu;
#ifdef CONFIG_SCHED_BOOK
extern unsigned char cpu_socket_id[NR_CPUS];
#define topology_physical_package_id(cpu) (cpu_socket_id[cpu])
struct cpu_topology_s390 {
unsigned short core_id;
unsigned short socket_id;
unsigned short book_id;
cpumask_t core_mask;
cpumask_t book_mask;
};
extern struct cpu_topology_s390 cpu_topology[NR_CPUS];
#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_mask)
#define topology_book_id(cpu) (cpu_topology[cpu].book_id)
#define topology_book_cpumask(cpu) (&cpu_topology[cpu].book_mask)
extern unsigned char cpu_core_id[NR_CPUS];
extern cpumask_t cpu_core_map[NR_CPUS];
#define mc_capable() 1
static inline const struct cpumask *cpu_coregroup_mask(int cpu)
{
return &cpu_core_map[cpu];
return &cpu_topology[cpu].core_mask;
}
#define topology_core_id(cpu) (cpu_core_id[cpu])
#define topology_core_cpumask(cpu) (&cpu_core_map[cpu])
#define mc_capable() (1)
extern unsigned char cpu_book_id[NR_CPUS];
extern cpumask_t cpu_book_map[NR_CPUS];
static inline const struct cpumask *cpu_book_mask(int cpu)
{
return &cpu_book_map[cpu];
return &cpu_topology[cpu].book_mask;
}
#define topology_book_id(cpu) (cpu_book_id[cpu])
#define topology_book_cpumask(cpu) (&cpu_book_map[cpu])
int topology_cpu_init(struct cpu *);
int topology_set_cpu_management(int fc);
void topology_schedule_update(void);
......
#ifndef _ASM_S390_VGA_H
#define _ASM_S390_VGA_H
/* Avoid compile errors due to missing asm/vga.h */
#endif /* _ASM_S390_VGA_H */
......@@ -23,7 +23,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \
processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \
debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \
sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o
sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
......
此差异已折叠。
......@@ -231,12 +231,12 @@ sysc_work:
jo sysc_mcck_pending
tm __TI_flags+3(%r12),_TIF_NEED_RESCHED
jo sysc_reschedule
tm __TI_flags+3(%r12),_TIF_PER_TRAP
jo sysc_singlestep
tm __TI_flags+3(%r12),_TIF_SIGPENDING
jo sysc_sigpending
tm __TI_flags+3(%r12),_TIF_NOTIFY_RESUME
jo sysc_notify_resume
tm __TI_flags+3(%r12),_TIF_PER_TRAP
jo sysc_singlestep
j sysc_return # beware of critical section cleanup
#
......@@ -259,7 +259,6 @@ sysc_mcck_pending:
# _TIF_SIGPENDING is set, call do_signal
#
sysc_sigpending:
ni __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
lr %r2,%r11 # pass pointer to pt_regs
l %r1,BASED(.Ldo_signal)
basr %r14,%r1 # call do_signal
......@@ -286,7 +285,7 @@ sysc_notify_resume:
# _TIF_PER_TRAP is set, call do_per_trap
#
sysc_singlestep:
ni __TI_flags+3(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
ni __TI_flags+3(%r12),255-_TIF_PER_TRAP
lr %r2,%r11 # pass pointer to pt_regs
l %r1,BASED(.Ldo_per_trap)
la %r14,BASED(sysc_return)
......
......@@ -6,7 +6,6 @@
#include <asm/ptrace.h>
#include <asm/cputime.h>
extern void (*pgm_check_table[128])(struct pt_regs *);
extern void *restart_stack;
void system_call(void);
......@@ -25,6 +24,26 @@ void do_protection_exception(struct pt_regs *regs);
void do_dat_exception(struct pt_regs *regs);
void do_asce_exception(struct pt_regs *regs);
void addressing_exception(struct pt_regs *regs);
void data_exception(struct pt_regs *regs);
void default_trap_handler(struct pt_regs *regs);
void divide_exception(struct pt_regs *regs);
void execute_exception(struct pt_regs *regs);
void hfp_divide_exception(struct pt_regs *regs);
void hfp_overflow_exception(struct pt_regs *regs);
void hfp_significance_exception(struct pt_regs *regs);
void hfp_sqrt_exception(struct pt_regs *regs);
void hfp_underflow_exception(struct pt_regs *regs);
void illegal_op(struct pt_regs *regs);
void operand_exception(struct pt_regs *regs);
void overflow_exception(struct pt_regs *regs);
void privileged_op(struct pt_regs *regs);
void space_switch_exception(struct pt_regs *regs);
void special_op_exception(struct pt_regs *regs);
void specification_exception(struct pt_regs *regs);
void transaction_exception(struct pt_regs *regs);
void translation_exception(struct pt_regs *regs);
void do_per_trap(struct pt_regs *regs);
void syscall_trace(struct pt_regs *regs, int entryexit);
void kernel_stack_overflow(struct pt_regs * regs);
......
......@@ -80,14 +80,21 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING)
#endif
.endm
.macro HANDLE_SIE_INTERCEPT scratch
.macro HANDLE_SIE_INTERCEPT scratch,pgmcheck
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
tmhh %r8,0x0001 # interrupting from user ?
jnz .+42
lgr \scratch,%r9
slg \scratch,BASED(.Lsie_loop)
clg \scratch,BASED(.Lsie_length)
.if \pgmcheck
# Some program interrupts are suppressing (e.g. protection).
# We must also check the instruction after SIE in that case.
# do_protection_exception will rewind to rewind_pad
jh .+22
.else
jhe .+22
.endif
lg %r9,BASED(.Lsie_loop)
SPP BASED(.Lhost_id) # set host id
#endif
......@@ -262,12 +269,12 @@ sysc_work:
jo sysc_mcck_pending
tm __TI_flags+7(%r12),_TIF_NEED_RESCHED
jo sysc_reschedule
tm __TI_flags+7(%r12),_TIF_PER_TRAP
jo sysc_singlestep
tm __TI_flags+7(%r12),_TIF_SIGPENDING
jo sysc_sigpending
tm __TI_flags+7(%r12),_TIF_NOTIFY_RESUME
jo sysc_notify_resume
tm __TI_flags+7(%r12),_TIF_PER_TRAP
jo sysc_singlestep
j sysc_return # beware of critical section cleanup
#
......@@ -288,7 +295,6 @@ sysc_mcck_pending:
# _TIF_SIGPENDING is set, call do_signal
#
sysc_sigpending:
ni __TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
lgr %r2,%r11 # pass pointer to pt_regs
brasl %r14,do_signal
tm __TI_flags+7(%r12),_TIF_SYSCALL
......@@ -313,7 +319,7 @@ sysc_notify_resume:
# _TIF_PER_TRAP is set, call do_per_trap
#
sysc_singlestep:
ni __TI_flags+7(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
ni __TI_flags+7(%r12),255-_TIF_PER_TRAP
lgr %r2,%r11 # pass pointer to pt_regs
larl %r14,sysc_return
jg do_per_trap
......@@ -375,7 +381,7 @@ ENTRY(pgm_check_handler)
lg %r12,__LC_THREAD_INFO
larl %r13,system_call
lmg %r8,%r9,__LC_PGM_OLD_PSW
HANDLE_SIE_INTERCEPT %r14
HANDLE_SIE_INTERCEPT %r14,1
tmhh %r8,0x0001 # test problem state bit
jnz 1f # -> fault in user space
tmhh %r8,0x4000 # PER bit set in old PSW ?
......@@ -413,9 +419,9 @@ ENTRY(pgm_check_handler)
larl %r1,pgm_check_table
llgh %r10,__PT_INT_CODE+2(%r11)
nill %r10,0x007f
sll %r10,3
sll %r10,2
je sysc_return
lg %r1,0(%r10,%r1) # load address of handler routine
lgf %r1,0(%r10,%r1) # load address of handler routine
lgr %r2,%r11 # pass pointer to pt_regs
basr %r14,%r1 # branch to interrupt-handler
j sysc_return
......@@ -451,7 +457,7 @@ ENTRY(io_int_handler)
lg %r12,__LC_THREAD_INFO
larl %r13,system_call
lmg %r8,%r9,__LC_IO_OLD_PSW
HANDLE_SIE_INTERCEPT %r14
HANDLE_SIE_INTERCEPT %r14,0
SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
tmhh %r8,0x0001 # interrupting from user?
jz io_skip
......@@ -597,7 +603,7 @@ ENTRY(ext_int_handler)
lg %r12,__LC_THREAD_INFO
larl %r13,system_call
lmg %r8,%r9,__LC_EXT_OLD_PSW
HANDLE_SIE_INTERCEPT %r14
HANDLE_SIE_INTERCEPT %r14,0
SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
tmhh %r8,0x0001 # interrupting from user ?
jz ext_skip
......@@ -645,7 +651,7 @@ ENTRY(mcck_int_handler)
lg %r12,__LC_THREAD_INFO
larl %r13,system_call
lmg %r8,%r9,__LC_MCK_OLD_PSW
HANDLE_SIE_INTERCEPT %r14
HANDLE_SIE_INTERCEPT %r14,0
tm __LC_MCCK_CODE,0x80 # system damage?
jo mcck_panic # yes -> rest of mcck code invalid
lghi %r14,__LC_CPU_TIMER_SAVE_AREA
......@@ -944,6 +950,13 @@ ENTRY(sie64a)
stg %r3,__SF_EMPTY+8(%r15) # save guest register save area
xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # host id == 0
lmg %r0,%r13,0(%r3) # load guest gprs 0-13
# some program checks are suppressing. C code (e.g. do_protection_exception)
# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other
# instructions in the sie_loop should not cause program interrupts. So
# lets use a nop (47 00 00 00) as a landing pad.
# See also HANDLE_SIE_INTERCEPT
rewind_pad:
nop 0
sie_loop:
lg %r14,__LC_THREAD_INFO # pointer thread_info struct
tm __TI_flags+7(%r14),_TIF_EXIT_SIE
......@@ -983,6 +996,7 @@ sie_fault:
.Lhost_id:
.quad 0
EX_TABLE(rewind_pad,sie_fault)
EX_TABLE(sie_loop,sie_fault)
#endif
......
......@@ -393,30 +393,35 @@ ENTRY(startup_kdump)
xc 0x300(256),0x300
xc 0xe00(256),0xe00
stck __LC_LAST_UPDATE_CLOCK
spt 5f-.LPG0(%r13)
mvc __LC_LAST_UPDATE_TIMER(8),5f-.LPG0(%r13)
spt 6f-.LPG0(%r13)
mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13)
xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST
#ifndef CONFIG_MARCH_G5
# check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10}
.insn s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list
tm __LC_STFL_FAC_LIST,0x01 # stfle available ?
jz 0f
la %r0,0
la %r0,1
.insn s,0xb2b00000,__LC_STFL_FAC_LIST # store facility list extended
0: l %r0,__LC_STFL_FAC_LIST
n %r0,2f+8-.LPG0(%r13)
cl %r0,2f+8-.LPG0(%r13)
jne 1f
l %r0,__LC_STFL_FAC_LIST+4
n %r0,2f+12-.LPG0(%r13)
cl %r0,2f+12-.LPG0(%r13)
je 3f
1: l %r15,.Lstack-.LPG0(%r13)
# verify if all required facilities are supported by the machine
0: la %r1,__LC_STFL_FAC_LIST
la %r2,3f+8-.LPG0(%r13)
l %r3,0(%r2)
1: l %r0,0(%r1)
n %r0,4(%r2)
cl %r0,4(%r2)
jne 2f
la %r1,4(%r1)
la %r2,4(%r2)
ahi %r3,-1
jnz 1b
j 4f
2: l %r15,.Lstack-.LPG0(%r13)
ahi %r15,-96
la %r2,.Lals_string-.LPG0(%r13)
l %r3,.Lsclp_print-.LPG0(%r13)
basr %r14,%r3
lpsw 2f-.LPG0(%r13) # machine type not good enough, crash
lpsw 3f-.LPG0(%r13) # machine type not good enough, crash
.Lals_string:
.asciz "The Linux kernel requires more recent processor hardware"
.Lsclp_print:
......@@ -424,33 +429,42 @@ ENTRY(startup_kdump)
.Lstack:
.long 0x8000 + (1<<(PAGE_SHIFT+THREAD_ORDER))
.align 16
2: .long 0x000a0000,0x8badcccc
3: .long 0x000a0000,0x8badcccc
# List of facilities that are required. If not all facilities are present
# the kernel will crash. Format is number of facility words with bits set,
# followed by the facility words.
#if defined(CONFIG_64BIT)
#if defined(CONFIG_MARCH_Z196)
.long 0xc100efe3, 0xf46c0000
#if defined(CONFIG_MARCH_ZEC12)
.long 3, 0xc100efe3, 0xf46ce000, 0x00400000
#elif defined(CONFIG_MARCH_Z196)
.long 2, 0xc100efe3, 0xf46c0000
#elif defined(CONFIG_MARCH_Z10)
.long 0xc100efe3, 0xf0680000
.long 2, 0xc100efe3, 0xf0680000
#elif defined(CONFIG_MARCH_Z9_109)
.long 0xc100efc3, 0x00000000
.long 1, 0xc100efc3
#elif defined(CONFIG_MARCH_Z990)
.long 0xc0002000, 0x00000000
.long 1, 0xc0002000
#elif defined(CONFIG_MARCH_Z900)
.long 0xc0000000, 0x00000000
.long 1, 0xc0000000
#endif
#else
#if defined(CONFIG_MARCH_Z196)
.long 0x8100c880, 0x00000000
#if defined(CONFIG_MARCH_ZEC12)
.long 1, 0x8100c880
#elif defined(CONFIG_MARCH_Z196)
.long 1, 0x8100c880
#elif defined(CONFIG_MARCH_Z10)
.long 0x8100c880, 0x00000000
.long 1, 0x8100c880
#elif defined(CONFIG_MARCH_Z9_109)
.long 0x8100c880, 0x00000000
.long 1, 0x8100c880
#elif defined(CONFIG_MARCH_Z990)
.long 0x80002000, 0x00000000
.long 1, 0x80002000
#elif defined(CONFIG_MARCH_Z900)
.long 0x80000000, 0x00000000
.long 1, 0x80000000
#endif
#endif
3:
4:
#endif
#ifdef CONFIG_64BIT
......@@ -459,14 +473,14 @@ ENTRY(startup_kdump)
jg startup_continue
#else
/* Continue with 31bit startup code in head31.S */
l %r13,4f-.LPG0(%r13)
l %r13,5f-.LPG0(%r13)
b 0(%r13)
.align 8
4: .long startup_continue
5: .long startup_continue
#endif
.align 8
5: .long 0x7fffffff,0xffffffff
6: .long 0x7fffffff,0xffffffff
#include "head_kdump.S"
......
......@@ -58,6 +58,8 @@ static const struct irq_class intrclass_names[] = {
[IOINT_APB] = {.name = "APB", .desc = "[I/O] AP Bus"},
[IOINT_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
[IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
[IOINT_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" },
[IOINT_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" },
[NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
};
......
/*
* Program check table.
*
* Copyright IBM Corp. 2012
*/
#include <linux/linkage.h>
#ifdef CONFIG_32BIT
#define PGM_CHECK_64BIT(handler) .long default_trap_handler
#else
#define PGM_CHECK_64BIT(handler) .long handler
#endif
#define PGM_CHECK(handler) .long handler
#define PGM_CHECK_DEFAULT PGM_CHECK(default_trap_handler)
/*
* The program check table contains exactly 128 (0x00-0x7f) entries. Each
* line defines the 31 and/or 64 bit function to be called corresponding
* to the program check interruption code.
*/
.section .rodata, "a"
ENTRY(pgm_check_table)
PGM_CHECK_DEFAULT /* 00 */
PGM_CHECK(illegal_op) /* 01 */
PGM_CHECK(privileged_op) /* 02 */
PGM_CHECK(execute_exception) /* 03 */
PGM_CHECK(do_protection_exception) /* 04 */
PGM_CHECK(addressing_exception) /* 05 */
PGM_CHECK(specification_exception) /* 06 */
PGM_CHECK(data_exception) /* 07 */
PGM_CHECK(overflow_exception) /* 08 */
PGM_CHECK(divide_exception) /* 09 */
PGM_CHECK(overflow_exception) /* 0a */
PGM_CHECK(divide_exception) /* 0b */
PGM_CHECK(hfp_overflow_exception) /* 0c */
PGM_CHECK(hfp_underflow_exception) /* 0d */
PGM_CHECK(hfp_significance_exception) /* 0e */
PGM_CHECK(hfp_divide_exception) /* 0f */
PGM_CHECK(do_dat_exception) /* 10 */
PGM_CHECK(do_dat_exception) /* 11 */
PGM_CHECK(translation_exception) /* 12 */
PGM_CHECK(special_op_exception) /* 13 */
PGM_CHECK_DEFAULT /* 14 */
PGM_CHECK(operand_exception) /* 15 */
PGM_CHECK_DEFAULT /* 16 */
PGM_CHECK_DEFAULT /* 17 */
PGM_CHECK_64BIT(transaction_exception) /* 18 */
PGM_CHECK_DEFAULT /* 19 */
PGM_CHECK_DEFAULT /* 1a */
PGM_CHECK_DEFAULT /* 1b */
PGM_CHECK(space_switch_exception) /* 1c */
PGM_CHECK(hfp_sqrt_exception) /* 1d */
PGM_CHECK_DEFAULT /* 1e */
PGM_CHECK_DEFAULT /* 1f */
PGM_CHECK_DEFAULT /* 20 */
PGM_CHECK_DEFAULT /* 21 */
PGM_CHECK_DEFAULT /* 22 */
PGM_CHECK_DEFAULT /* 23 */
PGM_CHECK_DEFAULT /* 24 */
PGM_CHECK_DEFAULT /* 25 */
PGM_CHECK_DEFAULT /* 26 */
PGM_CHECK_DEFAULT /* 27 */
PGM_CHECK_DEFAULT /* 28 */
PGM_CHECK_DEFAULT /* 29 */
PGM_CHECK_DEFAULT /* 2a */
PGM_CHECK_DEFAULT /* 2b */
PGM_CHECK_DEFAULT /* 2c */
PGM_CHECK_DEFAULT /* 2d */
PGM_CHECK_DEFAULT /* 2e */
PGM_CHECK_DEFAULT /* 2f */
PGM_CHECK_DEFAULT /* 30 */
PGM_CHECK_DEFAULT /* 31 */
PGM_CHECK_DEFAULT /* 32 */
PGM_CHECK_DEFAULT /* 33 */
PGM_CHECK_DEFAULT /* 34 */
PGM_CHECK_DEFAULT /* 35 */
PGM_CHECK_DEFAULT /* 36 */
PGM_CHECK_DEFAULT /* 37 */
PGM_CHECK_64BIT(do_asce_exception) /* 38 */
PGM_CHECK_64BIT(do_dat_exception) /* 39 */
PGM_CHECK_64BIT(do_dat_exception) /* 3a */
PGM_CHECK_64BIT(do_dat_exception) /* 3b */
PGM_CHECK_DEFAULT /* 3c */
PGM_CHECK_DEFAULT /* 3d */
PGM_CHECK_DEFAULT /* 3e */
PGM_CHECK_DEFAULT /* 3f */
PGM_CHECK_DEFAULT /* 40 */
PGM_CHECK_DEFAULT /* 41 */
PGM_CHECK_DEFAULT /* 42 */
PGM_CHECK_DEFAULT /* 43 */
PGM_CHECK_DEFAULT /* 44 */
PGM_CHECK_DEFAULT /* 45 */
PGM_CHECK_DEFAULT /* 46 */
PGM_CHECK_DEFAULT /* 47 */
PGM_CHECK_DEFAULT /* 48 */
PGM_CHECK_DEFAULT /* 49 */
PGM_CHECK_DEFAULT /* 4a */
PGM_CHECK_DEFAULT /* 4b */
PGM_CHECK_DEFAULT /* 4c */
PGM_CHECK_DEFAULT /* 4d */
PGM_CHECK_DEFAULT /* 4e */
PGM_CHECK_DEFAULT /* 4f */
PGM_CHECK_DEFAULT /* 50 */
PGM_CHECK_DEFAULT /* 51 */
PGM_CHECK_DEFAULT /* 52 */
PGM_CHECK_DEFAULT /* 53 */
PGM_CHECK_DEFAULT /* 54 */
PGM_CHECK_DEFAULT /* 55 */
PGM_CHECK_DEFAULT /* 56 */
PGM_CHECK_DEFAULT /* 57 */
PGM_CHECK_DEFAULT /* 58 */
PGM_CHECK_DEFAULT /* 59 */
PGM_CHECK_DEFAULT /* 5a */
PGM_CHECK_DEFAULT /* 5b */
PGM_CHECK_DEFAULT /* 5c */
PGM_CHECK_DEFAULT /* 5d */
PGM_CHECK_DEFAULT /* 5e */
PGM_CHECK_DEFAULT /* 5f */
PGM_CHECK_DEFAULT /* 60 */
PGM_CHECK_DEFAULT /* 61 */
PGM_CHECK_DEFAULT /* 62 */
PGM_CHECK_DEFAULT /* 63 */
PGM_CHECK_DEFAULT /* 64 */
PGM_CHECK_DEFAULT /* 65 */
PGM_CHECK_DEFAULT /* 66 */
PGM_CHECK_DEFAULT /* 67 */
PGM_CHECK_DEFAULT /* 68 */
PGM_CHECK_DEFAULT /* 69 */
PGM_CHECK_DEFAULT /* 6a */
PGM_CHECK_DEFAULT /* 6b */
PGM_CHECK_DEFAULT /* 6c */
PGM_CHECK_DEFAULT /* 6d */
PGM_CHECK_DEFAULT /* 6e */
PGM_CHECK_DEFAULT /* 6f */
PGM_CHECK_DEFAULT /* 70 */
PGM_CHECK_DEFAULT /* 71 */
PGM_CHECK_DEFAULT /* 72 */
PGM_CHECK_DEFAULT /* 73 */
PGM_CHECK_DEFAULT /* 74 */
PGM_CHECK_DEFAULT /* 75 */
PGM_CHECK_DEFAULT /* 76 */
PGM_CHECK_DEFAULT /* 77 */
PGM_CHECK_DEFAULT /* 78 */
PGM_CHECK_DEFAULT /* 79 */
PGM_CHECK_DEFAULT /* 7a */
PGM_CHECK_DEFAULT /* 7b */
PGM_CHECK_DEFAULT /* 7c */
PGM_CHECK_DEFAULT /* 7d */
PGM_CHECK_DEFAULT /* 7e */
PGM_CHECK_DEFAULT /* 7f */
......@@ -777,40 +777,6 @@ static void __init reserve_crashkernel(void)
#endif
}
static void __init init_storage_keys(unsigned long start, unsigned long end)
{
unsigned long boundary, function, size;
while (start < end) {
if (MACHINE_HAS_EDAT2) {
/* set storage keys for a 2GB frame */
function = 0x22000 | PAGE_DEFAULT_KEY;
size = 1UL << 31;
boundary = (start + size) & ~(size - 1);
if (boundary <= end) {
do {
start = pfmf(function, start);
} while (start < boundary);
continue;
}
}
if (MACHINE_HAS_EDAT1) {
/* set storage keys for a 1MB frame */
function = 0x21000 | PAGE_DEFAULT_KEY;
size = 1UL << 20;
boundary = (start + size) & ~(size - 1);
if (boundary <= end) {
do {
start = pfmf(function, start);
} while (start < boundary);
continue;
}
}
page_set_storage_key(start, PAGE_DEFAULT_KEY, 0);
start += PAGE_SIZE;
}
}
static void __init setup_memory(void)
{
unsigned long bootmap_size;
......@@ -889,7 +855,7 @@ static void __init setup_memory(void)
memblock_add_node(PFN_PHYS(start_chunk),
PFN_PHYS(end_chunk - start_chunk), 0);
pfn = max(start_chunk, start_pfn);
init_storage_keys(PFN_PHYS(pfn), PFN_PHYS(end_chunk));
storage_key_init_range(PFN_PHYS(pfn), PFN_PHYS(end_chunk));
}
psw_set_key(PAGE_DEFAULT_KEY);
......@@ -1040,6 +1006,9 @@ static void __init setup_hwcaps(void)
case 0x2818:
strcpy(elf_platform, "z196");
break;
case 0x2827:
strcpy(elf_platform, "zEC12");
break;
}
}
......
......@@ -461,6 +461,8 @@ void do_signal(struct pt_regs *regs)
/* Restart system call with magic TIF bit. */
regs->gprs[2] = regs->orig_gpr2;
set_thread_flag(TIF_SYSCALL);
if (test_thread_flag(TIF_SINGLE_STEP))
set_thread_flag(TIF_PER_TRAP);
break;
}
}
......
......@@ -29,48 +29,38 @@ struct mask_info {
cpumask_t mask;
};
static int topology_enabled = 1;
static void set_topology_timer(void);
static void topology_work_fn(struct work_struct *work);
static struct sysinfo_15_1_x *tl_info;
static void set_topology_timer(void);
static DECLARE_WORK(topology_work, topology_work_fn);
/* topology_lock protects the core linked list */
static DEFINE_SPINLOCK(topology_lock);
static struct mask_info core_info;
cpumask_t cpu_core_map[NR_CPUS];
unsigned char cpu_core_id[NR_CPUS];
unsigned char cpu_socket_id[NR_CPUS];
static int topology_enabled = 1;
static DECLARE_WORK(topology_work, topology_work_fn);
/* topology_lock protects the socket and book linked lists */
static DEFINE_SPINLOCK(topology_lock);
static struct mask_info socket_info;
static struct mask_info book_info;
cpumask_t cpu_book_map[NR_CPUS];
unsigned char cpu_book_id[NR_CPUS];
struct cpu_topology_s390 cpu_topology[NR_CPUS];
static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
{
cpumask_t mask;
cpumask_clear(&mask);
if (!topology_enabled || !MACHINE_HAS_TOPOLOGY) {
cpumask_copy(&mask, cpumask_of(cpu));
cpumask_copy(&mask, cpumask_of(cpu));
if (!topology_enabled || !MACHINE_HAS_TOPOLOGY)
return mask;
for (; info; info = info->next) {
if (cpumask_test_cpu(cpu, &info->mask))
return info->mask;
}
while (info) {
if (cpumask_test_cpu(cpu, &info->mask)) {
mask = info->mask;
break;
}
info = info->next;
}
if (cpumask_empty(&mask))
cpumask_copy(&mask, cpumask_of(cpu));
return mask;
}
static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
struct mask_info *book,
struct mask_info *core,
int one_core_per_cpu)
struct mask_info *socket,
int one_socket_per_cpu)
{
unsigned int cpu;
......@@ -80,28 +70,28 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
rcpu = TOPOLOGY_CPU_BITS - 1 - cpu + tl_cpu->origin;
lcpu = smp_find_processor_id(rcpu);
if (lcpu >= 0) {
cpumask_set_cpu(lcpu, &book->mask);
cpu_book_id[lcpu] = book->id;
cpumask_set_cpu(lcpu, &core->mask);
cpu_core_id[lcpu] = rcpu;
if (one_core_per_cpu) {
cpu_socket_id[lcpu] = rcpu;
core = core->next;
} else {
cpu_socket_id[lcpu] = core->id;
}
smp_cpu_set_polarization(lcpu, tl_cpu->pp);
if (lcpu < 0)
continue;
cpumask_set_cpu(lcpu, &book->mask);
cpu_topology[lcpu].book_id = book->id;
cpumask_set_cpu(lcpu, &socket->mask);
cpu_topology[lcpu].core_id = rcpu;
if (one_socket_per_cpu) {
cpu_topology[lcpu].socket_id = rcpu;
socket = socket->next;
} else {
cpu_topology[lcpu].socket_id = socket->id;
}
smp_cpu_set_polarization(lcpu, tl_cpu->pp);
}
return core;
return socket;
}
static void clear_masks(void)
{
struct mask_info *info;
info = &core_info;
info = &socket_info;
while (info) {
cpumask_clear(&info->mask);
info = info->next;
......@@ -120,9 +110,9 @@ static union topology_entry *next_tle(union topology_entry *tle)
return (union topology_entry *)((struct topology_container *)tle + 1);
}
static void __tl_to_cores_generic(struct sysinfo_15_1_x *info)
static void __tl_to_masks_generic(struct sysinfo_15_1_x *info)
{
struct mask_info *core = &core_info;
struct mask_info *socket = &socket_info;
struct mask_info *book = &book_info;
union topology_entry *tle, *end;
......@@ -135,11 +125,11 @@ static void __tl_to_cores_generic(struct sysinfo_15_1_x *info)
book->id = tle->container.id;
break;
case 1:
core = core->next;
core->id = tle->container.id;
socket = socket->next;
socket->id = tle->container.id;
break;
case 0:
add_cpus_to_mask(&tle->cpu, book, core, 0);
add_cpus_to_mask(&tle->cpu, book, socket, 0);
break;
default:
clear_masks();
......@@ -149,9 +139,9 @@ static void __tl_to_cores_generic(struct sysinfo_15_1_x *info)
}
}
static void __tl_to_cores_z10(struct sysinfo_15_1_x *info)
static void __tl_to_masks_z10(struct sysinfo_15_1_x *info)
{
struct mask_info *core = &core_info;
struct mask_info *socket = &socket_info;
struct mask_info *book = &book_info;
union topology_entry *tle, *end;
......@@ -164,7 +154,7 @@ static void __tl_to_cores_z10(struct sysinfo_15_1_x *info)
book->id = tle->container.id;
break;
case 0:
core = add_cpus_to_mask(&tle->cpu, book, core, 1);
socket = add_cpus_to_mask(&tle->cpu, book, socket, 1);
break;
default:
clear_masks();
......@@ -174,20 +164,20 @@ static void __tl_to_cores_z10(struct sysinfo_15_1_x *info)
}
}
static void tl_to_cores(struct sysinfo_15_1_x *info)
static void tl_to_masks(struct sysinfo_15_1_x *info)
{
struct cpuid cpu_id;
get_cpu_id(&cpu_id);
spin_lock_irq(&topology_lock);
get_cpu_id(&cpu_id);
clear_masks();
switch (cpu_id.machine) {
case 0x2097:
case 0x2098:
__tl_to_cores_z10(info);
__tl_to_masks_z10(info);
break;
default:
__tl_to_cores_generic(info);
__tl_to_masks_generic(info);
}
spin_unlock_irq(&topology_lock);
}
......@@ -232,15 +222,20 @@ int topology_set_cpu_management(int fc)
return rc;
}
static void update_cpu_core_map(void)
static void update_cpu_masks(void)
{
unsigned long flags;
int cpu;
spin_lock_irqsave(&topology_lock, flags);
for_each_possible_cpu(cpu) {
cpu_core_map[cpu] = cpu_group_map(&core_info, cpu);
cpu_book_map[cpu] = cpu_group_map(&book_info, cpu);
cpu_topology[cpu].core_mask = cpu_group_map(&socket_info, cpu);
cpu_topology[cpu].book_mask = cpu_group_map(&book_info, cpu);
if (!MACHINE_HAS_TOPOLOGY) {
cpu_topology[cpu].core_id = cpu;
cpu_topology[cpu].socket_id = cpu;
cpu_topology[cpu].book_id = cpu;
}
}
spin_unlock_irqrestore(&topology_lock, flags);
}
......@@ -260,13 +255,13 @@ int arch_update_cpu_topology(void)
int cpu;
if (!MACHINE_HAS_TOPOLOGY) {
update_cpu_core_map();
update_cpu_masks();
topology_update_polarization_simple();
return 0;
}
store_topology(info);
tl_to_cores(info);
update_cpu_core_map();
tl_to_masks(info);
update_cpu_masks();
for_each_online_cpu(cpu) {
dev = get_cpu_device(cpu);
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
......@@ -355,7 +350,7 @@ void __init s390_init_cpu_topology(void)
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
printk(KERN_CONT " %d", info->mag[i]);
printk(KERN_CONT " / %d\n", info->mnest);
alloc_masks(info, &core_info, 1);
alloc_masks(info, &socket_info, 1);
alloc_masks(info, &book_info, 2);
}
......@@ -454,7 +449,7 @@ static int __init topology_init(void)
}
set_topology_timer();
out:
update_cpu_core_map();
update_cpu_masks();
return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching);
}
device_initcall(topology_init);
......@@ -41,8 +41,6 @@
#include <asm/ipl.h>
#include "entry.h"
void (*pgm_check_table[128])(struct pt_regs *regs);
int show_unhandled_signals = 1;
#define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
......@@ -350,7 +348,7 @@ void __kprobes do_per_trap(struct pt_regs *regs)
force_sig_info(SIGTRAP, &info, current);
}
static void default_trap_handler(struct pt_regs *regs)
void default_trap_handler(struct pt_regs *regs)
{
if (user_mode(regs)) {
report_user_fault(regs, SIGSEGV);
......@@ -360,9 +358,9 @@ static void default_trap_handler(struct pt_regs *regs)
}
#define DO_ERROR_INFO(name, signr, sicode, str) \
static void name(struct pt_regs *regs) \
{ \
do_trap(regs, signr, sicode, str); \
void name(struct pt_regs *regs) \
{ \
do_trap(regs, signr, sicode, str); \
}
DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
......@@ -417,7 +415,7 @@ static inline void do_fp_trap(struct pt_regs *regs, int fpc)
do_trap(regs, SIGFPE, si_code, "floating point exception");
}
static void __kprobes illegal_op(struct pt_regs *regs)
void __kprobes illegal_op(struct pt_regs *regs)
{
siginfo_t info;
__u8 opcode[6];
......@@ -536,7 +534,7 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
"specification exception");
#endif
static void data_exception(struct pt_regs *regs)
void data_exception(struct pt_regs *regs)
{
__u16 __user *location;
int signal = 0;
......@@ -611,7 +609,7 @@ static void data_exception(struct pt_regs *regs)
do_trap(regs, signal, ILL_ILLOPN, "data exception");
}
static void space_switch_exception(struct pt_regs *regs)
void space_switch_exception(struct pt_regs *regs)
{
/* Set user psw back to home space mode. */
if (user_mode(regs))
......@@ -629,43 +627,7 @@ void __kprobes kernel_stack_overflow(struct pt_regs * regs)
panic("Corrupt kernel stack, can't continue.");
}
/* init is done in lowcore.S and head.S */
void __init trap_init(void)
{
int i;
for (i = 0; i < 128; i++)
pgm_check_table[i] = &default_trap_handler;
pgm_check_table[1] = &illegal_op;
pgm_check_table[2] = &privileged_op;
pgm_check_table[3] = &execute_exception;
pgm_check_table[4] = &do_protection_exception;
pgm_check_table[5] = &addressing_exception;
pgm_check_table[6] = &specification_exception;
pgm_check_table[7] = &data_exception;
pgm_check_table[8] = &overflow_exception;
pgm_check_table[9] = &divide_exception;
pgm_check_table[0x0A] = &overflow_exception;
pgm_check_table[0x0B] = &divide_exception;
pgm_check_table[0x0C] = &hfp_overflow_exception;
pgm_check_table[0x0D] = &hfp_underflow_exception;
pgm_check_table[0x0E] = &hfp_significance_exception;
pgm_check_table[0x0F] = &hfp_divide_exception;
pgm_check_table[0x10] = &do_dat_exception;
pgm_check_table[0x11] = &do_dat_exception;
pgm_check_table[0x12] = &translation_exception;
pgm_check_table[0x13] = &special_op_exception;
#ifdef CONFIG_64BIT
pgm_check_table[0x18] = &transaction_exception;
pgm_check_table[0x38] = &do_asce_exception;
pgm_check_table[0x39] = &do_dat_exception;
pgm_check_table[0x3A] = &do_dat_exception;
pgm_check_table[0x3B] = &do_dat_exception;
#endif /* CONFIG_64BIT */
pgm_check_table[0x15] = &operand_exception;
pgm_check_table[0x1C] = &space_switch_exception;
pgm_check_table[0x1D] = &hfp_sqrt_exception;
/* Enable machine checks early. */
local_mcck_enable();
}
......@@ -2,9 +2,9 @@
# Makefile for the linux s390-specific parts of the memory manager.
#
obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o maccess.o \
page-states.o gup.o extable.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_DEBUG_SET_MODULE_RONX) += pageattr.o
obj-$(CONFIG_S390_PTDUMP) += dump_pagetables.o
obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o maccess.o
obj-y += page-states.o gup.o extable.o pageattr.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_S390_PTDUMP) += dump_pagetables.o
......@@ -150,6 +150,7 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st,
static void walk_pud_level(struct seq_file *m, struct pg_state *st,
pgd_t *pgd, unsigned long addr)
{
unsigned int prot;
pud_t *pud;
int i;
......@@ -157,7 +158,11 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st,
st->current_address = addr;
pud = pud_offset(pgd, addr);
if (!pud_none(*pud))
walk_pmd_level(m, st, pud, addr);
if (pud_large(*pud)) {
prot = pud_val(*pud) & _PAGE_RO;
note_page(m, st, prot, 2);
} else
walk_pmd_level(m, st, pud, addr);
else
note_page(m, st, _PAGE_INVALID, 2);
addr += PUD_SIZE;
......
......@@ -49,15 +49,19 @@
#define VM_FAULT_BADCONTEXT 0x010000
#define VM_FAULT_BADMAP 0x020000
#define VM_FAULT_BADACCESS 0x040000
#define VM_FAULT_SIGNAL 0x080000
#define VM_FAULT_SIGNAL 0x080000
static unsigned long store_indication;
static unsigned long store_indication __read_mostly;
void fault_init(void)
#ifdef CONFIG_64BIT
static int __init fault_init(void)
{
if (test_facility(2) && test_facility(75))
if (test_facility(75))
store_indication = 0xc00;
return 0;
}
early_initcall(fault_init);
#endif
static inline int notify_page_fault(struct pt_regs *regs)
{
......@@ -273,10 +277,16 @@ static inline int do_exception(struct pt_regs *regs, int access)
unsigned int flags;
int fault;
tsk = current;
/*
* The instruction that caused the program check has
* been nullified. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(tsk, TIF_PER_TRAP);
if (notify_page_fault(regs))
return 0;
tsk = current;
mm = tsk->mm;
trans_exc_code = regs->int_parm_long;
......@@ -372,11 +382,6 @@ retry:
goto retry;
}
}
/*
* The instruction that caused the program check will
* be repeated. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(tsk, TIF_PER_TRAP);
fault = 0;
out_up:
up_read(&mm->mmap_sem);
......@@ -423,6 +428,12 @@ void __kprobes do_asce_exception(struct pt_regs *regs)
struct vm_area_struct *vma;
unsigned long trans_exc_code;
/*
* The instruction that caused the program check has
* been nullified. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(current, TIF_PER_TRAP);
trans_exc_code = regs->int_parm_long;
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
goto no_context;
......
......@@ -125,7 +125,6 @@ void __init paging_init(void)
max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
free_area_init_nodes(max_zone_pfns);
fault_init();
}
void __init mem_init(void)
......@@ -159,34 +158,6 @@ void __init mem_init(void)
PFN_ALIGN((unsigned long)&_eshared) - 1);
}
#ifdef CONFIG_DEBUG_PAGEALLOC
void kernel_map_pages(struct page *page, int numpages, int enable)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long address;
int i;
for (i = 0; i < numpages; i++) {
address = page_to_phys(page + i);
pgd = pgd_offset_k(address);
pud = pud_offset(pgd, address);
pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address);
if (!enable) {
__ptep_ipte(address, pte);
pte_val(*pte) = _PAGE_TYPE_EMPTY;
continue;
}
*pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW));
/* Flush cpu write queue. */
mb();
}
}
#endif
void free_init_pages(char *what, unsigned long begin, unsigned long end)
{
unsigned long addr = begin;
......
......@@ -2,11 +2,46 @@
* Copyright IBM Corp. 2011
* Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
*/
#include <linux/hugetlb.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <asm/page.h>
void storage_key_init_range(unsigned long start, unsigned long end)
{
unsigned long boundary, function, size;
while (start < end) {
if (MACHINE_HAS_EDAT2) {
/* set storage keys for a 2GB frame */
function = 0x22000 | PAGE_DEFAULT_KEY;
size = 1UL << 31;
boundary = (start + size) & ~(size - 1);
if (boundary <= end) {
do {
start = pfmf(function, start);
} while (start < boundary);
continue;
}
}
if (MACHINE_HAS_EDAT1) {
/* set storage keys for a 1MB frame */
function = 0x21000 | PAGE_DEFAULT_KEY;
size = 1UL << 20;
boundary = (start + size) & ~(size - 1);
if (boundary <= end) {
do {
start = pfmf(function, start);
} while (start < boundary);
continue;
}
}
page_set_storage_key(start, PAGE_DEFAULT_KEY, 0);
start += PAGE_SIZE;
}
}
static pte_t *walk_page_table(unsigned long addr)
{
......@@ -19,7 +54,7 @@ static pte_t *walk_page_table(unsigned long addr)
if (pgd_none(*pgdp))
return NULL;
pudp = pud_offset(pgdp, addr);
if (pud_none(*pudp))
if (pud_none(*pudp) || pud_large(*pudp))
return NULL;
pmdp = pmd_offset(pudp, addr);
if (pmd_none(*pmdp) || pmd_large(*pmdp))
......@@ -70,3 +105,46 @@ int set_memory_x(unsigned long addr, int numpages)
{
return 0;
}
#ifdef CONFIG_DEBUG_PAGEALLOC
void kernel_map_pages(struct page *page, int numpages, int enable)
{
unsigned long address;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
int i;
for (i = 0; i < numpages; i++) {
address = page_to_phys(page + i);
pgd = pgd_offset_k(address);
pud = pud_offset(pgd, address);
pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address);
if (!enable) {
__ptep_ipte(address, pte);
pte_val(*pte) = _PAGE_TYPE_EMPTY;
continue;
}
*pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW));
}
}
#ifdef CONFIG_HIBERNATION
bool kernel_page_present(struct page *page)
{
unsigned long addr;
int cc;
addr = page_to_phys(page);
asm volatile(
" lra %1,0(%1)\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc), "+a" (addr) : : "cc");
return cc == 0;
}
#endif /* CONFIG_HIBERNATION */
#endif /* CONFIG_DEBUG_PAGEALLOC */
......@@ -881,22 +881,6 @@ int s390_enable_sie(void)
}
EXPORT_SYMBOL_GPL(s390_enable_sie);
#if defined(CONFIG_DEBUG_PAGEALLOC) && defined(CONFIG_HIBERNATION)
bool kernel_page_present(struct page *page)
{
unsigned long addr;
int cc;
addr = page_to_phys(page);
asm volatile(
" lra %1,0(%1)\n"
" ipm %0\n"
" srl %0,28"
: "=d" (cc), "+a" (addr) : : "cc");
return cc == 0;
}
#endif /* CONFIG_HIBERNATION && CONFIG_DEBUG_PAGEALLOC */
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
int pmdp_clear_flush_young(struct vm_area_struct *vma, unsigned long address,
pmd_t *pmdp)
......
......@@ -89,6 +89,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
int ret = -ENOMEM;
while (address < end) {
pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
pu_dir = vmem_pud_alloc();
......@@ -96,18 +97,24 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
goto out;
pgd_populate(&init_mm, pg_dir, pu_dir);
}
pu_dir = pud_offset(pg_dir, address);
#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
if (MACHINE_HAS_EDAT2 && pud_none(*pu_dir) && address &&
!(address & ~PUD_MASK) && (address + PUD_SIZE <= end)) {
pte_val(pte) |= _REGION3_ENTRY_LARGE;
pte_val(pte) |= _REGION_ENTRY_TYPE_R3;
pud_val(*pu_dir) = pte_val(pte);
address += PUD_SIZE;
continue;
}
#endif
if (pud_none(*pu_dir)) {
pm_dir = vmem_pmd_alloc();
if (!pm_dir)
goto out;
pud_populate(&init_mm, pu_dir, pm_dir);
}
pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
pm_dir = pmd_offset(pu_dir, address);
#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
if (MACHINE_HAS_EDAT1 && pmd_none(*pm_dir) && address &&
!(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) {
......@@ -160,6 +167,11 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
address += PUD_SIZE;
continue;
}
if (pud_large(*pu_dir)) {
pud_clear(pu_dir);
address += PUD_SIZE;
continue;
}
pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir)) {
address += PMD_SIZE;
......@@ -193,7 +205,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
start_addr = (unsigned long) start;
end_addr = (unsigned long) (start + nr);
for (address = start_addr; address < end_addr; address += PAGE_SIZE) {
for (address = start_addr; address < end_addr;) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
pu_dir = vmem_pud_alloc();
......@@ -212,10 +224,33 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir)) {
#ifdef CONFIG_64BIT
/* Use 1MB frames for vmemmap if available. We always
* use large frames even if they are only partially
* used.
* Otherwise we would have also page tables since
* vmemmap_populate gets called for each section
* separately. */
if (MACHINE_HAS_EDAT1) {
void *new_page;
new_page = vmemmap_alloc_block(PMD_SIZE, node);
if (!new_page)
goto out;
pte = mk_pte_phys(__pa(new_page), PAGE_RW);
pte_val(pte) |= _SEGMENT_ENTRY_LARGE;
pmd_val(*pm_dir) = pte_val(pte);
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
#endif
pt_dir = vmem_pte_alloc(address);
if (!pt_dir)
goto out;
pmd_populate(&init_mm, pm_dir, pt_dir);
} else if (pmd_large(*pm_dir)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
pt_dir = pte_offset_kernel(pm_dir, address);
......@@ -228,6 +263,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL);
*pt_dir = pte;
}
address += PAGE_SIZE;
}
memset(start, 0, nr * sizeof(struct page));
ret = 0;
......
......@@ -341,6 +341,27 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
/* lr %r5,%r4 */
EMIT2(0x1854);
break;
case BPF_S_ALU_MOD_X: /* A %= X */
jit->seen |= SEEN_XREG | SEEN_RET0;
/* ltr %r12,%r12 */
EMIT2(0x12cc);
/* jz <ret0> */
EMIT4_PCREL(0xa7840000, (jit->ret0_ip - jit->prg));
/* lhi %r4,0 */
EMIT4(0xa7480000);
/* dr %r4,%r12 */
EMIT2(0x1d4c);
/* lr %r5,%r4 */
EMIT2(0x1854);
break;
case BPF_S_ALU_MOD_K: /* A %= K */
/* lhi %r4,0 */
EMIT4(0xa7480000);
/* d %r4,<d(K)>(%r13) */
EMIT4_DISP(0x5d40d000, EMIT_CONST(K));
/* lr %r5,%r4 */
EMIT2(0x1854);
break;
case BPF_S_ALU_AND_X: /* A &= X */
jit->seen |= SEEN_XREG;
/* nr %r5,%r12 */
......@@ -368,10 +389,17 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
EMIT4_DISP(0x5650d000, EMIT_CONST(K));
break;
case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
case BPF_S_ALU_XOR_X:
jit->seen |= SEEN_XREG;
/* xr %r5,%r12 */
EMIT2(0x175c);
break;
case BPF_S_ALU_XOR_K: /* A ^= K */
if (!K)
break;
/* x %r5,<d(K)>(%r13) */
EMIT4_DISP(0x5750d000, EMIT_CONST(K));
break;
case BPF_S_ALU_LSH_X: /* A <<= X; */
jit->seen |= SEEN_XREG;
/* sll %r5,0(%r12) */
......
#
# Makefile for the s390 PCI subsystem.
#
obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_msi.o \
pci_sysfs.o pci_event.o
此差异已折叠。
/*
* Copyright IBM Corp. 2012
*
* Author(s):
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
#define COMPONENT "zPCI"
#define pr_fmt(fmt) COMPONENT ": " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <asm/pci_clp.h>
/*
* Call Logical Processor
* Retry logic is handled by the caller.
*/
static inline u8 clp_instr(void *req)
{
u64 ilpm;
u8 cc;
asm volatile (
" .insn rrf,0xb9a00000,%[ilpm],%[req],0x0,0x2\n"
" ipm %[cc]\n"
" srl %[cc],28\n"
: [cc] "=d" (cc), [ilpm] "=d" (ilpm)
: [req] "a" (req)
: "cc", "memory");
return cc;
}
static void *clp_alloc_block(void)
{
struct page *page = alloc_pages(GFP_KERNEL, get_order(CLP_BLK_SIZE));
return (page) ? page_address(page) : NULL;
}
static void clp_free_block(void *ptr)
{
free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE));
}
static void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
struct clp_rsp_query_pci_grp *response)
{
zdev->tlb_refresh = response->refresh;
zdev->dma_mask = response->dasm;
zdev->msi_addr = response->msia;
pr_debug("Supported number of MSI vectors: %u\n", response->noi);
switch (response->version) {
case 1:
zdev->max_bus_speed = PCIE_SPEED_5_0GT;
break;
default:
zdev->max_bus_speed = PCI_SPEED_UNKNOWN;
break;
}
}
static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid)
{
struct clp_req_rsp_query_pci_grp *rrb;
int rc;
rrb = clp_alloc_block();
if (!rrb)
return -ENOMEM;
memset(rrb, 0, sizeof(*rrb));
rrb->request.hdr.len = sizeof(rrb->request);
rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP;
rrb->response.hdr.len = sizeof(rrb->response);
rrb->request.pfgid = pfgid;
rc = clp_instr(rrb);
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
clp_store_query_pci_fngrp(zdev, &rrb->response);
else {
pr_err("Query PCI FNGRP failed with response: %x cc: %d\n",
rrb->response.hdr.rsp, rc);
rc = -EIO;
}
clp_free_block(rrb);
return rc;
}
static int clp_store_query_pci_fn(struct zpci_dev *zdev,
struct clp_rsp_query_pci *response)
{
int i;
for (i = 0; i < PCI_BAR_COUNT; i++) {
zdev->bars[i].val = le32_to_cpu(response->bar[i]);
zdev->bars[i].size = response->bar_size[i];
}
zdev->start_dma = response->sdma;
zdev->end_dma = response->edma;
zdev->pchid = response->pchid;
zdev->pfgid = response->pfgid;
return 0;
}
static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh)
{
struct clp_req_rsp_query_pci *rrb;
int rc;
rrb = clp_alloc_block();
if (!rrb)
return -ENOMEM;
memset(rrb, 0, sizeof(*rrb));
rrb->request.hdr.len = sizeof(rrb->request);
rrb->request.hdr.cmd = CLP_QUERY_PCI_FN;
rrb->response.hdr.len = sizeof(rrb->response);
rrb->request.fh = fh;
rc = clp_instr(rrb);
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
rc = clp_store_query_pci_fn(zdev, &rrb->response);
if (rc)
goto out;
if (rrb->response.pfgid)
rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid);
} else {
pr_err("Query PCI failed with response: %x cc: %d\n",
rrb->response.hdr.rsp, rc);
rc = -EIO;
}
out:
clp_free_block(rrb);
return rc;
}
int clp_add_pci_device(u32 fid, u32 fh, int configured)
{
struct zpci_dev *zdev;
int rc;
zdev = zpci_alloc_device();
if (IS_ERR(zdev))
return PTR_ERR(zdev);
zdev->fh = fh;
zdev->fid = fid;
/* Query function properties and update zdev */
rc = clp_query_pci_fn(zdev, fh);
if (rc)
goto error;
if (configured)
zdev->state = ZPCI_FN_STATE_CONFIGURED;
else
zdev->state = ZPCI_FN_STATE_STANDBY;
rc = zpci_create_device(zdev);
if (rc)
goto error;
return 0;
error:
zpci_free_device(zdev);
return rc;
}
/*
* Enable/Disable a given PCI function defined by its function handle.
*/
static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
{
struct clp_req_rsp_set_pci *rrb;
int rc, retries = 1000;
rrb = clp_alloc_block();
if (!rrb)
return -ENOMEM;
do {
memset(rrb, 0, sizeof(*rrb));
rrb->request.hdr.len = sizeof(rrb->request);
rrb->request.hdr.cmd = CLP_SET_PCI_FN;
rrb->response.hdr.len = sizeof(rrb->response);
rrb->request.fh = *fh;
rrb->request.oc = command;
rrb->request.ndas = nr_dma_as;
rc = clp_instr(rrb);
if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) {
retries--;
if (retries < 0)
break;
msleep(1);
}
} while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
*fh = rrb->response.fh;
else {
pr_err("Set PCI FN failed with response: %x cc: %d\n",
rrb->response.hdr.rsp, rc);
rc = -EIO;
}
clp_free_block(rrb);
return rc;
}
int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as)
{
u32 fh = zdev->fh;
int rc;
rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
if (!rc)
/* Success -> store enabled handle in zdev */
zdev->fh = fh;
return rc;
}
int clp_disable_fh(struct zpci_dev *zdev)
{
u32 fh = zdev->fh;
int rc;
if (!zdev_enabled(zdev))
return 0;
dev_info(&zdev->pdev->dev, "disabling fn handle: 0x%x\n", fh);
rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN);
if (!rc)
/* Success -> store disabled handle in zdev */
zdev->fh = fh;
else
dev_err(&zdev->pdev->dev,
"Failed to disable fn handle: 0x%x\n", fh);
return rc;
}
static void clp_check_pcifn_entry(struct clp_fh_list_entry *entry)
{
int present, rc;
if (!entry->vendor_id)
return;
/* TODO: be a little bit more scalable */
present = zpci_fid_present(entry->fid);
if (present)
pr_debug("%s: device %x already present\n", __func__, entry->fid);
/* skip already used functions */
if (present && entry->config_state)
return;
/* aev 306: function moved to stand-by state */
if (present && !entry->config_state) {
/*
* The handle is already disabled, that means no iota/irq freeing via
* the firmware interfaces anymore. Need to free resources manually
* (DMA memory, debug, sysfs)...
*/
zpci_stop_device(get_zdev_by_fid(entry->fid));
return;
}
rc = clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
if (rc)
pr_err("Failed to add fid: 0x%x\n", entry->fid);
}
int clp_find_pci_devices(void)
{
struct clp_req_rsp_list_pci *rrb;
u64 resume_token = 0;
int entries, i, rc;
rrb = clp_alloc_block();
if (!rrb)
return -ENOMEM;
do {
memset(rrb, 0, sizeof(*rrb));
rrb->request.hdr.len = sizeof(rrb->request);
rrb->request.hdr.cmd = CLP_LIST_PCI;
/* store as many entries as possible */
rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN;
rrb->request.resume_token = resume_token;
/* Get PCI function handle list */
rc = clp_instr(rrb);
if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
pr_err("List PCI failed with response: 0x%x cc: %d\n",
rrb->response.hdr.rsp, rc);
rc = -EIO;
goto out;
}
WARN_ON_ONCE(rrb->response.entry_size !=
sizeof(struct clp_fh_list_entry));
entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
rrb->response.entry_size;
pr_info("Detected number of PCI functions: %u\n", entries);
/* Store the returned resume token as input for the next call */
resume_token = rrb->response.resume_token;
for (i = 0; i < entries; i++)
clp_check_pcifn_entry(&rrb->response.fh_list[i]);
} while (resume_token);
pr_debug("Maximum number of supported PCI functions: %u\n",
rrb->response.max_fn);
out:
clp_free_block(rrb);
return rc;
}
/*
* Copyright IBM Corp. 2012
*
* Author(s):
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/iommu-helper.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <asm/pci_dma.h>
static enum zpci_ioat_dtype zpci_ioat_dt = ZPCI_IOTA_RTTO;
static struct kmem_cache *dma_region_table_cache;
static struct kmem_cache *dma_page_table_cache;
static unsigned long *dma_alloc_cpu_table(void)
{
unsigned long *table, *entry;
table = kmem_cache_alloc(dma_region_table_cache, GFP_ATOMIC);
if (!table)
return NULL;
for (entry = table; entry < table + ZPCI_TABLE_ENTRIES; entry++)
*entry = ZPCI_TABLE_INVALID | ZPCI_TABLE_PROTECTED;
return table;
}
static void dma_free_cpu_table(void *table)
{
kmem_cache_free(dma_region_table_cache, table);
}
static unsigned long *dma_alloc_page_table(void)
{
unsigned long *table, *entry;
table = kmem_cache_alloc(dma_page_table_cache, GFP_ATOMIC);
if (!table)
return NULL;
for (entry = table; entry < table + ZPCI_PT_ENTRIES; entry++)
*entry = ZPCI_PTE_INVALID | ZPCI_TABLE_PROTECTED;
return table;
}
static void dma_free_page_table(void *table)
{
kmem_cache_free(dma_page_table_cache, table);
}
static unsigned long *dma_get_seg_table_origin(unsigned long *entry)
{
unsigned long *sto;
if (reg_entry_isvalid(*entry))
sto = get_rt_sto(*entry);
else {
sto = dma_alloc_cpu_table();
if (!sto)
return NULL;
set_rt_sto(entry, sto);
validate_rt_entry(entry);
entry_clr_protected(entry);
}
return sto;
}
static unsigned long *dma_get_page_table_origin(unsigned long *entry)
{
unsigned long *pto;
if (reg_entry_isvalid(*entry))
pto = get_st_pto(*entry);
else {
pto = dma_alloc_page_table();
if (!pto)
return NULL;
set_st_pto(entry, pto);
validate_st_entry(entry);
entry_clr_protected(entry);
}
return pto;
}
static unsigned long *dma_walk_cpu_trans(unsigned long *rto, dma_addr_t dma_addr)
{
unsigned long *sto, *pto;
unsigned int rtx, sx, px;
rtx = calc_rtx(dma_addr);
sto = dma_get_seg_table_origin(&rto[rtx]);
if (!sto)
return NULL;
sx = calc_sx(dma_addr);
pto = dma_get_page_table_origin(&sto[sx]);
if (!pto)
return NULL;
px = calc_px(dma_addr);
return &pto[px];
}
static void dma_update_cpu_trans(struct zpci_dev *zdev, void *page_addr,
dma_addr_t dma_addr, int flags)
{
unsigned long *entry;
entry = dma_walk_cpu_trans(zdev->dma_table, dma_addr);
if (!entry) {
WARN_ON_ONCE(1);
return;
}
if (flags & ZPCI_PTE_INVALID) {
invalidate_pt_entry(entry);
return;
} else {
set_pt_pfaa(entry, page_addr);
validate_pt_entry(entry);
}
if (flags & ZPCI_TABLE_PROTECTED)
entry_set_protected(entry);
else
entry_clr_protected(entry);
}
static int dma_update_trans(struct zpci_dev *zdev, unsigned long pa,
dma_addr_t dma_addr, size_t size, int flags)
{
unsigned int nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
u8 *page_addr = (u8 *) (pa & PAGE_MASK);
dma_addr_t start_dma_addr = dma_addr;
unsigned long irq_flags;
int i, rc = 0;
if (!nr_pages)
return -EINVAL;
spin_lock_irqsave(&zdev->dma_table_lock, irq_flags);
if (!zdev->dma_table) {
dev_err(&zdev->pdev->dev, "Missing DMA table\n");
goto no_refresh;
}
for (i = 0; i < nr_pages; i++) {
dma_update_cpu_trans(zdev, page_addr, dma_addr, flags);
page_addr += PAGE_SIZE;
dma_addr += PAGE_SIZE;
}
/*
* rpcit is not required to establish new translations when previously
* invalid translation-table entries are validated, however it is
* required when altering previously valid entries.
*/
if (!zdev->tlb_refresh &&
((flags & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID))
/*
* TODO: also need to check that the old entry is indeed INVALID
* and not only for one page but for the whole range...
* -> now we WARN_ON in that case but with lazy unmap that
* needs to be redone!
*/
goto no_refresh;
rc = rpcit_instr((u64) zdev->fh << 32, start_dma_addr,
nr_pages * PAGE_SIZE);
no_refresh:
spin_unlock_irqrestore(&zdev->dma_table_lock, irq_flags);
return rc;
}
static void dma_free_seg_table(unsigned long entry)
{
unsigned long *sto = get_rt_sto(entry);
int sx;
for (sx = 0; sx < ZPCI_TABLE_ENTRIES; sx++)
if (reg_entry_isvalid(sto[sx]))
dma_free_page_table(get_st_pto(sto[sx]));
dma_free_cpu_table(sto);
}
static void dma_cleanup_tables(struct zpci_dev *zdev)
{
unsigned long *table;
int rtx;
if (!zdev || !zdev->dma_table)
return;
table = zdev->dma_table;
for (rtx = 0; rtx < ZPCI_TABLE_ENTRIES; rtx++)
if (reg_entry_isvalid(table[rtx]))
dma_free_seg_table(table[rtx]);
dma_free_cpu_table(table);
zdev->dma_table = NULL;
}
static unsigned long __dma_alloc_iommu(struct zpci_dev *zdev, unsigned long start,
int size)
{
unsigned long boundary_size = 0x1000000;
return iommu_area_alloc(zdev->iommu_bitmap, zdev->iommu_pages,
start, size, 0, boundary_size, 0);
}
static unsigned long dma_alloc_iommu(struct zpci_dev *zdev, int size)
{
unsigned long offset, flags;
spin_lock_irqsave(&zdev->iommu_bitmap_lock, flags);
offset = __dma_alloc_iommu(zdev, zdev->next_bit, size);
if (offset == -1)
offset = __dma_alloc_iommu(zdev, 0, size);
if (offset != -1) {
zdev->next_bit = offset + size;
if (zdev->next_bit >= zdev->iommu_pages)
zdev->next_bit = 0;
}
spin_unlock_irqrestore(&zdev->iommu_bitmap_lock, flags);
return offset;
}
static void dma_free_iommu(struct zpci_dev *zdev, unsigned long offset, int size)
{
unsigned long flags;
spin_lock_irqsave(&zdev->iommu_bitmap_lock, flags);
if (!zdev->iommu_bitmap)
goto out;
bitmap_clear(zdev->iommu_bitmap, offset, size);
if (offset >= zdev->next_bit)
zdev->next_bit = offset + size;
out:
spin_unlock_irqrestore(&zdev->iommu_bitmap_lock, flags);
}
int dma_set_mask(struct device *dev, u64 mask)
{
if (!dev->dma_mask || !dma_supported(dev, mask))
return -EIO;
*dev->dma_mask = mask;
return 0;
}
EXPORT_SYMBOL_GPL(dma_set_mask);
static dma_addr_t s390_dma_map_pages(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev));
unsigned long nr_pages, iommu_page_index;
unsigned long pa = page_to_phys(page) + offset;
int flags = ZPCI_PTE_VALID;
dma_addr_t dma_addr;
WARN_ON_ONCE(offset > PAGE_SIZE);
/* This rounds up number of pages based on size and offset */
nr_pages = iommu_num_pages(pa, size, PAGE_SIZE);
iommu_page_index = dma_alloc_iommu(zdev, nr_pages);
if (iommu_page_index == -1)
goto out_err;
/* Use rounded up size */
size = nr_pages * PAGE_SIZE;
dma_addr = zdev->start_dma + iommu_page_index * PAGE_SIZE;
if (dma_addr + size > zdev->end_dma) {
dev_err(dev, "(dma_addr: 0x%16.16LX + size: 0x%16.16lx) > end_dma: 0x%16.16Lx\n",
dma_addr, size, zdev->end_dma);
goto out_free;
}
if (direction == DMA_NONE || direction == DMA_TO_DEVICE)
flags |= ZPCI_TABLE_PROTECTED;
if (!dma_update_trans(zdev, pa, dma_addr, size, flags))
return dma_addr + offset;
out_free:
dma_free_iommu(zdev, iommu_page_index, nr_pages);
out_err:
dev_err(dev, "Failed to map addr: %lx\n", pa);
return DMA_ERROR_CODE;
}
static void s390_dma_unmap_pages(struct device *dev, dma_addr_t dma_addr,
size_t size, enum dma_data_direction direction,
struct dma_attrs *attrs)
{
struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev));
unsigned long iommu_page_index;
int npages;
npages = iommu_num_pages(dma_addr, size, PAGE_SIZE);
dma_addr = dma_addr & PAGE_MASK;
if (dma_update_trans(zdev, 0, dma_addr, npages * PAGE_SIZE,
ZPCI_TABLE_PROTECTED | ZPCI_PTE_INVALID))
dev_err(dev, "Failed to unmap addr: %Lx\n", dma_addr);
iommu_page_index = (dma_addr - zdev->start_dma) >> PAGE_SHIFT;
dma_free_iommu(zdev, iommu_page_index, npages);
}
static void *s390_dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag,
struct dma_attrs *attrs)
{
struct page *page;
unsigned long pa;
dma_addr_t map;
size = PAGE_ALIGN(size);
page = alloc_pages(flag, get_order(size));
if (!page)
return NULL;
pa = page_to_phys(page);
memset((void *) pa, 0, size);
map = s390_dma_map_pages(dev, page, pa % PAGE_SIZE,
size, DMA_BIDIRECTIONAL, NULL);
if (dma_mapping_error(dev, map)) {
free_pages(pa, get_order(size));
return NULL;
}
if (dma_handle)
*dma_handle = map;
return (void *) pa;
}
static void s390_dma_free(struct device *dev, size_t size,
void *pa, dma_addr_t dma_handle,
struct dma_attrs *attrs)
{
s390_dma_unmap_pages(dev, dma_handle, PAGE_ALIGN(size),
DMA_BIDIRECTIONAL, NULL);
free_pages((unsigned long) pa, get_order(size));
}
static int s390_dma_map_sg(struct device *dev, struct scatterlist *sg,
int nr_elements, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
int mapped_elements = 0;
struct scatterlist *s;
int i;
for_each_sg(sg, s, nr_elements, i) {
struct page *page = sg_page(s);
s->dma_address = s390_dma_map_pages(dev, page, s->offset,
s->length, dir, NULL);
if (!dma_mapping_error(dev, s->dma_address)) {
s->dma_length = s->length;
mapped_elements++;
} else
goto unmap;
}
out:
return mapped_elements;
unmap:
for_each_sg(sg, s, mapped_elements, i) {
if (s->dma_address)
s390_dma_unmap_pages(dev, s->dma_address, s->dma_length,
dir, NULL);
s->dma_address = 0;
s->dma_length = 0;
}
mapped_elements = 0;
goto out;
}
static void s390_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
int nr_elements, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
struct scatterlist *s;
int i;
for_each_sg(sg, s, nr_elements, i) {
s390_dma_unmap_pages(dev, s->dma_address, s->dma_length, dir, NULL);
s->dma_address = 0;
s->dma_length = 0;
}
}
int zpci_dma_init_device(struct zpci_dev *zdev)
{
unsigned int bitmap_order;
int rc;
spin_lock_init(&zdev->iommu_bitmap_lock);
spin_lock_init(&zdev->dma_table_lock);
zdev->dma_table = dma_alloc_cpu_table();
if (!zdev->dma_table) {
rc = -ENOMEM;
goto out_clean;
}
zdev->iommu_size = (unsigned long) high_memory - PAGE_OFFSET;
zdev->iommu_pages = zdev->iommu_size >> PAGE_SHIFT;
bitmap_order = get_order(zdev->iommu_pages / 8);
pr_info("iommu_size: 0x%lx iommu_pages: 0x%lx bitmap_order: %i\n",
zdev->iommu_size, zdev->iommu_pages, bitmap_order);
zdev->iommu_bitmap = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
bitmap_order);
if (!zdev->iommu_bitmap) {
rc = -ENOMEM;
goto out_reg;
}
rc = zpci_register_ioat(zdev,
0,
zdev->start_dma + PAGE_OFFSET,
zdev->start_dma + zdev->iommu_size - 1,
(u64) zdev->dma_table);
if (rc)
goto out_reg;
return 0;
out_reg:
dma_free_cpu_table(zdev->dma_table);
out_clean:
return rc;
}
void zpci_dma_exit_device(struct zpci_dev *zdev)
{
zpci_unregister_ioat(zdev, 0);
dma_cleanup_tables(zdev);
free_pages((unsigned long) zdev->iommu_bitmap,
get_order(zdev->iommu_pages / 8));
zdev->iommu_bitmap = NULL;
zdev->next_bit = 0;
}
static int __init dma_alloc_cpu_table_caches(void)
{
dma_region_table_cache = kmem_cache_create("PCI_DMA_region_tables",
ZPCI_TABLE_SIZE, ZPCI_TABLE_ALIGN,
0, NULL);
if (!dma_region_table_cache)
return -ENOMEM;
dma_page_table_cache = kmem_cache_create("PCI_DMA_page_tables",
ZPCI_PT_SIZE, ZPCI_PT_ALIGN,
0, NULL);
if (!dma_page_table_cache) {
kmem_cache_destroy(dma_region_table_cache);
return -ENOMEM;
}
return 0;
}
int __init zpci_dma_init(void)
{
return dma_alloc_cpu_table_caches();
}
void zpci_dma_exit(void)
{
kmem_cache_destroy(dma_page_table_cache);
kmem_cache_destroy(dma_region_table_cache);
}
#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
static int __init dma_debug_do_init(void)
{
dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
return 0;
}
fs_initcall(dma_debug_do_init);
struct dma_map_ops s390_dma_ops = {
.alloc = s390_dma_alloc,
.free = s390_dma_free,
.map_sg = s390_dma_map_sg,
.unmap_sg = s390_dma_unmap_sg,
.map_page = s390_dma_map_pages,
.unmap_page = s390_dma_unmap_pages,
/* if we support direct DMA this must be conditional */
.is_phys = 0,
/* dma_supported is unconditionally true without a callback */
};
EXPORT_SYMBOL_GPL(s390_dma_ops);
/*
* Copyright IBM Corp. 2012
*
* Author(s):
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
#define COMPONENT "zPCI"
#define pr_fmt(fmt) COMPONENT ": " fmt
#include <linux/kernel.h>
#include <linux/pci.h>
/* Content Code Description for PCI Function Error */
struct zpci_ccdf_err {
u32 reserved1;
u32 fh; /* function handle */
u32 fid; /* function id */
u32 ett : 4; /* expected table type */
u32 mvn : 12; /* MSI vector number */
u32 dmaas : 8; /* DMA address space */
u32 : 6;
u32 q : 1; /* event qualifier */
u32 rw : 1; /* read/write */
u64 faddr; /* failing address */
u32 reserved3;
u16 reserved4;
u16 pec; /* PCI event code */
} __packed;
/* Content Code Description for PCI Function Availability */
struct zpci_ccdf_avail {
u32 reserved1;
u32 fh; /* function handle */
u32 fid; /* function id */
u32 reserved2;
u32 reserved3;
u32 reserved4;
u32 reserved5;
u16 reserved6;
u16 pec; /* PCI event code */
} __packed;
static void zpci_event_log_err(struct zpci_ccdf_err *ccdf)
{
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
dev_err(&zdev->pdev->dev, "event code: 0x%x\n", ccdf->pec);
}
static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf)
{
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
pr_err("%s%s: availability event: fh: 0x%x fid: 0x%x event code: 0x%x reason:",
(zdev) ? dev_driver_string(&zdev->pdev->dev) : "?",
(zdev) ? dev_name(&zdev->pdev->dev) : "?",
ccdf->fh, ccdf->fid, ccdf->pec);
print_hex_dump(KERN_CONT, "ccdf", DUMP_PREFIX_OFFSET,
16, 1, ccdf, sizeof(*ccdf), false);
switch (ccdf->pec) {
case 0x0301:
zpci_enable_device(zdev);
break;
case 0x0302:
clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
break;
case 0x0306:
clp_find_pci_devices();
break;
default:
break;
}
}
void zpci_event_error(void *data)
{
struct zpci_ccdf_err *ccdf = data;
struct zpci_dev *zdev;
zpci_event_log_err(ccdf);
zdev = get_zdev_by_fid(ccdf->fid);
if (!zdev) {
pr_err("Error event for unknown fid: %x", ccdf->fid);
return;
}
}
void zpci_event_availability(void *data)
{
zpci_event_log_avail(data);
}
此差异已折叠。
此差异已折叠。
config VGA_ARB
bool "VGA Arbitration" if EXPERT
default y
depends on PCI
depends on (PCI && !S390)
help
Some "legacy" VGA devices implemented on PCI typically have the same
hard-decoded addresses as they did on ISA. When multiple PCI devices
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册