Commit d91d66e8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc

Pull powerpc fixes and cleanups from Ben Herrenschmidt:
 "Here are a handful or two of powerpc fixes and simple/trivial
  cleanups.  A bunch of them fix ftrace with the new ABI v2 in Little
  Endian, the rest is a scattering of fairly simple things"

* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc:
  powerpc: Don't skip ePAPR spin-table CPUs
  powerpc/module: Fix TOC symbol CRC
  powerpc/powernv: Remove OPAL v1 takeover
  powerpc/kmemleak: Do not scan the DART table
  selftests/powerpc: Use the test harness for the TM DSCR test
  powerpc/cell: cbe_thermal.c: Cleaning up a variable is of the wrong type
  powerpc/kprobes: Fix jprobes on ABI v2 (LE)
  powerpc/ftrace: Use pr_fmt() to namespace error messages
  powerpc/ftrace: Fix nop of modules on 64bit LE (ABIv2)
  powerpc/ftrace: Fix inverted check of create_branch()
  powerpc/ftrace: Fix typo in mask of opcode
  powerpc: Add ppc_global_function_entry()
  powerpc/macintosh/smu.c: Fix closing brace followed by if
  powerpc: Remove __arch_swab*
  powerpc: Remove ancient DEBUG_SIG code
  powerpc/kerenl: Enable EEH for IO accessors
parents 07f4695c 6663a4fa
...@@ -303,7 +303,6 @@ config PPC_EARLY_DEBUG_OPAL_VTERMNO ...@@ -303,7 +303,6 @@ config PPC_EARLY_DEBUG_OPAL_VTERMNO
This correspond to which /dev/hvcN you want to use for early This correspond to which /dev/hvcN you want to use for early
debug. debug.
On OPAL v1 (takeover) this should always be 0
On OPAL v2, this will be 0 for network console and 1 or 2 for On OPAL v2, this will be 0 for network console and 1 or 2 for
the machine built-in serial ports. the machine built-in serial ports.
......
...@@ -88,4 +88,15 @@ static inline unsigned long ppc_function_entry(void *func) ...@@ -88,4 +88,15 @@ static inline unsigned long ppc_function_entry(void *func)
#endif #endif
} }
static inline unsigned long ppc_global_function_entry(void *func)
{
#if defined(CONFIG_PPC64) && defined(_CALL_ELF) && _CALL_ELF == 2
/* PPC64 ABIv2 the global entry point is at the address */
return (unsigned long)func;
#else
/* All other cases there is no change vs ppc_function_entry() */
return ppc_function_entry(func);
#endif
}
#endif /* _ASM_POWERPC_CODE_PATCHING_H */ #endif /* _ASM_POWERPC_CODE_PATCHING_H */
...@@ -12,27 +12,7 @@ ...@@ -12,27 +12,7 @@
#ifndef __OPAL_H #ifndef __OPAL_H
#define __OPAL_H #define __OPAL_H
/****** Takeover interface ********/
/* PAPR H-Call used to querty the HAL existence and/or instanciate
* it from within pHyp (tech preview only).
*
* This is exclusively used in prom_init.c
*/
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
struct opal_takeover_args {
u64 k_image; /* r4 */
u64 k_size; /* r5 */
u64 k_entry; /* r6 */
u64 k_entry2; /* r7 */
u64 hal_addr; /* r8 */
u64 rd_image; /* r9 */
u64 rd_size; /* r10 */
u64 rd_loc; /* r11 */
};
/* /*
* SG entry * SG entry
* *
...@@ -55,15 +35,6 @@ struct opal_sg_list { ...@@ -55,15 +35,6 @@ struct opal_sg_list {
/* We calculate number of sg entries based on PAGE_SIZE */ /* We calculate number of sg entries based on PAGE_SIZE */
#define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry)) #define SG_ENTRIES_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct opal_sg_entry))
extern long opal_query_takeover(u64 *hal_size, u64 *hal_align);
extern long opal_do_takeover(struct opal_takeover_args *args);
struct rtas_args;
extern int opal_enter_rtas(struct rtas_args *args,
unsigned long data,
unsigned long entry);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
/****** OPAL APIs ******/ /****** OPAL APIs ******/
......
...@@ -9,10 +9,6 @@ ...@@ -9,10 +9,6 @@
#include <uapi/asm/swab.h> #include <uapi/asm/swab.h>
#ifdef __GNUC__
#ifndef __powerpc64__
#endif /* __powerpc64__ */
static __inline__ __u16 ld_le16(const volatile __u16 *addr) static __inline__ __u16 ld_le16(const volatile __u16 *addr)
{ {
__u16 val; __u16 val;
...@@ -20,19 +16,12 @@ static __inline__ __u16 ld_le16(const volatile __u16 *addr) ...@@ -20,19 +16,12 @@ static __inline__ __u16 ld_le16(const volatile __u16 *addr)
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr));
return val; return val;
} }
#define __arch_swab16p ld_le16
static __inline__ void st_le16(volatile __u16 *addr, const __u16 val) static __inline__ void st_le16(volatile __u16 *addr, const __u16 val)
{ {
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
} }
static inline void __arch_swab16s(__u16 *addr)
{
st_le16(addr, *addr);
}
#define __arch_swab16s __arch_swab16s
static __inline__ __u32 ld_le32(const volatile __u32 *addr) static __inline__ __u32 ld_le32(const volatile __u32 *addr)
{ {
__u32 val; __u32 val;
...@@ -40,42 +29,10 @@ static __inline__ __u32 ld_le32(const volatile __u32 *addr) ...@@ -40,42 +29,10 @@ static __inline__ __u32 ld_le32(const volatile __u32 *addr)
__asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr)); __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr));
return val; return val;
} }
#define __arch_swab32p ld_le32
static __inline__ void st_le32(volatile __u32 *addr, const __u32 val) static __inline__ void st_le32(volatile __u32 *addr, const __u32 val)
{ {
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr)); __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
} }
static inline void __arch_swab32s(__u32 *addr)
{
st_le32(addr, *addr);
}
#define __arch_swab32s __arch_swab32s
static inline __attribute_const__ __u16 __arch_swab16(__u16 value)
{
__u16 result;
__asm__("rlwimi %0,%1,8,16,23"
: "=r" (result)
: "r" (value), "0" (value >> 8));
return result;
}
#define __arch_swab16 __arch_swab16
static inline __attribute_const__ __u32 __arch_swab32(__u32 value)
{
__u32 result;
__asm__("rlwimi %0,%1,24,16,23\n\t"
"rlwimi %0,%1,8,8,15\n\t"
"rlwimi %0,%1,24,0,7"
: "=r" (result)
: "r" (value), "0" (value >> 24));
return result;
}
#define __arch_swab32 __arch_swab32
#endif /* __GNUC__ */
#endif /* _ASM_POWERPC_SWAB_H */ #endif /* _ASM_POWERPC_SWAB_H */
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
* *
*/ */
#define pr_fmt(fmt) "ftrace-powerpc: " fmt
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -105,7 +107,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -105,7 +107,7 @@ __ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr) struct dyn_ftrace *rec, unsigned long addr)
{ {
unsigned int op; unsigned int op;
unsigned long ptr; unsigned long entry, ptr;
unsigned long ip = rec->ip; unsigned long ip = rec->ip;
void *tramp; void *tramp;
...@@ -115,7 +117,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -115,7 +117,7 @@ __ftrace_make_nop(struct module *mod,
/* Make sure that that this is still a 24bit jump */ /* Make sure that that this is still a 24bit jump */
if (!is_bl_op(op)) { if (!is_bl_op(op)) {
printk(KERN_ERR "Not expected bl: opcode is %x\n", op); pr_err("Not expected bl: opcode is %x\n", op);
return -EINVAL; return -EINVAL;
} }
...@@ -125,21 +127,21 @@ __ftrace_make_nop(struct module *mod, ...@@ -125,21 +127,21 @@ __ftrace_make_nop(struct module *mod,
pr_devel("ip:%lx jumps to %p", ip, tramp); pr_devel("ip:%lx jumps to %p", ip, tramp);
if (!is_module_trampoline(tramp)) { if (!is_module_trampoline(tramp)) {
printk(KERN_ERR "Not a trampoline\n"); pr_err("Not a trampoline\n");
return -EINVAL; return -EINVAL;
} }
if (module_trampoline_target(mod, tramp, &ptr)) { if (module_trampoline_target(mod, tramp, &ptr)) {
printk(KERN_ERR "Failed to get trampoline target\n"); pr_err("Failed to get trampoline target\n");
return -EFAULT; return -EFAULT;
} }
pr_devel("trampoline target %lx", ptr); pr_devel("trampoline target %lx", ptr);
entry = ppc_global_function_entry((void *)addr);
/* This should match what was called */ /* This should match what was called */
if (ptr != ppc_function_entry((void *)addr)) { if (ptr != entry) {
printk(KERN_ERR "addr %lx does not match expected %lx\n", pr_err("addr %lx does not match expected %lx\n", ptr, entry);
ptr, ppc_function_entry((void *)addr));
return -EINVAL; return -EINVAL;
} }
...@@ -179,7 +181,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -179,7 +181,7 @@ __ftrace_make_nop(struct module *mod,
/* Make sure that that this is still a 24bit jump */ /* Make sure that that this is still a 24bit jump */
if (!is_bl_op(op)) { if (!is_bl_op(op)) {
printk(KERN_ERR "Not expected bl: opcode is %x\n", op); pr_err("Not expected bl: opcode is %x\n", op);
return -EINVAL; return -EINVAL;
} }
...@@ -198,7 +200,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -198,7 +200,7 @@ __ftrace_make_nop(struct module *mod,
/* Find where the trampoline jumps to */ /* Find where the trampoline jumps to */
if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) {
printk(KERN_ERR "Failed to read %lx\n", tramp); pr_err("Failed to read %lx\n", tramp);
return -EFAULT; return -EFAULT;
} }
...@@ -209,7 +211,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -209,7 +211,7 @@ __ftrace_make_nop(struct module *mod,
((jmp[1] & 0xffff0000) != 0x398c0000) || ((jmp[1] & 0xffff0000) != 0x398c0000) ||
(jmp[2] != 0x7d8903a6) || (jmp[2] != 0x7d8903a6) ||
(jmp[3] != 0x4e800420)) { (jmp[3] != 0x4e800420)) {
printk(KERN_ERR "Not a trampoline\n"); pr_err("Not a trampoline\n");
return -EINVAL; return -EINVAL;
} }
...@@ -221,8 +223,7 @@ __ftrace_make_nop(struct module *mod, ...@@ -221,8 +223,7 @@ __ftrace_make_nop(struct module *mod,
pr_devel(" %lx ", tramp); pr_devel(" %lx ", tramp);
if (tramp != addr) { if (tramp != addr) {
printk(KERN_ERR pr_err("Trampoline location %08lx does not match addr\n",
"Trampoline location %08lx does not match addr\n",
tramp); tramp);
return -EINVAL; return -EINVAL;
} }
...@@ -263,15 +264,13 @@ int ftrace_make_nop(struct module *mod, ...@@ -263,15 +264,13 @@ int ftrace_make_nop(struct module *mod,
*/ */
if (!rec->arch.mod) { if (!rec->arch.mod) {
if (!mod) { if (!mod) {
printk(KERN_ERR "No module loaded addr=%lx\n", pr_err("No module loaded addr=%lx\n", addr);
addr);
return -EFAULT; return -EFAULT;
} }
rec->arch.mod = mod; rec->arch.mod = mod;
} else if (mod) { } else if (mod) {
if (mod != rec->arch.mod) { if (mod != rec->arch.mod) {
printk(KERN_ERR pr_err("Record mod %p not equal to passed in mod %p\n",
"Record mod %p not equal to passed in mod %p\n",
rec->arch.mod, mod); rec->arch.mod, mod);
return -EINVAL; return -EINVAL;
} }
...@@ -307,26 +306,25 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -307,26 +306,25 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
* The load offset is different depending on the ABI. For simplicity * The load offset is different depending on the ABI. For simplicity
* just mask it out when doing the compare. * just mask it out when doing the compare.
*/ */
if ((op[0] != 0x48000008) || ((op[1] & 0xffff00000) != 0xe8410000)) { if ((op[0] != 0x48000008) || ((op[1] & 0xffff0000) != 0xe8410000)) {
printk(KERN_ERR "Unexpected call sequence: %x %x\n", pr_err("Unexpected call sequence: %x %x\n", op[0], op[1]);
op[0], op[1]);
return -EINVAL; return -EINVAL;
} }
/* If we never set up a trampoline to ftrace_caller, then bail */ /* If we never set up a trampoline to ftrace_caller, then bail */
if (!rec->arch.mod->arch.tramp) { if (!rec->arch.mod->arch.tramp) {
printk(KERN_ERR "No ftrace trampoline\n"); pr_err("No ftrace trampoline\n");
return -EINVAL; return -EINVAL;
} }
/* Ensure branch is within 24 bits */ /* Ensure branch is within 24 bits */
if (create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { if (!create_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) {
printk(KERN_ERR "Branch out of range"); pr_err("Branch out of range\n");
return -EINVAL; return -EINVAL;
} }
if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) { if (patch_branch(ip, rec->arch.mod->arch.tramp, BRANCH_SET_LINK)) {
printk(KERN_ERR "REL24 out of range!\n"); pr_err("REL24 out of range!\n");
return -EINVAL; return -EINVAL;
} }
...@@ -345,13 +343,13 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -345,13 +343,13 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
/* It should be pointing to a nop */ /* It should be pointing to a nop */
if (op != PPC_INST_NOP) { if (op != PPC_INST_NOP) {
printk(KERN_ERR "Expected NOP but have %x\n", op); pr_err("Expected NOP but have %x\n", op);
return -EINVAL; return -EINVAL;
} }
/* If we never set up a trampoline to ftrace_caller, then bail */ /* If we never set up a trampoline to ftrace_caller, then bail */
if (!rec->arch.mod->arch.tramp) { if (!rec->arch.mod->arch.tramp) {
printk(KERN_ERR "No ftrace trampoline\n"); pr_err("No ftrace trampoline\n");
return -EINVAL; return -EINVAL;
} }
...@@ -359,7 +357,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -359,7 +357,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
op = create_branch((unsigned int *)ip, op = create_branch((unsigned int *)ip,
rec->arch.mod->arch.tramp, BRANCH_SET_LINK); rec->arch.mod->arch.tramp, BRANCH_SET_LINK);
if (!op) { if (!op) {
printk(KERN_ERR "REL24 out of range!\n"); pr_err("REL24 out of range!\n");
return -EINVAL; return -EINVAL;
} }
...@@ -397,7 +395,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) ...@@ -397,7 +395,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
* already have a module defined. * already have a module defined.
*/ */
if (!rec->arch.mod) { if (!rec->arch.mod) {
printk(KERN_ERR "No module loaded\n"); pr_err("No module loaded\n");
return -EINVAL; return -EINVAL;
} }
......
...@@ -23,7 +23,7 @@ unsigned int ioread16(void __iomem *addr) ...@@ -23,7 +23,7 @@ unsigned int ioread16(void __iomem *addr)
} }
unsigned int ioread16be(void __iomem *addr) unsigned int ioread16be(void __iomem *addr)
{ {
return in_be16(addr); return readw_be(addr);
} }
unsigned int ioread32(void __iomem *addr) unsigned int ioread32(void __iomem *addr)
{ {
...@@ -31,7 +31,7 @@ unsigned int ioread32(void __iomem *addr) ...@@ -31,7 +31,7 @@ unsigned int ioread32(void __iomem *addr)
} }
unsigned int ioread32be(void __iomem *addr) unsigned int ioread32be(void __iomem *addr)
{ {
return in_be32(addr); return readl_be(addr);
} }
EXPORT_SYMBOL(ioread8); EXPORT_SYMBOL(ioread8);
EXPORT_SYMBOL(ioread16); EXPORT_SYMBOL(ioread16);
...@@ -49,7 +49,7 @@ void iowrite16(u16 val, void __iomem *addr) ...@@ -49,7 +49,7 @@ void iowrite16(u16 val, void __iomem *addr)
} }
void iowrite16be(u16 val, void __iomem *addr) void iowrite16be(u16 val, void __iomem *addr)
{ {
out_be16(addr, val); writew_be(val, addr);
} }
void iowrite32(u32 val, void __iomem *addr) void iowrite32(u32 val, void __iomem *addr)
{ {
...@@ -57,7 +57,7 @@ void iowrite32(u32 val, void __iomem *addr) ...@@ -57,7 +57,7 @@ void iowrite32(u32 val, void __iomem *addr)
} }
void iowrite32be(u32 val, void __iomem *addr) void iowrite32be(u32 val, void __iomem *addr)
{ {
out_be32(addr, val); writel_be(val, addr);
} }
EXPORT_SYMBOL(iowrite8); EXPORT_SYMBOL(iowrite8);
EXPORT_SYMBOL(iowrite16); EXPORT_SYMBOL(iowrite16);
...@@ -75,15 +75,15 @@ EXPORT_SYMBOL(iowrite32be); ...@@ -75,15 +75,15 @@ EXPORT_SYMBOL(iowrite32be);
*/ */
void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) void ioread8_rep(void __iomem *addr, void *dst, unsigned long count)
{ {
_insb((u8 __iomem *) addr, dst, count); readsb(addr, dst, count);
} }
void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) void ioread16_rep(void __iomem *addr, void *dst, unsigned long count)
{ {
_insw_ns((u16 __iomem *) addr, dst, count); readsw(addr, dst, count);
} }
void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) void ioread32_rep(void __iomem *addr, void *dst, unsigned long count)
{ {
_insl_ns((u32 __iomem *) addr, dst, count); readsl(addr, dst, count);
} }
EXPORT_SYMBOL(ioread8_rep); EXPORT_SYMBOL(ioread8_rep);
EXPORT_SYMBOL(ioread16_rep); EXPORT_SYMBOL(ioread16_rep);
...@@ -91,15 +91,15 @@ EXPORT_SYMBOL(ioread32_rep); ...@@ -91,15 +91,15 @@ EXPORT_SYMBOL(ioread32_rep);
void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count)
{ {
_outsb((u8 __iomem *) addr, src, count); writesb(addr, src, count);
} }
void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count)
{ {
_outsw_ns((u16 __iomem *) addr, src, count); writesw(addr, src, count);
} }
void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count)
{ {
_outsl_ns((u32 __iomem *) addr, src, count); writesl(addr, src, count);
} }
EXPORT_SYMBOL(iowrite8_rep); EXPORT_SYMBOL(iowrite8_rep);
EXPORT_SYMBOL(iowrite16_rep); EXPORT_SYMBOL(iowrite16_rep);
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/code-patching.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/sstep.h> #include <asm/sstep.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -491,12 +492,10 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, ...@@ -491,12 +492,10 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
return ret; return ret;
} }
#ifdef CONFIG_PPC64
unsigned long arch_deref_entry_point(void *entry) unsigned long arch_deref_entry_point(void *entry)
{ {
return ((func_descr_t *)entry)->entry; return ppc_global_function_entry(entry);
} }
#endif
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
{ {
...@@ -508,7 +507,11 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) ...@@ -508,7 +507,11 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
/* setup return addr to the jprobe handler routine */ /* setup return addr to the jprobe handler routine */
regs->nip = arch_deref_entry_point(jp->entry); regs->nip = arch_deref_entry_point(jp->entry);
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
#if defined(_CALL_ELF) && _CALL_ELF == 2
regs->gpr[12] = (unsigned long)jp->entry;
#else
regs->gpr[2] = (unsigned long)(((func_descr_t *)jp->entry)->toc); regs->gpr[2] = (unsigned long)(((func_descr_t *)jp->entry)->toc);
#endif
#endif #endif
return 1; return 1;
......
...@@ -315,8 +315,17 @@ static void dedotify_versions(struct modversion_info *vers, ...@@ -315,8 +315,17 @@ static void dedotify_versions(struct modversion_info *vers,
struct modversion_info *end; struct modversion_info *end;
for (end = (void *)vers + size; vers < end; vers++) for (end = (void *)vers + size; vers < end; vers++)
if (vers->name[0] == '.') if (vers->name[0] == '.') {
memmove(vers->name, vers->name+1, strlen(vers->name)); memmove(vers->name, vers->name+1, strlen(vers->name));
#ifdef ARCH_RELOCATES_KCRCTAB
/* The TOC symbol has no CRC computed. To avoid CRC
* check failing, we must force it to the expected
* value (see CRC check in module.c).
*/
if (!strcmp(vers->name, "TOC."))
vers->crc = -(unsigned long)reloc_start;
#endif
}
} }
/* Undefined symbols which refer to .funcname, hack to funcname (or .TOC.) */ /* Undefined symbols which refer to .funcname, hack to funcname (or .TOC.) */
......
...@@ -662,13 +662,6 @@ void __init early_init_devtree(void *params) ...@@ -662,13 +662,6 @@ void __init early_init_devtree(void *params)
of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL); of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL);
#endif #endif
/* Pre-initialize the cmd_line with the content of boot_commmand_line,
* which will be empty except when the content of the variable has
* been overriden by a bootloading mechanism. This happens typically
* with HAL takeover
*/
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
/* Retrieve various informations from the /chosen node of the /* Retrieve various informations from the /chosen node of the
* device-tree, including the platform type, initrd location and * device-tree, including the platform type, initrd location and
* size, TCE reserve, and more ... * size, TCE reserve, and more ...
......
...@@ -1268,201 +1268,6 @@ static u64 __initdata prom_opal_base; ...@@ -1268,201 +1268,6 @@ static u64 __initdata prom_opal_base;
static u64 __initdata prom_opal_entry; static u64 __initdata prom_opal_entry;
#endif #endif
#ifdef __BIG_ENDIAN__
/* XXX Don't change this structure without updating opal-takeover.S */
static struct opal_secondary_data {
s64 ack; /* 0 */
u64 go; /* 8 */
struct opal_takeover_args args; /* 16 */
} opal_secondary_data;
static u64 __initdata prom_opal_align;
static u64 __initdata prom_opal_size;
static int __initdata prom_rtas_start_cpu;
static u64 __initdata prom_rtas_data;
static u64 __initdata prom_rtas_entry;
extern char opal_secondary_entry;
static void __init prom_query_opal(void)
{
long rc;
/* We must not query for OPAL presence on a machine that
* supports TNK takeover (970 blades), as this uses the same
* h-call with different arguments and will crash
*/
if (PHANDLE_VALID(call_prom("finddevice", 1, 1,
ADDR("/tnk-memory-map")))) {
prom_printf("TNK takeover detected, skipping OPAL check\n");
return;
}
prom_printf("Querying for OPAL presence... ");
rc = opal_query_takeover(&prom_opal_size,
&prom_opal_align);
prom_debug("(rc = %ld) ", rc);
if (rc != 0) {
prom_printf("not there.\n");
return;
}
of_platform = PLATFORM_OPAL;
prom_printf(" there !\n");
prom_debug(" opal_size = 0x%lx\n", prom_opal_size);
prom_debug(" opal_align = 0x%lx\n", prom_opal_align);
if (prom_opal_align < 0x10000)
prom_opal_align = 0x10000;
}
static int __init prom_rtas_call(int token, int nargs, int nret,
int *outputs, ...)
{
struct rtas_args rtas_args;
va_list list;
int i;
rtas_args.token = token;
rtas_args.nargs = nargs;
rtas_args.nret = nret;
rtas_args.rets = (rtas_arg_t *)&(rtas_args.args[nargs]);
va_start(list, outputs);
for (i = 0; i < nargs; ++i)
rtas_args.args[i] = va_arg(list, rtas_arg_t);
va_end(list);
for (i = 0; i < nret; ++i)
rtas_args.rets[i] = 0;
opal_enter_rtas(&rtas_args, prom_rtas_data,
prom_rtas_entry);
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
outputs[i] = rtas_args.rets[i+1];
return (nret > 0)? rtas_args.rets[0]: 0;
}
static void __init prom_opal_hold_cpus(void)
{
int i, cnt, cpu, rc;
long j;
phandle node;
char type[64];
u32 servers[8];
void *entry = (unsigned long *)&opal_secondary_entry;
struct opal_secondary_data *data = &opal_secondary_data;
prom_debug("prom_opal_hold_cpus: start...\n");
prom_debug(" - entry = 0x%x\n", entry);
prom_debug(" - data = 0x%x\n", data);
data->ack = -1;
data->go = 0;
/* look for cpus */
for (node = 0; prom_next_node(&node); ) {
type[0] = 0;
prom_getprop(node, "device_type", type, sizeof(type));
if (strcmp(type, "cpu") != 0)
continue;
/* Skip non-configured cpus. */
if (prom_getprop(node, "status", type, sizeof(type)) > 0)
if (strcmp(type, "okay") != 0)
continue;
cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers,
sizeof(servers));
if (cnt == PROM_ERROR)
break;
cnt >>= 2;
for (i = 0; i < cnt; i++) {
cpu = servers[i];
prom_debug("CPU %d ... ", cpu);
if (cpu == prom.cpu) {
prom_debug("booted !\n");
continue;
}
prom_debug("starting ... ");
/* Init the acknowledge var which will be reset by
* the secondary cpu when it awakens from its OF
* spinloop.
*/
data->ack = -1;
rc = prom_rtas_call(prom_rtas_start_cpu, 3, 1,
NULL, cpu, entry, data);
prom_debug("rtas rc=%d ...", rc);
for (j = 0; j < 100000000 && data->ack == -1; j++) {
HMT_low();
mb();
}
HMT_medium();
if (data->ack != -1)
prom_debug("done, PIR=0x%x\n", data->ack);
else
prom_debug("timeout !\n");
}
}
prom_debug("prom_opal_hold_cpus: end...\n");
}
static void __init prom_opal_takeover(void)
{
struct opal_secondary_data *data = &opal_secondary_data;
struct opal_takeover_args *args = &data->args;
u64 align = prom_opal_align;
u64 top_addr, opal_addr;
args->k_image = (u64)_stext;
args->k_size = _end - _stext;
args->k_entry = 0;
args->k_entry2 = 0x60;
top_addr = _ALIGN_UP(args->k_size, align);
if (prom_initrd_start != 0) {
args->rd_image = prom_initrd_start;
args->rd_size = prom_initrd_end - args->rd_image;
args->rd_loc = top_addr;
top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align);
}
/* Pickup an address for the HAL. We want to go really high
* up to avoid problem with future kexecs. On the other hand
* we don't want to be all over the TCEs on P5IOC2 machines
* which are going to be up there too. We assume the machine
* has plenty of memory, and we ask for the HAL for now to
* be just below the 1G point, or above the initrd
*/
opal_addr = _ALIGN_DOWN(0x40000000 - prom_opal_size, align);
if (opal_addr < top_addr)
opal_addr = top_addr;
args->hal_addr = opal_addr;
/* Copy the command line to the kernel image */
strlcpy(boot_command_line, prom_cmd_line,
COMMAND_LINE_SIZE);
prom_debug(" k_image = 0x%lx\n", args->k_image);
prom_debug(" k_size = 0x%lx\n", args->k_size);
prom_debug(" k_entry = 0x%lx\n", args->k_entry);
prom_debug(" k_entry2 = 0x%lx\n", args->k_entry2);
prom_debug(" hal_addr = 0x%lx\n", args->hal_addr);
prom_debug(" rd_image = 0x%lx\n", args->rd_image);
prom_debug(" rd_size = 0x%lx\n", args->rd_size);
prom_debug(" rd_loc = 0x%lx\n", args->rd_loc);
prom_printf("Performing OPAL takeover,this can take a few minutes..\n");
prom_close_stdin();
mb();
data->go = 1;
for (;;)
opal_do_takeover(args);
}
#endif /* __BIG_ENDIAN__ */
/* /*
* Allocate room for and instantiate OPAL * Allocate room for and instantiate OPAL
*/ */
...@@ -1597,12 +1402,6 @@ static void __init prom_instantiate_rtas(void) ...@@ -1597,12 +1402,6 @@ static void __init prom_instantiate_rtas(void)
&val, sizeof(val)) != PROM_ERROR) &val, sizeof(val)) != PROM_ERROR)
rtas_has_query_cpu_stopped = true; rtas_has_query_cpu_stopped = true;
#if defined(CONFIG_PPC_POWERNV) && defined(__BIG_ENDIAN__)
/* PowerVN takeover hack */
prom_rtas_data = base;
prom_rtas_entry = entry;
prom_getprop(rtas_node, "start-cpu", &prom_rtas_start_cpu, 4);
#endif
prom_debug("rtas base = 0x%x\n", base); prom_debug("rtas base = 0x%x\n", base);
prom_debug("rtas entry = 0x%x\n", entry); prom_debug("rtas entry = 0x%x\n", entry);
prom_debug("rtas size = 0x%x\n", (long)size); prom_debug("rtas size = 0x%x\n", (long)size);
...@@ -3027,16 +2826,6 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, ...@@ -3027,16 +2826,6 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
prom_instantiate_rtas(); prom_instantiate_rtas();
#ifdef CONFIG_PPC_POWERNV #ifdef CONFIG_PPC_POWERNV
#ifdef __BIG_ENDIAN__
/* Detect HAL and try instanciating it & doing takeover */
if (of_platform == PLATFORM_PSERIES_LPAR) {
prom_query_opal();
if (of_platform == PLATFORM_OPAL) {
prom_opal_hold_cpus();
prom_opal_takeover();
}
} else
#endif /* __BIG_ENDIAN__ */
if (of_platform == PLATFORM_OPAL) if (of_platform == PLATFORM_OPAL)
prom_instantiate_opal(); prom_instantiate_opal();
#endif /* CONFIG_PPC_POWERNV */ #endif /* CONFIG_PPC_POWERNV */
......
...@@ -21,9 +21,7 @@ _end enter_prom memcpy memset reloc_offset __secondary_hold ...@@ -21,9 +21,7 @@ _end enter_prom memcpy memset reloc_offset __secondary_hold
__secondary_hold_acknowledge __secondary_hold_spinloop __start __secondary_hold_acknowledge __secondary_hold_spinloop __start
strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
reloc_got2 kernstart_addr memstart_addr linux_banner _stext reloc_got2 kernstart_addr memstart_addr linux_banner _stext
opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry __prom_init_toc_start __prom_init_toc_end btext_setup_display TOC."
boot_command_line __prom_init_toc_start __prom_init_toc_end
btext_setup_display TOC."
NM="$1" NM="$1"
OBJ="$2" OBJ="$2"
......
...@@ -469,9 +469,17 @@ void __init smp_setup_cpu_maps(void) ...@@ -469,9 +469,17 @@ void __init smp_setup_cpu_maps(void)
} }
for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) { for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
bool avail;
DBG(" thread %d -> cpu %d (hard id %d)\n", DBG(" thread %d -> cpu %d (hard id %d)\n",
j, cpu, be32_to_cpu(intserv[j])); j, cpu, be32_to_cpu(intserv[j]));
set_cpu_present(cpu, of_device_is_available(dn));
avail = of_device_is_available(dn);
if (!avail)
avail = !of_property_match_string(dn,
"enable-method", "spin-table");
set_cpu_present(cpu, avail);
set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j])); set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
set_cpu_possible(cpu, true); set_cpu_possible(cpu, true);
cpu++; cpu++;
......
...@@ -54,7 +54,6 @@ ...@@ -54,7 +54,6 @@
#include "signal.h" #include "signal.h"
#undef DEBUG_SIG
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
#define sys_rt_sigreturn compat_sys_rt_sigreturn #define sys_rt_sigreturn compat_sys_rt_sigreturn
...@@ -1063,10 +1062,6 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, ...@@ -1063,10 +1062,6 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
return 1; return 1;
badframe: badframe:
#ifdef DEBUG_SIG
printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp);
#endif
if (show_unhandled_signals) if (show_unhandled_signals)
printk_ratelimited(KERN_INFO printk_ratelimited(KERN_INFO
"%s[%d]: bad frame in handle_rt_signal32: " "%s[%d]: bad frame in handle_rt_signal32: "
...@@ -1484,10 +1479,6 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, ...@@ -1484,10 +1479,6 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
return 1; return 1;
badframe: badframe:
#ifdef DEBUG_SIG
printk("badframe in handle_signal, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp);
#endif
if (show_unhandled_signals) if (show_unhandled_signals)
printk_ratelimited(KERN_INFO printk_ratelimited(KERN_INFO
"%s[%d]: bad frame in handle_signal32: " "%s[%d]: bad frame in handle_signal32: "
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
#include "signal.h" #include "signal.h"
#define DEBUG_SIG 0
#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs)) #define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
#define FP_REGS_SIZE sizeof(elf_fpregset_t) #define FP_REGS_SIZE sizeof(elf_fpregset_t)
...@@ -700,10 +699,6 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, ...@@ -700,10 +699,6 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5,
return 0; return 0;
badframe: badframe:
#if DEBUG_SIG
printk("badframe in sys_rt_sigreturn, regs=%p uc=%p &uc->uc_mcontext=%p\n",
regs, uc, &uc->uc_mcontext);
#endif
if (show_unhandled_signals) if (show_unhandled_signals)
printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32, printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32,
current->comm, current->pid, "rt_sigreturn", current->comm, current->pid, "rt_sigreturn",
...@@ -809,10 +804,6 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, ...@@ -809,10 +804,6 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
return 1; return 1;
badframe: badframe:
#if DEBUG_SIG
printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n",
regs, frame, newsp);
#endif
if (show_unhandled_signals) if (show_unhandled_signals)
printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32, printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32,
current->comm, current->pid, "setup_rt_frame", current->comm, current->pid, "setup_rt_frame",
......
...@@ -125,7 +125,7 @@ static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, i ...@@ -125,7 +125,7 @@ static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, i
static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos) static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)
{ {
u64 reg_value; u64 reg_value;
int temp; unsigned int temp;
u64 new_value; u64 new_value;
int ret; int ret;
......
obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o obj-y += setup.o opal-wrappers.o opal.o opal-async.o
obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o obj-y += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o obj-y += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
obj-y += opal-msglog.o obj-y += opal-msglog.o
......
/*
* PowerNV OPAL takeover assembly code, for use by prom_init.c
*
* Copyright 2011 IBM Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <asm/ppc_asm.h>
#include <asm/hvcall.h>
#include <asm/asm-offsets.h>
#include <asm/opal.h>
#define H_HAL_TAKEOVER 0x5124
#define H_HAL_TAKEOVER_QUERY_MAGIC -1
.text
_GLOBAL(opal_query_takeover)
mfcr r0
stw r0,8(r1)
stdu r1,-STACKFRAMESIZE(r1)
std r3,STK_PARAM(R3)(r1)
std r4,STK_PARAM(R4)(r1)
li r3,H_HAL_TAKEOVER
li r4,H_HAL_TAKEOVER_QUERY_MAGIC
HVSC
addi r1,r1,STACKFRAMESIZE
ld r10,STK_PARAM(R3)(r1)
std r4,0(r10)
ld r10,STK_PARAM(R4)(r1)
std r5,0(r10)
lwz r0,8(r1)
mtcrf 0xff,r0
blr
_GLOBAL(opal_do_takeover)
mfcr r0
stw r0,8(r1)
mflr r0
std r0,16(r1)
bl __opal_do_takeover
ld r0,16(r1)
mtlr r0
lwz r0,8(r1)
mtcrf 0xff,r0
blr
__opal_do_takeover:
ld r4,0(r3)
ld r5,0x8(r3)
ld r6,0x10(r3)
ld r7,0x18(r3)
ld r8,0x20(r3)
ld r9,0x28(r3)
ld r10,0x30(r3)
ld r11,0x38(r3)
li r3,H_HAL_TAKEOVER
HVSC
blr
.globl opal_secondary_entry
opal_secondary_entry:
mr r31,r3
mfmsr r11
li r12,(MSR_SF | MSR_ISF)@highest
sldi r12,r12,48
or r11,r11,r12
mtmsrd r11
isync
mfspr r4,SPRN_PIR
std r4,0(r3)
1: HMT_LOW
ld r4,8(r3)
cmpli cr0,r4,0
beq 1b
HMT_MEDIUM
1: addi r3,r31,16
bl __opal_do_takeover
b 1b
_GLOBAL(opal_enter_rtas)
mflr r0
std r0,16(r1)
stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */
/* Because PROM is running in 32b mode, it clobbers the high order half
* of all registers that it saves. We therefore save those registers
* PROM might touch to the stack. (r0, r3-r13 are caller saved)
*/
SAVE_GPR(2, r1)
SAVE_GPR(13, r1)
SAVE_8GPRS(14, r1)
SAVE_10GPRS(22, r1)
mfcr r10
mfmsr r11
std r10,_CCR(r1)
std r11,_MSR(r1)
/* Get the PROM entrypoint */
mtlr r5
/* Switch MSR to 32 bits mode
*/
li r12,1
rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG)
andc r11,r11,r12
li r12,1
rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG)
andc r11,r11,r12
mtmsrd r11
isync
/* Enter RTAS here... */
blrl
/* Just make sure that r1 top 32 bits didn't get
* corrupt by OF
*/
rldicl r1,r1,0,32
/* Restore the MSR (back to 64 bits) */
ld r0,_MSR(r1)
MTMSRD(r0)
isync
/* Restore other registers */
REST_GPR(2, r1)
REST_GPR(13, r1)
REST_8GPRS(14, r1)
REST_10GPRS(22, r1)
ld r4,_CCR(r1)
mtcr r4
addi r1,r1,PROM_FRAME_SIZE
ld r0,16(r1)
mtlr r0
blr
...@@ -476,6 +476,11 @@ void __init alloc_dart_table(void) ...@@ -476,6 +476,11 @@ void __init alloc_dart_table(void)
*/ */
dart_tablebase = (unsigned long) dart_tablebase = (unsigned long)
__va(memblock_alloc_base(1UL<<24, 1UL<<24, 0x80000000L)); __va(memblock_alloc_base(1UL<<24, 1UL<<24, 0x80000000L));
/*
* The DART space is later unmapped from the kernel linear mapping and
* accessing dart_tablebase during kmemleak scanning will fault.
*/
kmemleak_no_scan((void *)dart_tablebase);
printk(KERN_INFO "DART table allocated at: %lx\n", dart_tablebase); printk(KERN_INFO "DART table allocated at: %lx\n", dart_tablebase);
} }
...@@ -1257,7 +1257,8 @@ static unsigned int smu_fpoll(struct file *file, poll_table *wait) ...@@ -1257,7 +1257,8 @@ static unsigned int smu_fpoll(struct file *file, poll_table *wait)
if (pp->busy && pp->cmd.status != 1) if (pp->busy && pp->cmd.status != 1)
mask |= POLLIN; mask |= POLLIN;
spin_unlock_irqrestore(&pp->lock, flags); spin_unlock_irqrestore(&pp->lock, flags);
} if (pp->mode == smu_file_events) { }
if (pp->mode == smu_file_events) {
/* Not yet implemented */ /* Not yet implemented */
} }
return mask; return mask;
......
...@@ -2,7 +2,7 @@ PROGS := tm-resched-dscr ...@@ -2,7 +2,7 @@ PROGS := tm-resched-dscr
all: $(PROGS) all: $(PROGS)
$(PROGS): $(PROGS): ../harness.c
run_tests: all run_tests: all
@-for PROG in $(PROGS); do \ @-for PROG in $(PROGS); do \
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <assert.h> #include <assert.h>
#include <asm/tm.h> #include <asm/tm.h>
#include "utils.h"
#define TBEGIN ".long 0x7C00051D ;" #define TBEGIN ".long 0x7C00051D ;"
#define TEND ".long 0x7C00055D ;" #define TEND ".long 0x7C00055D ;"
#define TCHECK ".long 0x7C00059C ;" #define TCHECK ".long 0x7C00059C ;"
...@@ -36,7 +38,8 @@ ...@@ -36,7 +38,8 @@
#define SPRN_TEXASR 0x82 #define SPRN_TEXASR 0x82
#define SPRN_DSCR 0x03 #define SPRN_DSCR 0x03
int main(void) { int test_body(void)
{
uint64_t rv, dscr1 = 1, dscr2, texasr; uint64_t rv, dscr1 = 1, dscr2, texasr;
printf("Check DSCR TM context switch: "); printf("Check DSCR TM context switch: ");
...@@ -81,10 +84,15 @@ int main(void) { ...@@ -81,10 +84,15 @@ int main(void) {
} }
if (dscr2 != dscr1) { if (dscr2 != dscr1) {
printf(" FAIL\n"); printf(" FAIL\n");
exit(EXIT_FAILURE); return 1;
} else { } else {
printf(" OK\n"); printf(" OK\n");
exit(EXIT_SUCCESS); return 0;
} }
} }
} }
int main(void)
{
return test_harness(test_body, "tm_resched_dscr");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment