mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-24 19:40:21 +09:00
Merge 4.9.107 into android-4.9
Changes in 4.9.107
arm64: lse: Add early clobbers to some input/output asm operands
powerpc/64s: Clear PCR on boot
USB: serial: cp210x: use tcflag_t to fix incompatible pointer type
Revert "pinctrl: msm: Use dynamic GPIO numbering"
xfs: detect agfl count corruption and reset agfl
Revert "ima: limit file hash setting by user to fix and log modes"
Input: elan_i2c_smbus - fix corrupted stack
tracing: Fix crash when freeing instances with event triggers
selinux: KASAN: slab-out-of-bounds in xattr_getsecurity
cfg80211: further limit wiphy names to 64 bytes
dma-buf: remove redundant initialization of sg_table
rtlwifi: rtl8192cu: Remove variable self-assignment in rf.c
ASoC: Intel: sst: remove redundant variable dma_dev_name
platform/chrome: cros_ec_lpc: remove redundant pointer request
x86/amd: revert commit 944e0fc51a
xen: set cpu capabilities from xen_start_kernel()
x86/amd: don't set X86_BUG_SYSRET_SS_ATTRS when running under Xen
tcp: avoid integer overflows in tcp_rcv_space_adjust()
scsi: ufs: fix failure to read the string descriptor
scsi: ufs: refactor device descriptor reading
scsi: ufs: Factor out ufshcd_read_desc_param
arm64: Add hypervisor safe helper for checking constant capabilities
arm64/cpufeature: don't use mutex in bringup path
powerpc/rfi-flush: Move out of HARDLOCKUP_DETECTOR #ifdef
powerpc/pseries: Support firmware disable of RFI flush
powerpc/powernv: Support firmware disable of RFI flush
powerpc/rfi-flush: Move the logic to avoid a redo into the debugfs code
powerpc/rfi-flush: Make it possible to call setup_rfi_flush() again
powerpc/rfi-flush: Always enable fallback flush on pseries
powerpc/rfi-flush: Differentiate enabled and patched flush types
powerpc/rfi-flush: Call setup_rfi_flush() after LPM migration
powerpc/pseries: Add new H_GET_CPU_CHARACTERISTICS flags
powerpc: Add security feature flags for Spectre/Meltdown
powerpc/pseries: Set or clear security feature flags
powerpc/powernv: Set or clear security feature flags
powerpc/64s: Move cpu_show_meltdown()
powerpc/64s: Enhance the information in cpu_show_meltdown()
powerpc/powernv: Use the security flags in pnv_setup_rfi_flush()
powerpc/pseries: Use the security flags in pseries_setup_rfi_flush()
powerpc/64s: Wire up cpu_show_spectre_v1()
powerpc/64s: Wire up cpu_show_spectre_v2()
powerpc/pseries: Fix clearing of security feature flags
powerpc: Move default security feature flags
powerpc/pseries: Restore default security feature flags on setup
powerpc/64s: Fix section mismatch warnings from setup_rfi_flush()
powerpc/64s: Add support for a store forwarding barrier at kernel entry/exit
net/mlx4_en: fix potential use-after-free with dma_unmap_page
iio:kfifo_buf: check for uint overflow
MIPS: ptrace: Fix PTRACE_PEEKUSR requests for 64-bit FGRs
MIPS: prctl: Disallow FRE without FR with PR_SET_FP_MODE requests
scsi: scsi_transport_srp: Fix shost to rport translation
stm class: Use vmalloc for the master map
hwtracing: stm: fix build error on some arches
IB/core: Fix error code for invalid GID entry
drm/psr: Fix missed entry in PSR setup time table.
drm/i915: Disable LVDS on Radiant P845
sparc64: Fix build warnings with gcc 7.
fix io_destroy()/aio_complete() race
mm: fix the NULL mapping case in __isolate_lru_page()
sparc64: Don't clibber fixed registers in __multi4.
serial: pl011: add console matching function
Linux 4.9.107
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
VERSION = 4
|
||||
PATCHLEVEL = 9
|
||||
SUBLEVEL = 106
|
||||
SUBLEVEL = 107
|
||||
EXTRAVERSION =
|
||||
NAME = Roaring Lionus
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ static inline void atomic_and(int i, atomic_t *v)
|
||||
/* LSE atomics */
|
||||
" mvn %w[i], %w[i]\n"
|
||||
" stclr %w[i], %[v]")
|
||||
: [i] "+r" (w0), [v] "+Q" (v->counter)
|
||||
: [i] "+&r" (w0), [v] "+Q" (v->counter)
|
||||
: "r" (x1)
|
||||
: __LL_SC_CLOBBERS);
|
||||
}
|
||||
@@ -135,7 +135,7 @@ static inline int atomic_fetch_and##name(int i, atomic_t *v) \
|
||||
/* LSE atomics */ \
|
||||
" mvn %w[i], %w[i]\n" \
|
||||
" ldclr" #mb " %w[i], %w[i], %[v]") \
|
||||
: [i] "+r" (w0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (w0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS, ##cl); \
|
||||
\
|
||||
@@ -161,7 +161,7 @@ static inline void atomic_sub(int i, atomic_t *v)
|
||||
/* LSE atomics */
|
||||
" neg %w[i], %w[i]\n"
|
||||
" stadd %w[i], %[v]")
|
||||
: [i] "+r" (w0), [v] "+Q" (v->counter)
|
||||
: [i] "+&r" (w0), [v] "+Q" (v->counter)
|
||||
: "r" (x1)
|
||||
: __LL_SC_CLOBBERS);
|
||||
}
|
||||
@@ -180,7 +180,7 @@ static inline int atomic_sub_return##name(int i, atomic_t *v) \
|
||||
" neg %w[i], %w[i]\n" \
|
||||
" ldadd" #mb " %w[i], w30, %[v]\n" \
|
||||
" add %w[i], %w[i], w30") \
|
||||
: [i] "+r" (w0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (w0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS , ##cl); \
|
||||
\
|
||||
@@ -207,7 +207,7 @@ static inline int atomic_fetch_sub##name(int i, atomic_t *v) \
|
||||
/* LSE atomics */ \
|
||||
" neg %w[i], %w[i]\n" \
|
||||
" ldadd" #mb " %w[i], %w[i], %[v]") \
|
||||
: [i] "+r" (w0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (w0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS, ##cl); \
|
||||
\
|
||||
@@ -314,7 +314,7 @@ static inline void atomic64_and(long i, atomic64_t *v)
|
||||
/* LSE atomics */
|
||||
" mvn %[i], %[i]\n"
|
||||
" stclr %[i], %[v]")
|
||||
: [i] "+r" (x0), [v] "+Q" (v->counter)
|
||||
: [i] "+&r" (x0), [v] "+Q" (v->counter)
|
||||
: "r" (x1)
|
||||
: __LL_SC_CLOBBERS);
|
||||
}
|
||||
@@ -332,7 +332,7 @@ static inline long atomic64_fetch_and##name(long i, atomic64_t *v) \
|
||||
/* LSE atomics */ \
|
||||
" mvn %[i], %[i]\n" \
|
||||
" ldclr" #mb " %[i], %[i], %[v]") \
|
||||
: [i] "+r" (x0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (x0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS, ##cl); \
|
||||
\
|
||||
@@ -358,7 +358,7 @@ static inline void atomic64_sub(long i, atomic64_t *v)
|
||||
/* LSE atomics */
|
||||
" neg %[i], %[i]\n"
|
||||
" stadd %[i], %[v]")
|
||||
: [i] "+r" (x0), [v] "+Q" (v->counter)
|
||||
: [i] "+&r" (x0), [v] "+Q" (v->counter)
|
||||
: "r" (x1)
|
||||
: __LL_SC_CLOBBERS);
|
||||
}
|
||||
@@ -377,7 +377,7 @@ static inline long atomic64_sub_return##name(long i, atomic64_t *v) \
|
||||
" neg %[i], %[i]\n" \
|
||||
" ldadd" #mb " %[i], x30, %[v]\n" \
|
||||
" add %[i], %[i], x30") \
|
||||
: [i] "+r" (x0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (x0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS, ##cl); \
|
||||
\
|
||||
@@ -404,7 +404,7 @@ static inline long atomic64_fetch_sub##name(long i, atomic64_t *v) \
|
||||
/* LSE atomics */ \
|
||||
" neg %[i], %[i]\n" \
|
||||
" ldadd" #mb " %[i], %[i], %[v]") \
|
||||
: [i] "+r" (x0), [v] "+Q" (v->counter) \
|
||||
: [i] "+&r" (x0), [v] "+Q" (v->counter) \
|
||||
: "r" (x1) \
|
||||
: __LL_SC_CLOBBERS, ##cl); \
|
||||
\
|
||||
@@ -516,7 +516,7 @@ static inline long __cmpxchg_double##name(unsigned long old1, \
|
||||
" eor %[old1], %[old1], %[oldval1]\n" \
|
||||
" eor %[old2], %[old2], %[oldval2]\n" \
|
||||
" orr %[old1], %[old1], %[old2]") \
|
||||
: [old1] "+r" (x0), [old2] "+r" (x1), \
|
||||
: [old1] "+&r" (x0), [old2] "+&r" (x1), \
|
||||
[v] "+Q" (*(unsigned long *)ptr) \
|
||||
: [new1] "r" (x2), [new2] "r" (x3), [ptr] "r" (x4), \
|
||||
[oldval1] "r" (oldval1), [oldval2] "r" (oldval2) \
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#ifndef __ASM_CPUFEATURE_H
|
||||
#define __ASM_CPUFEATURE_H
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
#include <asm/cpucaps.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/sysreg.h>
|
||||
@@ -27,6 +25,8 @@
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/* CPU feature register tracking */
|
||||
@@ -96,6 +96,7 @@ struct arm64_cpu_capabilities {
|
||||
|
||||
extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
|
||||
extern struct static_key_false cpu_hwcap_keys[ARM64_NCAPS];
|
||||
extern struct static_key_false arm64_const_caps_ready;
|
||||
|
||||
bool this_cpu_has_cap(unsigned int cap);
|
||||
|
||||
@@ -104,14 +105,27 @@ static inline bool cpu_have_feature(unsigned int num)
|
||||
return elf_hwcap & (1UL << num);
|
||||
}
|
||||
|
||||
/* System capability check for constant caps */
|
||||
static inline bool __cpus_have_const_cap(int num)
|
||||
{
|
||||
if (num >= ARM64_NCAPS)
|
||||
return false;
|
||||
return static_branch_unlikely(&cpu_hwcap_keys[num]);
|
||||
}
|
||||
|
||||
static inline bool cpus_have_cap(unsigned int num)
|
||||
{
|
||||
if (num >= ARM64_NCAPS)
|
||||
return false;
|
||||
if (__builtin_constant_p(num))
|
||||
return static_branch_unlikely(&cpu_hwcap_keys[num]);
|
||||
return test_bit(num, cpu_hwcaps);
|
||||
}
|
||||
|
||||
static inline bool cpus_have_const_cap(int num)
|
||||
{
|
||||
if (static_branch_likely(&arm64_const_caps_ready))
|
||||
return __cpus_have_const_cap(num);
|
||||
else
|
||||
return test_bit(num, cpu_hwcaps);
|
||||
return cpus_have_cap(num);
|
||||
}
|
||||
|
||||
static inline void cpus_set_cap(unsigned int num)
|
||||
@@ -121,7 +135,6 @@ static inline void cpus_set_cap(unsigned int num)
|
||||
num, ARM64_NCAPS);
|
||||
} else {
|
||||
__set_bit(num, cpu_hwcaps);
|
||||
static_branch_enable(&cpu_hwcap_keys[num]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +213,7 @@ static inline bool cpu_supports_mixed_endian_el0(void)
|
||||
|
||||
static inline bool system_supports_32bit_el0(void)
|
||||
{
|
||||
return cpus_have_cap(ARM64_HAS_32BIT_EL0);
|
||||
return cpus_have_const_cap(ARM64_HAS_32BIT_EL0);
|
||||
}
|
||||
|
||||
static inline bool system_supports_mixed_endian_el0(void)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kvm_types.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/kvm.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_mmio.h>
|
||||
@@ -358,9 +359,12 @@ static inline void __cpu_init_hyp_mode(phys_addr_t pgd_ptr,
|
||||
unsigned long vector_ptr)
|
||||
{
|
||||
/*
|
||||
* Call initialization code, and switch to the full blown
|
||||
* HYP code.
|
||||
* Call initialization code, and switch to the full blown HYP code.
|
||||
* If the cpucaps haven't been finalized yet, something has gone very
|
||||
* wrong, and hyp will crash and burn when it uses any
|
||||
* cpus_have_const_cap() wrapper.
|
||||
*/
|
||||
BUG_ON(!static_branch_likely(&arm64_const_caps_ready));
|
||||
__kvm_call_hyp((void *)pgd_ptr, hyp_stack_ptr, vector_ptr);
|
||||
}
|
||||
|
||||
@@ -398,7 +402,7 @@ static inline void __cpu_init_stage2(void)
|
||||
|
||||
static inline bool kvm_arm_harden_branch_predictor(void)
|
||||
{
|
||||
return cpus_have_cap(ARM64_HARDEN_BRANCH_PREDICTOR);
|
||||
return cpus_have_const_cap(ARM64_HARDEN_BRANCH_PREDICTOR);
|
||||
}
|
||||
|
||||
#endif /* __ARM64_KVM_HOST_H__ */
|
||||
|
||||
@@ -341,7 +341,7 @@ static inline void *kvm_get_hyp_vector(void)
|
||||
vect = __bp_harden_hyp_vecs_start +
|
||||
data->hyp_vectors_slot * SZ_2K;
|
||||
|
||||
if (!cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))
|
||||
if (!cpus_have_const_cap(ARM64_HAS_VIRT_HOST_EXTN))
|
||||
vect = lm_alias(vect);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ typedef struct {
|
||||
static inline bool arm64_kernel_unmapped_at_el0(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0) &&
|
||||
cpus_have_cap(ARM64_UNMAP_KERNEL_AT_EL0);
|
||||
cpus_have_const_cap(ARM64_UNMAP_KERNEL_AT_EL0);
|
||||
}
|
||||
|
||||
typedef void (*bp_hardening_cb_t)(void);
|
||||
|
||||
@@ -764,7 +764,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
|
||||
* ThunderX leads to apparent I-cache corruption of kernel text, which
|
||||
* ends as well as you might imagine. Don't even try.
|
||||
*/
|
||||
if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_27456)) {
|
||||
if (cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_27456)) {
|
||||
str = "ARM64_WORKAROUND_CAVIUM_27456";
|
||||
__kpti_forced = -1;
|
||||
}
|
||||
@@ -1053,8 +1053,16 @@ void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
|
||||
*/
|
||||
void __init enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
|
||||
{
|
||||
for (; caps->matches; caps++)
|
||||
if (caps->enable && cpus_have_cap(caps->capability))
|
||||
for (; caps->matches; caps++) {
|
||||
unsigned int num = caps->capability;
|
||||
|
||||
if (!cpus_have_cap(num))
|
||||
continue;
|
||||
|
||||
/* Ensure cpus_have_const_cap(num) works */
|
||||
static_branch_enable(&cpu_hwcap_keys[num]);
|
||||
|
||||
if (caps->enable) {
|
||||
/*
|
||||
* Use stop_machine() as it schedules the work allowing
|
||||
* us to modify PSTATE, instead of on_each_cpu() which
|
||||
@@ -1062,6 +1070,8 @@ void __init enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
|
||||
* we return.
|
||||
*/
|
||||
stop_machine(caps->enable, (void *)caps, cpu_online_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1165,6 +1175,14 @@ static void __init setup_feature_capabilities(void)
|
||||
enable_cpu_capabilities(arm64_features);
|
||||
}
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(arm64_const_caps_ready);
|
||||
EXPORT_SYMBOL(arm64_const_caps_ready);
|
||||
|
||||
static void __init mark_const_caps_ready(void)
|
||||
{
|
||||
static_branch_enable(&arm64_const_caps_ready);
|
||||
}
|
||||
|
||||
extern const struct arm64_cpu_capabilities arm64_errata[];
|
||||
|
||||
bool this_cpu_has_cap(unsigned int cap)
|
||||
@@ -1181,6 +1199,7 @@ void __init setup_cpu_features(void)
|
||||
/* Set the CPU feature capabilies */
|
||||
setup_feature_capabilities();
|
||||
enable_errata_workarounds();
|
||||
mark_const_caps_ready();
|
||||
setup_elf_hwcaps(arm64_elf_hwcaps);
|
||||
|
||||
if (system_supports_32bit_el0())
|
||||
@@ -1205,5 +1224,5 @@ void __init setup_cpu_features(void)
|
||||
static bool __maybe_unused
|
||||
cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
|
||||
{
|
||||
return (cpus_have_cap(ARM64_HAS_PAN) && !cpus_have_cap(ARM64_HAS_UAO));
|
||||
return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO));
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
childregs->pstate = PSR_MODE_EL1h;
|
||||
if (IS_ENABLED(CONFIG_ARM64_UAO) &&
|
||||
cpus_have_cap(ARM64_HAS_UAO))
|
||||
cpus_have_const_cap(ARM64_HAS_UAO))
|
||||
childregs->pstate |= PSR_UAO_BIT;
|
||||
p->thread.cpu_context.x19 = stack_start;
|
||||
p->thread.cpu_context.x20 = stk_sz;
|
||||
|
||||
@@ -699,6 +699,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
|
||||
if (value & ~known_bits)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Setting FRE without FR is not supported. */
|
||||
if ((value & (PR_FP_MODE_FR | PR_FP_MODE_FRE)) == PR_FP_MODE_FRE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Avoid inadvertently triggering emulation */
|
||||
if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu &&
|
||||
!(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64))
|
||||
|
||||
@@ -838,7 +838,7 @@ long arch_ptrace(struct task_struct *child, long request,
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
|
||||
tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
|
||||
break;
|
||||
case PC:
|
||||
tmp = regs->cp0_epc;
|
||||
|
||||
@@ -107,7 +107,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
||||
addr & 1);
|
||||
break;
|
||||
}
|
||||
tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
|
||||
tmp = get_fpr64(&fregs[addr - FPR_BASE], 0);
|
||||
break;
|
||||
case PC:
|
||||
tmp = regs->cp0_epc;
|
||||
|
||||
@@ -51,6 +51,27 @@
|
||||
#define EX_PPR 88 /* SMT thread status register (priority) */
|
||||
#define EX_CTR 96
|
||||
|
||||
#define STF_ENTRY_BARRIER_SLOT \
|
||||
STF_ENTRY_BARRIER_FIXUP_SECTION; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop
|
||||
|
||||
#define STF_EXIT_BARRIER_SLOT \
|
||||
STF_EXIT_BARRIER_FIXUP_SECTION; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop
|
||||
|
||||
/*
|
||||
* r10 must be free to use, r13 must be paca
|
||||
*/
|
||||
#define INTERRUPT_TO_KERNEL \
|
||||
STF_ENTRY_BARRIER_SLOT
|
||||
|
||||
/*
|
||||
* Macros for annotating the expected destination of (h)rfid
|
||||
*
|
||||
@@ -67,16 +88,19 @@
|
||||
rfid
|
||||
|
||||
#define RFI_TO_USER \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
rfid; \
|
||||
b rfi_flush_fallback
|
||||
|
||||
#define RFI_TO_USER_OR_KERNEL \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
rfid; \
|
||||
b rfi_flush_fallback
|
||||
|
||||
#define RFI_TO_GUEST \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
rfid; \
|
||||
b rfi_flush_fallback
|
||||
@@ -85,21 +109,25 @@
|
||||
hrfid
|
||||
|
||||
#define HRFI_TO_USER \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
hrfid; \
|
||||
b hrfi_flush_fallback
|
||||
|
||||
#define HRFI_TO_USER_OR_KERNEL \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
hrfid; \
|
||||
b hrfi_flush_fallback
|
||||
|
||||
#define HRFI_TO_GUEST \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
hrfid; \
|
||||
b hrfi_flush_fallback
|
||||
|
||||
#define HRFI_TO_UNKNOWN \
|
||||
STF_EXIT_BARRIER_SLOT; \
|
||||
RFI_FLUSH_SLOT; \
|
||||
hrfid; \
|
||||
b hrfi_flush_fallback
|
||||
@@ -225,6 +253,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
|
||||
#define __EXCEPTION_PROLOG_1(area, extra, vec) \
|
||||
OPT_SAVE_REG_TO_PACA(area+EX_PPR, r9, CPU_FTR_HAS_PPR); \
|
||||
OPT_SAVE_REG_TO_PACA(area+EX_CFAR, r10, CPU_FTR_CFAR); \
|
||||
INTERRUPT_TO_KERNEL; \
|
||||
SAVE_CTR(r10, area); \
|
||||
mfcr r9; \
|
||||
extra(vec); \
|
||||
|
||||
@@ -189,6 +189,22 @@ void apply_feature_fixups(void);
|
||||
void setup_feature_keys(void);
|
||||
#endif
|
||||
|
||||
#define STF_ENTRY_BARRIER_FIXUP_SECTION \
|
||||
953: \
|
||||
.pushsection __stf_entry_barrier_fixup,"a"; \
|
||||
.align 2; \
|
||||
954: \
|
||||
FTR_ENTRY_OFFSET 953b-954b; \
|
||||
.popsection;
|
||||
|
||||
#define STF_EXIT_BARRIER_FIXUP_SECTION \
|
||||
955: \
|
||||
.pushsection __stf_exit_barrier_fixup,"a"; \
|
||||
.align 2; \
|
||||
956: \
|
||||
FTR_ENTRY_OFFSET 955b-956b; \
|
||||
.popsection;
|
||||
|
||||
#define RFI_FLUSH_FIXUP_SECTION \
|
||||
951: \
|
||||
.pushsection __rfi_flush_fixup,"a"; \
|
||||
@@ -200,6 +216,9 @@ void setup_feature_keys(void);
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
extern long stf_barrier_fallback;
|
||||
extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup;
|
||||
extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup;
|
||||
extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -313,6 +313,9 @@
|
||||
#define H_CPU_CHAR_L1D_FLUSH_ORI30 (1ull << 61) // IBM bit 2
|
||||
#define H_CPU_CHAR_L1D_FLUSH_TRIG2 (1ull << 60) // IBM bit 3
|
||||
#define H_CPU_CHAR_L1D_THREAD_PRIV (1ull << 59) // IBM bit 4
|
||||
#define H_CPU_CHAR_BRANCH_HINTS_HONORED (1ull << 58) // IBM bit 5
|
||||
#define H_CPU_CHAR_THREAD_RECONFIG_CTRL (1ull << 57) // IBM bit 6
|
||||
#define H_CPU_CHAR_COUNT_CACHE_DISABLED (1ull << 56) // IBM bit 7
|
||||
|
||||
#define H_CPU_BEHAV_FAVOUR_SECURITY (1ull << 63) // IBM bit 0
|
||||
#define H_CPU_BEHAV_L1D_FLUSH_PR (1ull << 62) // IBM bit 1
|
||||
|
||||
85
arch/powerpc/include/asm/security_features.h
Normal file
85
arch/powerpc/include/asm/security_features.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Security related feature bit definitions.
|
||||
*
|
||||
* Copyright 2018, Michael Ellerman, IBM Corporation.
|
||||
*/
|
||||
|
||||
#ifndef _ASM_POWERPC_SECURITY_FEATURES_H
|
||||
#define _ASM_POWERPC_SECURITY_FEATURES_H
|
||||
|
||||
|
||||
extern unsigned long powerpc_security_features;
|
||||
extern bool rfi_flush;
|
||||
|
||||
/* These are bit flags */
|
||||
enum stf_barrier_type {
|
||||
STF_BARRIER_NONE = 0x1,
|
||||
STF_BARRIER_FALLBACK = 0x2,
|
||||
STF_BARRIER_EIEIO = 0x4,
|
||||
STF_BARRIER_SYNC_ORI = 0x8,
|
||||
};
|
||||
|
||||
void setup_stf_barrier(void);
|
||||
void do_stf_barrier_fixups(enum stf_barrier_type types);
|
||||
|
||||
static inline void security_ftr_set(unsigned long feature)
|
||||
{
|
||||
powerpc_security_features |= feature;
|
||||
}
|
||||
|
||||
static inline void security_ftr_clear(unsigned long feature)
|
||||
{
|
||||
powerpc_security_features &= ~feature;
|
||||
}
|
||||
|
||||
static inline bool security_ftr_enabled(unsigned long feature)
|
||||
{
|
||||
return !!(powerpc_security_features & feature);
|
||||
}
|
||||
|
||||
|
||||
// Features indicating support for Spectre/Meltdown mitigations
|
||||
|
||||
// The L1-D cache can be flushed with ori r30,r30,0
|
||||
#define SEC_FTR_L1D_FLUSH_ORI30 0x0000000000000001ull
|
||||
|
||||
// The L1-D cache can be flushed with mtspr 882,r0 (aka SPRN_TRIG2)
|
||||
#define SEC_FTR_L1D_FLUSH_TRIG2 0x0000000000000002ull
|
||||
|
||||
// ori r31,r31,0 acts as a speculation barrier
|
||||
#define SEC_FTR_SPEC_BAR_ORI31 0x0000000000000004ull
|
||||
|
||||
// Speculation past bctr is disabled
|
||||
#define SEC_FTR_BCCTRL_SERIALISED 0x0000000000000008ull
|
||||
|
||||
// Entries in L1-D are private to a SMT thread
|
||||
#define SEC_FTR_L1D_THREAD_PRIV 0x0000000000000010ull
|
||||
|
||||
// Indirect branch prediction cache disabled
|
||||
#define SEC_FTR_COUNT_CACHE_DISABLED 0x0000000000000020ull
|
||||
|
||||
|
||||
// Features indicating need for Spectre/Meltdown mitigations
|
||||
|
||||
// The L1-D cache should be flushed on MSR[HV] 1->0 transition (hypervisor to guest)
|
||||
#define SEC_FTR_L1D_FLUSH_HV 0x0000000000000040ull
|
||||
|
||||
// The L1-D cache should be flushed on MSR[PR] 0->1 transition (kernel to userspace)
|
||||
#define SEC_FTR_L1D_FLUSH_PR 0x0000000000000080ull
|
||||
|
||||
// A speculation barrier should be used for bounds checks (Spectre variant 1)
|
||||
#define SEC_FTR_BNDS_CHK_SPEC_BAR 0x0000000000000100ull
|
||||
|
||||
// Firmware configuration indicates user favours security over performance
|
||||
#define SEC_FTR_FAVOUR_SECURITY 0x0000000000000200ull
|
||||
|
||||
|
||||
// Features enabled by default
|
||||
#define SEC_FTR_DEFAULT \
|
||||
(SEC_FTR_L1D_FLUSH_HV | \
|
||||
SEC_FTR_L1D_FLUSH_PR | \
|
||||
SEC_FTR_BNDS_CHK_SPEC_BAR | \
|
||||
SEC_FTR_FAVOUR_SECURITY)
|
||||
|
||||
#endif /* _ASM_POWERPC_SECURITY_FEATURES_H */
|
||||
@@ -48,7 +48,7 @@ enum l1d_flush_type {
|
||||
L1D_FLUSH_MTTRIG = 0x8,
|
||||
};
|
||||
|
||||
void __init setup_rfi_flush(enum l1d_flush_type, bool enable);
|
||||
void setup_rfi_flush(enum l1d_flush_type, bool enable);
|
||||
void do_rfi_flush_fixups(enum l1d_flush_type types);
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
@@ -44,7 +44,7 @@ obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \
|
||||
obj-$(CONFIG_VDSO32) += vdso32/
|
||||
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
|
||||
obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_ppc970.o cpu_setup_pa6t.o
|
||||
obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_power.o
|
||||
obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_power.o security.o
|
||||
obj-$(CONFIG_PPC_BOOK3S_64) += mce.o mce_power.o
|
||||
obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o idle_book3e.o
|
||||
obj-$(CONFIG_PPC64) += vdso64/
|
||||
|
||||
@@ -28,6 +28,7 @@ _GLOBAL(__setup_cpu_power7)
|
||||
beqlr
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
bl __init_LPCR
|
||||
bl __init_tlb_power7
|
||||
@@ -41,6 +42,7 @@ _GLOBAL(__restore_cpu_power7)
|
||||
beqlr
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
bl __init_LPCR
|
||||
bl __init_tlb_power7
|
||||
@@ -57,6 +59,7 @@ _GLOBAL(__setup_cpu_power8)
|
||||
beqlr
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
ori r3, r3, LPCR_PECEDH
|
||||
bl __init_LPCR
|
||||
@@ -78,6 +81,7 @@ _GLOBAL(__restore_cpu_power8)
|
||||
beqlr
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
ori r3, r3, LPCR_PECEDH
|
||||
bl __init_LPCR
|
||||
@@ -98,6 +102,7 @@ _GLOBAL(__setup_cpu_power9)
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
LOAD_REG_IMMEDIATE(r4, LPCR_PECEDH | LPCR_PECE_HVEE | LPCR_HVICE)
|
||||
or r3, r3, r4
|
||||
@@ -121,6 +126,7 @@ _GLOBAL(__restore_cpu_power9)
|
||||
li r0,0
|
||||
mtspr SPRN_LPID,r0
|
||||
mtspr SPRN_PID,r0
|
||||
mtspr SPRN_PCR,r0
|
||||
mfspr r3,SPRN_LPCR
|
||||
LOAD_REG_IMMEDIATE(r4, LPCR_PECEDH | LPCR_PECE_HVEE | LPCR_HVICE)
|
||||
or r3, r3, r4
|
||||
|
||||
@@ -846,7 +846,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_TM)
|
||||
#endif
|
||||
|
||||
|
||||
EXC_REAL_MASKABLE(decrementer, 0x900, 0x980)
|
||||
EXC_REAL_OOL_MASKABLE(decrementer, 0x900, 0x980)
|
||||
EXC_VIRT_MASKABLE(decrementer, 0x4900, 0x4980, 0x900)
|
||||
TRAMP_KVM(PACA_EXGEN, 0x900)
|
||||
EXC_COMMON_ASYNC(decrementer_common, 0x900, timer_interrupt)
|
||||
@@ -884,6 +884,7 @@ BEGIN_FTR_SECTION \
|
||||
END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE) \
|
||||
mr r9,r13 ; \
|
||||
GET_PACA(r13) ; \
|
||||
INTERRUPT_TO_KERNEL ; \
|
||||
mfspr r11,SPRN_SRR0 ; \
|
||||
0:
|
||||
|
||||
@@ -1353,6 +1354,19 @@ masked_##_H##interrupt: \
|
||||
##_H##RFI_TO_KERNEL; \
|
||||
b .
|
||||
|
||||
TRAMP_REAL_BEGIN(stf_barrier_fallback)
|
||||
std r9,PACA_EXRFI+EX_R9(r13)
|
||||
std r10,PACA_EXRFI+EX_R10(r13)
|
||||
sync
|
||||
ld r9,PACA_EXRFI+EX_R9(r13)
|
||||
ld r10,PACA_EXRFI+EX_R10(r13)
|
||||
ori 31,31,0
|
||||
.rept 14
|
||||
b 1f
|
||||
1:
|
||||
.endr
|
||||
blr
|
||||
|
||||
/*
|
||||
* Real mode exceptions actually use this too, but alternate
|
||||
* instruction code patches (which end up in the common .text area)
|
||||
|
||||
237
arch/powerpc/kernel/security.c
Normal file
237
arch/powerpc/kernel/security.c
Normal file
@@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Security related flags and so on.
|
||||
//
|
||||
// Copyright 2018, Michael Ellerman, IBM Corporation.
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/seq_buf.h>
|
||||
|
||||
#include <asm/security_features.h>
|
||||
|
||||
|
||||
unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
|
||||
|
||||
ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
bool thread_priv;
|
||||
|
||||
thread_priv = security_ftr_enabled(SEC_FTR_L1D_THREAD_PRIV);
|
||||
|
||||
if (rfi_flush || thread_priv) {
|
||||
struct seq_buf s;
|
||||
seq_buf_init(&s, buf, PAGE_SIZE - 1);
|
||||
|
||||
seq_buf_printf(&s, "Mitigation: ");
|
||||
|
||||
if (rfi_flush)
|
||||
seq_buf_printf(&s, "RFI Flush");
|
||||
|
||||
if (rfi_flush && thread_priv)
|
||||
seq_buf_printf(&s, ", ");
|
||||
|
||||
if (thread_priv)
|
||||
seq_buf_printf(&s, "L1D private per thread");
|
||||
|
||||
seq_buf_printf(&s, "\n");
|
||||
|
||||
return s.len;
|
||||
}
|
||||
|
||||
if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
|
||||
!security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
|
||||
return sprintf(buf, "Not affected\n");
|
||||
|
||||
return sprintf(buf, "Vulnerable\n");
|
||||
}
|
||||
|
||||
ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (!security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR))
|
||||
return sprintf(buf, "Not affected\n");
|
||||
|
||||
return sprintf(buf, "Vulnerable\n");
|
||||
}
|
||||
|
||||
ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
bool bcs, ccd, ori;
|
||||
struct seq_buf s;
|
||||
|
||||
seq_buf_init(&s, buf, PAGE_SIZE - 1);
|
||||
|
||||
bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
|
||||
ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
|
||||
ori = security_ftr_enabled(SEC_FTR_SPEC_BAR_ORI31);
|
||||
|
||||
if (bcs || ccd) {
|
||||
seq_buf_printf(&s, "Mitigation: ");
|
||||
|
||||
if (bcs)
|
||||
seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
|
||||
|
||||
if (bcs && ccd)
|
||||
seq_buf_printf(&s, ", ");
|
||||
|
||||
if (ccd)
|
||||
seq_buf_printf(&s, "Indirect branch cache disabled");
|
||||
} else
|
||||
seq_buf_printf(&s, "Vulnerable");
|
||||
|
||||
if (ori)
|
||||
seq_buf_printf(&s, ", ori31 speculation barrier enabled");
|
||||
|
||||
seq_buf_printf(&s, "\n");
|
||||
|
||||
return s.len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store-forwarding barrier support.
|
||||
*/
|
||||
|
||||
static enum stf_barrier_type stf_enabled_flush_types;
|
||||
static bool no_stf_barrier;
|
||||
bool stf_barrier;
|
||||
|
||||
static int __init handle_no_stf_barrier(char *p)
|
||||
{
|
||||
pr_info("stf-barrier: disabled on command line.");
|
||||
no_stf_barrier = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("no_stf_barrier", handle_no_stf_barrier);
|
||||
|
||||
/* This is the generic flag used by other architectures */
|
||||
static int __init handle_ssbd(char *p)
|
||||
{
|
||||
if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) {
|
||||
/* Until firmware tells us, we have the barrier with auto */
|
||||
return 0;
|
||||
} else if (strncmp(p, "off", 3) == 0) {
|
||||
handle_no_stf_barrier(NULL);
|
||||
return 0;
|
||||
} else
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_param("spec_store_bypass_disable", handle_ssbd);
|
||||
|
||||
/* This is the generic flag used by other architectures */
|
||||
static int __init handle_no_ssbd(char *p)
|
||||
{
|
||||
handle_no_stf_barrier(NULL);
|
||||
return 0;
|
||||
}
|
||||
early_param("nospec_store_bypass_disable", handle_no_ssbd);
|
||||
|
||||
static void stf_barrier_enable(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
do_stf_barrier_fixups(stf_enabled_flush_types);
|
||||
else
|
||||
do_stf_barrier_fixups(STF_BARRIER_NONE);
|
||||
|
||||
stf_barrier = enable;
|
||||
}
|
||||
|
||||
void setup_stf_barrier(void)
|
||||
{
|
||||
enum stf_barrier_type type;
|
||||
bool enable, hv;
|
||||
|
||||
hv = cpu_has_feature(CPU_FTR_HVMODE);
|
||||
|
||||
/* Default to fallback in case fw-features are not available */
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_300))
|
||||
type = STF_BARRIER_EIEIO;
|
||||
else if (cpu_has_feature(CPU_FTR_ARCH_207S))
|
||||
type = STF_BARRIER_SYNC_ORI;
|
||||
else if (cpu_has_feature(CPU_FTR_ARCH_206))
|
||||
type = STF_BARRIER_FALLBACK;
|
||||
else
|
||||
type = STF_BARRIER_NONE;
|
||||
|
||||
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
|
||||
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) ||
|
||||
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv));
|
||||
|
||||
if (type == STF_BARRIER_FALLBACK) {
|
||||
pr_info("stf-barrier: fallback barrier available\n");
|
||||
} else if (type == STF_BARRIER_SYNC_ORI) {
|
||||
pr_info("stf-barrier: hwsync barrier available\n");
|
||||
} else if (type == STF_BARRIER_EIEIO) {
|
||||
pr_info("stf-barrier: eieio barrier available\n");
|
||||
}
|
||||
|
||||
stf_enabled_flush_types = type;
|
||||
|
||||
if (!no_stf_barrier)
|
||||
stf_barrier_enable(enable);
|
||||
}
|
||||
|
||||
ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) {
|
||||
const char *type;
|
||||
switch (stf_enabled_flush_types) {
|
||||
case STF_BARRIER_EIEIO:
|
||||
type = "eieio";
|
||||
break;
|
||||
case STF_BARRIER_SYNC_ORI:
|
||||
type = "hwsync";
|
||||
break;
|
||||
case STF_BARRIER_FALLBACK:
|
||||
type = "fallback";
|
||||
break;
|
||||
default:
|
||||
type = "unknown";
|
||||
}
|
||||
return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type);
|
||||
}
|
||||
|
||||
if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) &&
|
||||
!security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR))
|
||||
return sprintf(buf, "Not affected\n");
|
||||
|
||||
return sprintf(buf, "Vulnerable\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int stf_barrier_set(void *data, u64 val)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
if (val == 1)
|
||||
enable = true;
|
||||
else if (val == 0)
|
||||
enable = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Only do anything if we're changing state */
|
||||
if (enable != stf_barrier)
|
||||
stf_barrier_enable(enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stf_barrier_get(void *data, u64 *val)
|
||||
{
|
||||
*val = stf_barrier ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n");
|
||||
|
||||
static __init int stf_barrier_debugfs_init(void)
|
||||
{
|
||||
debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(stf_barrier_debugfs_init);
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
@@ -679,6 +679,7 @@ static int __init disable_hardlockup_detector(void)
|
||||
return 0;
|
||||
}
|
||||
early_initcall(disable_hardlockup_detector);
|
||||
#endif /* CONFIG_HARDLOCKUP_DETECTOR */
|
||||
|
||||
#ifdef CONFIG_PPC_BOOK3S_64
|
||||
static enum l1d_flush_type enabled_flush_types;
|
||||
@@ -716,9 +717,6 @@ static void do_nothing(void *unused)
|
||||
|
||||
void rfi_flush_enable(bool enable)
|
||||
{
|
||||
if (rfi_flush == enable)
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
do_rfi_flush_fixups(enabled_flush_types);
|
||||
on_each_cpu(do_nothing, NULL, 1);
|
||||
@@ -728,11 +726,15 @@ void rfi_flush_enable(bool enable)
|
||||
rfi_flush = enable;
|
||||
}
|
||||
|
||||
static void init_fallback_flush(void)
|
||||
static void __ref init_fallback_flush(void)
|
||||
{
|
||||
u64 l1d_size, limit;
|
||||
int cpu;
|
||||
|
||||
/* Only allocate the fallback flush area once (at boot time). */
|
||||
if (l1d_flush_fallback_area)
|
||||
return;
|
||||
|
||||
l1d_size = ppc64_caches.dsize;
|
||||
limit = min(safe_stack_limit(), ppc64_rma_size);
|
||||
|
||||
@@ -750,18 +752,18 @@ static void init_fallback_flush(void)
|
||||
}
|
||||
}
|
||||
|
||||
void __init setup_rfi_flush(enum l1d_flush_type types, bool enable)
|
||||
void setup_rfi_flush(enum l1d_flush_type types, bool enable)
|
||||
{
|
||||
if (types & L1D_FLUSH_FALLBACK) {
|
||||
pr_info("rfi-flush: Using fallback displacement flush\n");
|
||||
pr_info("rfi-flush: fallback displacement flush available\n");
|
||||
init_fallback_flush();
|
||||
}
|
||||
|
||||
if (types & L1D_FLUSH_ORI)
|
||||
pr_info("rfi-flush: Using ori type flush\n");
|
||||
pr_info("rfi-flush: ori type flush available\n");
|
||||
|
||||
if (types & L1D_FLUSH_MTTRIG)
|
||||
pr_info("rfi-flush: Using mttrig type flush\n");
|
||||
pr_info("rfi-flush: mttrig type flush available\n");
|
||||
|
||||
enabled_flush_types = types;
|
||||
|
||||
@@ -772,13 +774,19 @@ void __init setup_rfi_flush(enum l1d_flush_type types, bool enable)
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int rfi_flush_set(void *data, u64 val)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
if (val == 1)
|
||||
rfi_flush_enable(true);
|
||||
enable = true;
|
||||
else if (val == 0)
|
||||
rfi_flush_enable(false);
|
||||
enable = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Only do anything if we're changing state */
|
||||
if (enable != rfi_flush)
|
||||
rfi_flush_enable(enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -797,13 +805,4 @@ static __init int rfi_flush_debugfs_init(void)
|
||||
}
|
||||
device_initcall(rfi_flush_debugfs_init);
|
||||
#endif
|
||||
|
||||
ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (rfi_flush)
|
||||
return sprintf(buf, "Mitigation: RFI Flush\n");
|
||||
|
||||
return sprintf(buf, "Vulnerable\n");
|
||||
}
|
||||
#endif /* CONFIG_PPC_BOOK3S_64 */
|
||||
#endif
|
||||
|
||||
@@ -133,6 +133,20 @@ SECTIONS
|
||||
RODATA
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
. = ALIGN(8);
|
||||
__stf_entry_barrier_fixup : AT(ADDR(__stf_entry_barrier_fixup) - LOAD_OFFSET) {
|
||||
__start___stf_entry_barrier_fixup = .;
|
||||
*(__stf_entry_barrier_fixup)
|
||||
__stop___stf_entry_barrier_fixup = .;
|
||||
}
|
||||
|
||||
. = ALIGN(8);
|
||||
__stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) {
|
||||
__start___stf_exit_barrier_fixup = .;
|
||||
*(__stf_exit_barrier_fixup)
|
||||
__stop___stf_exit_barrier_fixup = .;
|
||||
}
|
||||
|
||||
. = ALIGN(8);
|
||||
__rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) {
|
||||
__start___rfi_flush_fixup = .;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/security_features.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
@@ -117,6 +118,120 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_BOOK3S_64
|
||||
void do_stf_entry_barrier_fixups(enum stf_barrier_type types)
|
||||
{
|
||||
unsigned int instrs[3], *dest;
|
||||
long *start, *end;
|
||||
int i;
|
||||
|
||||
start = PTRRELOC(&__start___stf_entry_barrier_fixup),
|
||||
end = PTRRELOC(&__stop___stf_entry_barrier_fixup);
|
||||
|
||||
instrs[0] = 0x60000000; /* nop */
|
||||
instrs[1] = 0x60000000; /* nop */
|
||||
instrs[2] = 0x60000000; /* nop */
|
||||
|
||||
i = 0;
|
||||
if (types & STF_BARRIER_FALLBACK) {
|
||||
instrs[i++] = 0x7d4802a6; /* mflr r10 */
|
||||
instrs[i++] = 0x60000000; /* branch patched below */
|
||||
instrs[i++] = 0x7d4803a6; /* mtlr r10 */
|
||||
} else if (types & STF_BARRIER_EIEIO) {
|
||||
instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
|
||||
} else if (types & STF_BARRIER_SYNC_ORI) {
|
||||
instrs[i++] = 0x7c0004ac; /* hwsync */
|
||||
instrs[i++] = 0xe94d0000; /* ld r10,0(r13) */
|
||||
instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
|
||||
}
|
||||
|
||||
for (i = 0; start < end; start++, i++) {
|
||||
dest = (void *)start + *start;
|
||||
|
||||
pr_devel("patching dest %lx\n", (unsigned long)dest);
|
||||
|
||||
patch_instruction(dest, instrs[0]);
|
||||
|
||||
if (types & STF_BARRIER_FALLBACK)
|
||||
patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback,
|
||||
BRANCH_SET_LINK);
|
||||
else
|
||||
patch_instruction(dest + 1, instrs[1]);
|
||||
|
||||
patch_instruction(dest + 2, instrs[2]);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i,
|
||||
(types == STF_BARRIER_NONE) ? "no" :
|
||||
(types == STF_BARRIER_FALLBACK) ? "fallback" :
|
||||
(types == STF_BARRIER_EIEIO) ? "eieio" :
|
||||
(types == (STF_BARRIER_SYNC_ORI)) ? "hwsync"
|
||||
: "unknown");
|
||||
}
|
||||
|
||||
void do_stf_exit_barrier_fixups(enum stf_barrier_type types)
|
||||
{
|
||||
unsigned int instrs[6], *dest;
|
||||
long *start, *end;
|
||||
int i;
|
||||
|
||||
start = PTRRELOC(&__start___stf_exit_barrier_fixup),
|
||||
end = PTRRELOC(&__stop___stf_exit_barrier_fixup);
|
||||
|
||||
instrs[0] = 0x60000000; /* nop */
|
||||
instrs[1] = 0x60000000; /* nop */
|
||||
instrs[2] = 0x60000000; /* nop */
|
||||
instrs[3] = 0x60000000; /* nop */
|
||||
instrs[4] = 0x60000000; /* nop */
|
||||
instrs[5] = 0x60000000; /* nop */
|
||||
|
||||
i = 0;
|
||||
if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) {
|
||||
if (cpu_has_feature(CPU_FTR_HVMODE)) {
|
||||
instrs[i++] = 0x7db14ba6; /* mtspr 0x131, r13 (HSPRG1) */
|
||||
instrs[i++] = 0x7db04aa6; /* mfspr r13, 0x130 (HSPRG0) */
|
||||
} else {
|
||||
instrs[i++] = 0x7db243a6; /* mtsprg 2,r13 */
|
||||
instrs[i++] = 0x7db142a6; /* mfsprg r13,1 */
|
||||
}
|
||||
instrs[i++] = 0x7c0004ac; /* hwsync */
|
||||
instrs[i++] = 0xe9ad0000; /* ld r13,0(r13) */
|
||||
instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */
|
||||
if (cpu_has_feature(CPU_FTR_HVMODE)) {
|
||||
instrs[i++] = 0x7db14aa6; /* mfspr r13, 0x131 (HSPRG1) */
|
||||
} else {
|
||||
instrs[i++] = 0x7db242a6; /* mfsprg r13,2 */
|
||||
}
|
||||
} else if (types & STF_BARRIER_EIEIO) {
|
||||
instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */
|
||||
}
|
||||
|
||||
for (i = 0; start < end; start++, i++) {
|
||||
dest = (void *)start + *start;
|
||||
|
||||
pr_devel("patching dest %lx\n", (unsigned long)dest);
|
||||
|
||||
patch_instruction(dest, instrs[0]);
|
||||
patch_instruction(dest + 1, instrs[1]);
|
||||
patch_instruction(dest + 2, instrs[2]);
|
||||
patch_instruction(dest + 3, instrs[3]);
|
||||
patch_instruction(dest + 4, instrs[4]);
|
||||
patch_instruction(dest + 5, instrs[5]);
|
||||
}
|
||||
printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i,
|
||||
(types == STF_BARRIER_NONE) ? "no" :
|
||||
(types == STF_BARRIER_FALLBACK) ? "fallback" :
|
||||
(types == STF_BARRIER_EIEIO) ? "eieio" :
|
||||
(types == (STF_BARRIER_SYNC_ORI)) ? "hwsync"
|
||||
: "unknown");
|
||||
}
|
||||
|
||||
|
||||
void do_stf_barrier_fixups(enum stf_barrier_type types)
|
||||
{
|
||||
do_stf_entry_barrier_fixups(types);
|
||||
do_stf_exit_barrier_fixups(types);
|
||||
}
|
||||
|
||||
void do_rfi_flush_fixups(enum l1d_flush_type types)
|
||||
{
|
||||
unsigned int instrs[3], *dest;
|
||||
@@ -153,7 +268,14 @@ void do_rfi_flush_fixups(enum l1d_flush_type types)
|
||||
patch_instruction(dest + 2, instrs[2]);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "rfi-flush: patched %d locations\n", i);
|
||||
printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i,
|
||||
(types == L1D_FLUSH_NONE) ? "no" :
|
||||
(types == L1D_FLUSH_FALLBACK) ? "fallback displacement" :
|
||||
(types & L1D_FLUSH_ORI) ? (types & L1D_FLUSH_MTTRIG)
|
||||
? "ori+mttrig type"
|
||||
: "ori type" :
|
||||
(types & L1D_FLUSH_MTTRIG) ? "mttrig type"
|
||||
: "unknown");
|
||||
}
|
||||
#endif /* CONFIG_PPC_BOOK3S_64 */
|
||||
|
||||
|
||||
@@ -37,53 +37,92 @@
|
||||
#include <asm/smp.h>
|
||||
#include <asm/tm.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/security_features.h>
|
||||
|
||||
#include "powernv.h"
|
||||
|
||||
|
||||
static bool fw_feature_is(const char *state, const char *name,
|
||||
struct device_node *fw_features)
|
||||
{
|
||||
struct device_node *np;
|
||||
bool rc = false;
|
||||
|
||||
np = of_get_child_by_name(fw_features, name);
|
||||
if (np) {
|
||||
rc = of_property_read_bool(np, state);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void init_fw_feat_flags(struct device_node *np)
|
||||
{
|
||||
if (fw_feature_is("enabled", "inst-spec-barrier-ori31,31,0", np))
|
||||
security_ftr_set(SEC_FTR_SPEC_BAR_ORI31);
|
||||
|
||||
if (fw_feature_is("enabled", "fw-bcctrl-serialized", np))
|
||||
security_ftr_set(SEC_FTR_BCCTRL_SERIALISED);
|
||||
|
||||
if (fw_feature_is("enabled", "inst-l1d-flush-ori30,30,0", np))
|
||||
security_ftr_set(SEC_FTR_L1D_FLUSH_ORI30);
|
||||
|
||||
if (fw_feature_is("enabled", "inst-l1d-flush-trig2", np))
|
||||
security_ftr_set(SEC_FTR_L1D_FLUSH_TRIG2);
|
||||
|
||||
if (fw_feature_is("enabled", "fw-l1d-thread-split", np))
|
||||
security_ftr_set(SEC_FTR_L1D_THREAD_PRIV);
|
||||
|
||||
if (fw_feature_is("enabled", "fw-count-cache-disabled", np))
|
||||
security_ftr_set(SEC_FTR_COUNT_CACHE_DISABLED);
|
||||
|
||||
/*
|
||||
* The features below are enabled by default, so we instead look to see
|
||||
* if firmware has *disabled* them, and clear them if so.
|
||||
*/
|
||||
if (fw_feature_is("disabled", "speculation-policy-favor-security", np))
|
||||
security_ftr_clear(SEC_FTR_FAVOUR_SECURITY);
|
||||
|
||||
if (fw_feature_is("disabled", "needs-l1d-flush-msr-pr-0-to-1", np))
|
||||
security_ftr_clear(SEC_FTR_L1D_FLUSH_PR);
|
||||
|
||||
if (fw_feature_is("disabled", "needs-l1d-flush-msr-hv-1-to-0", np))
|
||||
security_ftr_clear(SEC_FTR_L1D_FLUSH_HV);
|
||||
|
||||
if (fw_feature_is("disabled", "needs-spec-barrier-for-bound-checks", np))
|
||||
security_ftr_clear(SEC_FTR_BNDS_CHK_SPEC_BAR);
|
||||
}
|
||||
|
||||
static void pnv_setup_rfi_flush(void)
|
||||
{
|
||||
struct device_node *np, *fw_features;
|
||||
enum l1d_flush_type type;
|
||||
int enable;
|
||||
bool enable;
|
||||
|
||||
/* Default to fallback in case fw-features are not available */
|
||||
type = L1D_FLUSH_FALLBACK;
|
||||
enable = 1;
|
||||
|
||||
np = of_find_node_by_name(NULL, "ibm,opal");
|
||||
fw_features = of_get_child_by_name(np, "fw-features");
|
||||
of_node_put(np);
|
||||
|
||||
if (fw_features) {
|
||||
np = of_get_child_by_name(fw_features, "inst-l1d-flush-trig2");
|
||||
if (np && of_property_read_bool(np, "enabled"))
|
||||
init_fw_feat_flags(fw_features);
|
||||
of_node_put(fw_features);
|
||||
|
||||
if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_TRIG2))
|
||||
type = L1D_FLUSH_MTTRIG;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
np = of_get_child_by_name(fw_features, "inst-l1d-flush-ori30,30,0");
|
||||
if (np && of_property_read_bool(np, "enabled"))
|
||||
if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_ORI30))
|
||||
type = L1D_FLUSH_ORI;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
/* Enable unless firmware says NOT to */
|
||||
enable = 2;
|
||||
np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-hv-1-to-0");
|
||||
if (np && of_property_read_bool(np, "disabled"))
|
||||
enable--;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
np = of_get_child_by_name(fw_features, "needs-l1d-flush-msr-pr-0-to-1");
|
||||
if (np && of_property_read_bool(np, "disabled"))
|
||||
enable--;
|
||||
|
||||
of_node_put(np);
|
||||
of_node_put(fw_features);
|
||||
}
|
||||
|
||||
setup_rfi_flush(type, enable > 0);
|
||||
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \
|
||||
(security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || \
|
||||
security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV));
|
||||
|
||||
setup_rfi_flush(type, enable);
|
||||
}
|
||||
|
||||
static void __init pnv_setup_arch(void)
|
||||
@@ -91,6 +130,7 @@ static void __init pnv_setup_arch(void)
|
||||
set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
|
||||
|
||||
pnv_setup_rfi_flush();
|
||||
setup_stf_barrier();
|
||||
|
||||
/* Initialize SMP */
|
||||
pnv_smp_init();
|
||||
|
||||
@@ -314,6 +314,9 @@ void post_mobility_fixup(void)
|
||||
printk(KERN_ERR "Post-mobility device tree update "
|
||||
"failed: %d\n", rc);
|
||||
|
||||
/* Possibly switch to a new RFI flush type */
|
||||
pseries_setup_rfi_flush();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,4 +79,6 @@ extern struct pci_controller_ops pseries_pci_controller_ops;
|
||||
|
||||
unsigned long pseries_memory_block_size(void);
|
||||
|
||||
void pseries_setup_rfi_flush(void);
|
||||
|
||||
#endif /* _PSERIES_PSERIES_H */
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include <asm/reg.h>
|
||||
#include <asm/plpar_wrappers.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/security_features.h>
|
||||
|
||||
#include "pseries.h"
|
||||
|
||||
@@ -450,35 +451,78 @@ static void __init find_and_init_phbs(void)
|
||||
of_pci_check_probe_only();
|
||||
}
|
||||
|
||||
static void pseries_setup_rfi_flush(void)
|
||||
static void init_cpu_char_feature_flags(struct h_cpu_char_result *result)
|
||||
{
|
||||
/*
|
||||
* The features below are disabled by default, so we instead look to see
|
||||
* if firmware has *enabled* them, and set them if so.
|
||||
*/
|
||||
if (result->character & H_CPU_CHAR_SPEC_BAR_ORI31)
|
||||
security_ftr_set(SEC_FTR_SPEC_BAR_ORI31);
|
||||
|
||||
if (result->character & H_CPU_CHAR_BCCTRL_SERIALISED)
|
||||
security_ftr_set(SEC_FTR_BCCTRL_SERIALISED);
|
||||
|
||||
if (result->character & H_CPU_CHAR_L1D_FLUSH_ORI30)
|
||||
security_ftr_set(SEC_FTR_L1D_FLUSH_ORI30);
|
||||
|
||||
if (result->character & H_CPU_CHAR_L1D_FLUSH_TRIG2)
|
||||
security_ftr_set(SEC_FTR_L1D_FLUSH_TRIG2);
|
||||
|
||||
if (result->character & H_CPU_CHAR_L1D_THREAD_PRIV)
|
||||
security_ftr_set(SEC_FTR_L1D_THREAD_PRIV);
|
||||
|
||||
if (result->character & H_CPU_CHAR_COUNT_CACHE_DISABLED)
|
||||
security_ftr_set(SEC_FTR_COUNT_CACHE_DISABLED);
|
||||
|
||||
/*
|
||||
* The features below are enabled by default, so we instead look to see
|
||||
* if firmware has *disabled* them, and clear them if so.
|
||||
*/
|
||||
if (!(result->behaviour & H_CPU_BEHAV_FAVOUR_SECURITY))
|
||||
security_ftr_clear(SEC_FTR_FAVOUR_SECURITY);
|
||||
|
||||
if (!(result->behaviour & H_CPU_BEHAV_L1D_FLUSH_PR))
|
||||
security_ftr_clear(SEC_FTR_L1D_FLUSH_PR);
|
||||
|
||||
if (!(result->behaviour & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR))
|
||||
security_ftr_clear(SEC_FTR_BNDS_CHK_SPEC_BAR);
|
||||
}
|
||||
|
||||
void pseries_setup_rfi_flush(void)
|
||||
{
|
||||
struct h_cpu_char_result result;
|
||||
enum l1d_flush_type types;
|
||||
bool enable;
|
||||
long rc;
|
||||
|
||||
/* Enable by default */
|
||||
enable = true;
|
||||
/*
|
||||
* Set features to the defaults assumed by init_cpu_char_feature_flags()
|
||||
* so it can set/clear again any features that might have changed after
|
||||
* migration, and in case the hypercall fails and it is not even called.
|
||||
*/
|
||||
powerpc_security_features = SEC_FTR_DEFAULT;
|
||||
|
||||
rc = plpar_get_cpu_characteristics(&result);
|
||||
if (rc == H_SUCCESS) {
|
||||
types = L1D_FLUSH_NONE;
|
||||
if (rc == H_SUCCESS)
|
||||
init_cpu_char_feature_flags(&result);
|
||||
|
||||
if (result.character & H_CPU_CHAR_L1D_FLUSH_TRIG2)
|
||||
types |= L1D_FLUSH_MTTRIG;
|
||||
if (result.character & H_CPU_CHAR_L1D_FLUSH_ORI30)
|
||||
types |= L1D_FLUSH_ORI;
|
||||
/*
|
||||
* We're the guest so this doesn't apply to us, clear it to simplify
|
||||
* handling of it elsewhere.
|
||||
*/
|
||||
security_ftr_clear(SEC_FTR_L1D_FLUSH_HV);
|
||||
|
||||
/* Use fallback if nothing set in hcall */
|
||||
if (types == L1D_FLUSH_NONE)
|
||||
types = L1D_FLUSH_FALLBACK;
|
||||
types = L1D_FLUSH_FALLBACK;
|
||||
|
||||
if (!(result.behaviour & H_CPU_BEHAV_L1D_FLUSH_PR))
|
||||
enable = false;
|
||||
} else {
|
||||
/* Default to fallback if case hcall is not available */
|
||||
types = L1D_FLUSH_FALLBACK;
|
||||
}
|
||||
if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_TRIG2))
|
||||
types |= L1D_FLUSH_MTTRIG;
|
||||
|
||||
if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_ORI30))
|
||||
types |= L1D_FLUSH_ORI;
|
||||
|
||||
enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \
|
||||
security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR);
|
||||
|
||||
setup_rfi_flush(types, enable);
|
||||
}
|
||||
@@ -501,6 +545,7 @@ static void __init pSeries_setup_arch(void)
|
||||
fwnmi_init();
|
||||
|
||||
pseries_setup_rfi_flush();
|
||||
setup_stf_barrier();
|
||||
|
||||
/* By default, only probe PCI (can be overridden by rtas_pci) */
|
||||
pci_add_flags(PCI_PROBE_ONLY);
|
||||
|
||||
@@ -908,7 +908,7 @@ static int register_services(struct ds_info *dp)
|
||||
pbuf.req.handle = cp->handle;
|
||||
pbuf.req.major = 1;
|
||||
pbuf.req.minor = 0;
|
||||
strcpy(pbuf.req.svc_id, cp->service_id);
|
||||
strcpy(pbuf.id_buf, cp->service_id);
|
||||
|
||||
err = __ds_send(lp, &pbuf, msg_len);
|
||||
if (err > 0)
|
||||
|
||||
@@ -5,26 +5,26 @@
|
||||
.align 4
|
||||
ENTRY(__multi3) /* %o0 = u, %o1 = v */
|
||||
mov %o1, %g1
|
||||
srl %o3, 0, %g4
|
||||
mulx %g4, %g1, %o1
|
||||
srl %o3, 0, %o4
|
||||
mulx %o4, %g1, %o1
|
||||
srlx %g1, 0x20, %g3
|
||||
mulx %g3, %g4, %g5
|
||||
sllx %g5, 0x20, %o5
|
||||
srl %g1, 0, %g4
|
||||
mulx %g3, %o4, %g7
|
||||
sllx %g7, 0x20, %o5
|
||||
srl %g1, 0, %o4
|
||||
sub %o1, %o5, %o5
|
||||
srlx %o5, 0x20, %o5
|
||||
addcc %g5, %o5, %g5
|
||||
addcc %g7, %o5, %g7
|
||||
srlx %o3, 0x20, %o5
|
||||
mulx %g4, %o5, %g4
|
||||
mulx %o4, %o5, %o4
|
||||
mulx %g3, %o5, %o5
|
||||
sethi %hi(0x80000000), %g3
|
||||
addcc %g5, %g4, %g5
|
||||
srlx %g5, 0x20, %g5
|
||||
addcc %g7, %o4, %g7
|
||||
srlx %g7, 0x20, %g7
|
||||
add %g3, %g3, %g3
|
||||
movcc %xcc, %g0, %g3
|
||||
addcc %o5, %g5, %o5
|
||||
sllx %g4, 0x20, %g4
|
||||
add %o1, %g4, %o1
|
||||
addcc %o5, %g7, %o5
|
||||
sllx %o4, 0x20, %o4
|
||||
add %o1, %o4, %o1
|
||||
add %o5, %g3, %g2
|
||||
mulx %g1, %o2, %g1
|
||||
add %g1, %g2, %g1
|
||||
|
||||
@@ -472,6 +472,12 @@ static void __init xen_init_cpuid_mask(void)
|
||||
cpuid_leaf1_ecx_set_mask = (1 << (X86_FEATURE_MWAIT % 32));
|
||||
}
|
||||
|
||||
static void __init xen_init_capabilities(void)
|
||||
{
|
||||
if (xen_pv_domain())
|
||||
setup_force_cpu_cap(X86_FEATURE_XENPV);
|
||||
}
|
||||
|
||||
static void xen_set_debugreg(int reg, unsigned long val)
|
||||
{
|
||||
HYPERVISOR_set_debugreg(reg, val);
|
||||
@@ -1634,6 +1640,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
|
||||
|
||||
xen_init_irq_ops();
|
||||
xen_init_cpuid_mask();
|
||||
xen_init_capabilities();
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
/*
|
||||
@@ -1978,12 +1985,6 @@ bool xen_hvm_need_lapic(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xen_hvm_need_lapic);
|
||||
|
||||
static void xen_set_cpu_features(struct cpuinfo_x86 *c)
|
||||
{
|
||||
if (xen_pv_domain())
|
||||
set_cpu_cap(c, X86_FEATURE_XENPV);
|
||||
}
|
||||
|
||||
static void xen_pin_vcpu(int cpu)
|
||||
{
|
||||
static bool disable_pinning;
|
||||
@@ -2030,7 +2031,6 @@ const struct hypervisor_x86 x86_hyper_xen = {
|
||||
.init_platform = xen_hvm_guest_init,
|
||||
#endif
|
||||
.x2apic_available = xen_x2apic_para_available,
|
||||
.set_cpu_features = xen_set_cpu_features,
|
||||
.pin_vcpu = xen_pin_vcpu,
|
||||
};
|
||||
EXPORT_SYMBOL(x86_hyper_xen);
|
||||
|
||||
@@ -551,7 +551,7 @@ EXPORT_SYMBOL_GPL(dma_buf_detach);
|
||||
struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct sg_table *sg_table = ERR_PTR(-EINVAL);
|
||||
struct sg_table *sg_table;
|
||||
|
||||
might_sleep();
|
||||
|
||||
|
||||
@@ -1065,6 +1065,7 @@ int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE])
|
||||
static const u16 psr_setup_time_us[] = {
|
||||
PSR_SETUP_TIME(330),
|
||||
PSR_SETUP_TIME(275),
|
||||
PSR_SETUP_TIME(220),
|
||||
PSR_SETUP_TIME(165),
|
||||
PSR_SETUP_TIME(110),
|
||||
PSR_SETUP_TIME(55),
|
||||
|
||||
@@ -864,6 +864,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = intel_no_lvds_dmi_callback,
|
||||
.ident = "Radiant P845",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "P845"),
|
||||
},
|
||||
},
|
||||
|
||||
{ } /* terminating entry */
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/stm.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "stm.h"
|
||||
|
||||
#include <uapi/linux/stm.h>
|
||||
@@ -682,7 +683,7 @@ static void stm_device_release(struct device *dev)
|
||||
{
|
||||
struct stm_device *stm = to_stm_device(dev);
|
||||
|
||||
kfree(stm);
|
||||
vfree(stm);
|
||||
}
|
||||
|
||||
int stm_register_device(struct device *parent, struct stm_data *stm_data,
|
||||
@@ -699,7 +700,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data,
|
||||
return -EINVAL;
|
||||
|
||||
nmasters = stm_data->sw_end - stm_data->sw_start + 1;
|
||||
stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL);
|
||||
stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *));
|
||||
if (!stm)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -752,7 +753,7 @@ err_device:
|
||||
/* matches device_initialize() above */
|
||||
put_device(&stm->dev);
|
||||
err_free:
|
||||
kfree(stm);
|
||||
vfree(stm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
|
||||
if ((length == 0) || (bytes_per_datum == 0))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Make sure we don't overflow an unsigned int after kfifo rounds up to
|
||||
* the next power of 2.
|
||||
*/
|
||||
if (roundup_pow_of_two(length) > UINT_MAX / bytes_per_datum)
|
||||
return -EINVAL;
|
||||
|
||||
return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
|
||||
bytes_per_datum, GFP_KERNEL);
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ static int __ib_cache_gid_get(struct ib_device *ib_dev, u8 port, int index,
|
||||
return -EINVAL;
|
||||
|
||||
if (table->data_vec[index].props & GID_TABLE_ENTRY_INVALID)
|
||||
return -EAGAIN;
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(gid, &table->data_vec[index].gid, sizeof(*gid));
|
||||
if (attr) {
|
||||
|
||||
@@ -130,7 +130,7 @@ static int elan_smbus_get_baseline_data(struct i2c_client *client,
|
||||
bool max_baseline, u8 *value)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
max_baseline ?
|
||||
@@ -149,7 +149,7 @@ static int elan_smbus_get_version(struct i2c_client *client,
|
||||
bool iap, u8 *version)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
iap ? ETP_SMBUS_IAP_VERSION_CMD :
|
||||
@@ -169,7 +169,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
|
||||
u8 *ic_type, u8 *version)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
ETP_SMBUS_SM_VERSION_CMD, val);
|
||||
@@ -186,7 +186,7 @@ static int elan_smbus_get_sm_version(struct i2c_client *client,
|
||||
static int elan_smbus_get_product_id(struct i2c_client *client, u16 *id)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
ETP_SMBUS_UNIQUEID_CMD, val);
|
||||
@@ -203,7 +203,7 @@ static int elan_smbus_get_checksum(struct i2c_client *client,
|
||||
bool iap, u16 *csum)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
iap ? ETP_SMBUS_FW_CHECKSUM_CMD :
|
||||
@@ -224,7 +224,7 @@ static int elan_smbus_get_max(struct i2c_client *client,
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
|
||||
if (ret != 3) {
|
||||
@@ -244,7 +244,7 @@ static int elan_smbus_get_resolution(struct i2c_client *client,
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val);
|
||||
if (ret != 3) {
|
||||
@@ -265,7 +265,7 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val);
|
||||
if (ret != 3) {
|
||||
@@ -292,7 +292,7 @@ static int elan_smbus_iap_get_mode(struct i2c_client *client,
|
||||
{
|
||||
int error;
|
||||
u16 constant;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
error = i2c_smbus_read_block_data(client, ETP_SMBUS_IAP_CTRL_CMD, val);
|
||||
if (error < 0) {
|
||||
@@ -343,7 +343,7 @@ static int elan_smbus_prepare_fw_update(struct i2c_client *client)
|
||||
int len;
|
||||
int error;
|
||||
enum tp_mode mode;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
u8 cmd[4] = {0x0F, 0x78, 0x00, 0x06};
|
||||
u16 password;
|
||||
|
||||
@@ -417,7 +417,7 @@ static int elan_smbus_write_fw_block(struct i2c_client *client,
|
||||
struct device *dev = &client->dev;
|
||||
int error;
|
||||
u16 result;
|
||||
u8 val[3];
|
||||
u8 val[I2C_SMBUS_BLOCK_MAX] = {0};
|
||||
|
||||
/*
|
||||
* Due to the limitation of smbus protocol limiting
|
||||
|
||||
@@ -120,11 +120,10 @@ static void gic_redist_wait_for_rwp(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM64
|
||||
static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
|
||||
|
||||
static u64 __maybe_unused gic_read_iar(void)
|
||||
{
|
||||
if (static_branch_unlikely(&is_cavium_thunderx))
|
||||
if (cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_23154))
|
||||
return gic_read_iar_cavium_thunderx();
|
||||
else
|
||||
return gic_read_iar_common();
|
||||
@@ -908,14 +907,6 @@ static const struct irq_domain_ops partition_domain_ops = {
|
||||
.select = gic_irq_domain_select,
|
||||
};
|
||||
|
||||
static void gicv3_enable_quirks(void)
|
||||
{
|
||||
#ifdef CONFIG_ARM64
|
||||
if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_23154))
|
||||
static_branch_enable(&is_cavium_thunderx);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __init gic_init_bases(void __iomem *dist_base,
|
||||
struct redist_region *rdist_regs,
|
||||
u32 nr_redist_regions,
|
||||
@@ -938,8 +929,6 @@ static int __init gic_init_bases(void __iomem *dist_base,
|
||||
gic_data.nr_redist_regions = nr_redist_regions;
|
||||
gic_data.redist_stride = redist_stride;
|
||||
|
||||
gicv3_enable_quirks();
|
||||
|
||||
/*
|
||||
* Find out how many interrupts are supported.
|
||||
* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
|
||||
|
||||
@@ -142,16 +142,17 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
|
||||
struct mlx4_en_rx_alloc *frags,
|
||||
int i)
|
||||
{
|
||||
const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
|
||||
u32 next_frag_end = frags[i].page_offset + 2 * frag_info->frag_stride;
|
||||
if (frags[i].page) {
|
||||
const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
|
||||
u32 next_frag_end = frags[i].page_offset +
|
||||
2 * frag_info->frag_stride;
|
||||
|
||||
|
||||
if (next_frag_end > frags[i].page_size)
|
||||
dma_unmap_page(priv->ddev, frags[i].dma, frags[i].page_size,
|
||||
frag_info->dma_dir);
|
||||
|
||||
if (frags[i].page)
|
||||
if (next_frag_end > frags[i].page_size) {
|
||||
dma_unmap_page(priv->ddev, frags[i].dma,
|
||||
frags[i].page_size, frag_info->dma_dir);
|
||||
}
|
||||
put_page(frags[i].page);
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx4_en_init_allocator(struct mlx4_en_priv *priv,
|
||||
@@ -586,21 +587,28 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
|
||||
int length)
|
||||
{
|
||||
struct skb_frag_struct *skb_frags_rx = skb_shinfo(skb)->frags;
|
||||
struct mlx4_en_frag_info *frag_info;
|
||||
int nr;
|
||||
dma_addr_t dma;
|
||||
|
||||
/* Collect used fragments while replacing them in the HW descriptors */
|
||||
for (nr = 0; nr < priv->num_frags; nr++) {
|
||||
frag_info = &priv->frag_info[nr];
|
||||
struct mlx4_en_frag_info *frag_info = &priv->frag_info[nr];
|
||||
u32 next_frag_end = frags[nr].page_offset +
|
||||
2 * frag_info->frag_stride;
|
||||
|
||||
if (length <= frag_info->frag_prefix_size)
|
||||
break;
|
||||
if (unlikely(!frags[nr].page))
|
||||
goto fail;
|
||||
|
||||
dma = be64_to_cpu(rx_desc->data[nr].addr);
|
||||
dma_sync_single_for_cpu(priv->ddev, dma, frag_info->frag_size,
|
||||
DMA_FROM_DEVICE);
|
||||
if (next_frag_end > frags[nr].page_size)
|
||||
dma_unmap_page(priv->ddev, frags[nr].dma,
|
||||
frags[nr].page_size, frag_info->dma_dir);
|
||||
else
|
||||
dma_sync_single_for_cpu(priv->ddev, dma,
|
||||
frag_info->frag_size,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/* Save page reference in skb */
|
||||
__skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page);
|
||||
|
||||
@@ -304,9 +304,6 @@ static void _rtl92c_get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw,
|
||||
writeVal = 0x00000000;
|
||||
if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
|
||||
writeVal = writeVal - 0x06060606;
|
||||
else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
|
||||
TXHIGHPWRLEVEL_BT2)
|
||||
writeVal = writeVal;
|
||||
*(p_outwriteval + rf) = writeVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ static int ec_response_timed_out(void)
|
||||
static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
|
||||
struct cros_ec_command *msg)
|
||||
{
|
||||
struct ec_host_request *request;
|
||||
struct ec_host_response response;
|
||||
u8 sum = 0;
|
||||
int i;
|
||||
@@ -62,8 +61,6 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
|
||||
for (i = 0; i < ret; i++)
|
||||
outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i);
|
||||
|
||||
request = (struct ec_host_request *)ec->dout;
|
||||
|
||||
/* Here we go */
|
||||
outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ struct srp_internal {
|
||||
struct transport_container rport_attr_cont;
|
||||
};
|
||||
|
||||
static int scsi_is_srp_rport(const struct device *dev);
|
||||
|
||||
#define to_srp_internal(tmpl) container_of(tmpl, struct srp_internal, t)
|
||||
|
||||
#define dev_to_rport(d) container_of(d, struct srp_rport, dev)
|
||||
@@ -61,9 +63,24 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r)
|
||||
return dev_to_shost(r->dev.parent);
|
||||
}
|
||||
|
||||
static int find_child_rport(struct device *dev, void *data)
|
||||
{
|
||||
struct device **child = data;
|
||||
|
||||
if (scsi_is_srp_rport(dev)) {
|
||||
WARN_ON_ONCE(*child);
|
||||
*child = dev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost)
|
||||
{
|
||||
return transport_class_to_srp_rport(&shost->shost_gendev);
|
||||
struct device *child = NULL;
|
||||
|
||||
WARN_ON_ONCE(device_for_each_child(&shost->shost_gendev, &child,
|
||||
find_child_rport) < 0);
|
||||
return child ? dev_to_rport(child) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,7 +654,8 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd)
|
||||
struct srp_rport *rport = shost_to_rport(shost);
|
||||
|
||||
pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev));
|
||||
return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 &&
|
||||
return rport && rport->fast_io_fail_tmo < 0 &&
|
||||
rport->dev_loss_tmo < 0 &&
|
||||
i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
|
||||
BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ enum attr_idn {
|
||||
/* Descriptor idn for Query requests */
|
||||
enum desc_idn {
|
||||
QUERY_DESC_IDN_DEVICE = 0x0,
|
||||
QUERY_DESC_IDN_CONFIGURAION = 0x1,
|
||||
QUERY_DESC_IDN_CONFIGURATION = 0x1,
|
||||
QUERY_DESC_IDN_UNIT = 0x2,
|
||||
QUERY_DESC_IDN_RFU_0 = 0x3,
|
||||
QUERY_DESC_IDN_INTERCONNECT = 0x4,
|
||||
@@ -161,19 +161,13 @@ enum desc_header_offset {
|
||||
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
|
||||
};
|
||||
|
||||
enum ufs_desc_max_size {
|
||||
QUERY_DESC_DEVICE_MAX_SIZE = 0x1F,
|
||||
QUERY_DESC_CONFIGURAION_MAX_SIZE = 0x90,
|
||||
QUERY_DESC_UNIT_MAX_SIZE = 0x23,
|
||||
QUERY_DESC_INTERCONNECT_MAX_SIZE = 0x06,
|
||||
/*
|
||||
* Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes
|
||||
* of descriptor header.
|
||||
*/
|
||||
QUERY_DESC_STRING_MAX_SIZE = 0xFE,
|
||||
QUERY_DESC_GEOMETRY_MAX_SIZE = 0x44,
|
||||
QUERY_DESC_POWER_MAX_SIZE = 0x62,
|
||||
QUERY_DESC_RFU_MAX_SIZE = 0x00,
|
||||
enum ufs_desc_def_size {
|
||||
QUERY_DESC_DEVICE_DEF_SIZE = 0x40,
|
||||
QUERY_DESC_CONFIGURATION_DEF_SIZE = 0x90,
|
||||
QUERY_DESC_UNIT_DEF_SIZE = 0x23,
|
||||
QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06,
|
||||
QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44,
|
||||
QUERY_DESC_POWER_DEF_SIZE = 0x62,
|
||||
};
|
||||
|
||||
/* Unit descriptor parameters offsets in bytes*/
|
||||
@@ -522,4 +516,16 @@ struct ufs_dev_info {
|
||||
bool is_lu_power_on_wp;
|
||||
};
|
||||
|
||||
#define MAX_MODEL_LEN 16
|
||||
/**
|
||||
* ufs_dev_desc - ufs device details from the device descriptor
|
||||
*
|
||||
* @wmanufacturerid: card details
|
||||
* @model: card model
|
||||
*/
|
||||
struct ufs_dev_desc {
|
||||
u16 wmanufacturerid;
|
||||
char model[MAX_MODEL_LEN + 1];
|
||||
};
|
||||
|
||||
#endif /* End of Header */
|
||||
|
||||
@@ -21,41 +21,28 @@
|
||||
#define UFS_ANY_VENDOR 0xFFFF
|
||||
#define UFS_ANY_MODEL "ANY_MODEL"
|
||||
|
||||
#define MAX_MODEL_LEN 16
|
||||
|
||||
#define UFS_VENDOR_TOSHIBA 0x198
|
||||
#define UFS_VENDOR_SAMSUNG 0x1CE
|
||||
#define UFS_VENDOR_SKHYNIX 0x1AD
|
||||
|
||||
/**
|
||||
* ufs_device_info - ufs device details
|
||||
* @wmanufacturerid: card details
|
||||
* @model: card model
|
||||
*/
|
||||
struct ufs_device_info {
|
||||
u16 wmanufacturerid;
|
||||
char model[MAX_MODEL_LEN + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* ufs_dev_fix - ufs device quirk info
|
||||
* @card: ufs card details
|
||||
* @quirk: device quirk
|
||||
*/
|
||||
struct ufs_dev_fix {
|
||||
struct ufs_device_info card;
|
||||
struct ufs_dev_desc card;
|
||||
unsigned int quirk;
|
||||
};
|
||||
|
||||
#define END_FIX { { 0 }, 0 }
|
||||
|
||||
/* add specific device quirk */
|
||||
#define UFS_FIX(_vendor, _model, _quirk) \
|
||||
{ \
|
||||
.card.wmanufacturerid = (_vendor),\
|
||||
.card.model = (_model), \
|
||||
.quirk = (_quirk), \
|
||||
}
|
||||
#define UFS_FIX(_vendor, _model, _quirk) { \
|
||||
.card.wmanufacturerid = (_vendor),\
|
||||
.card.model = (_model), \
|
||||
.quirk = (_quirk), \
|
||||
}
|
||||
|
||||
/*
|
||||
* If UFS device is having issue in processing LCC (Line Control
|
||||
@@ -144,7 +131,4 @@ struct ufs_dev_fix {
|
||||
*/
|
||||
#define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME (1 << 8)
|
||||
|
||||
struct ufs_hba;
|
||||
void ufs_advertise_fixup_device(struct ufs_hba *hba);
|
||||
|
||||
#endif /* UFS_QUIRKS_H_ */
|
||||
|
||||
@@ -99,19 +99,6 @@
|
||||
_ret; \
|
||||
})
|
||||
|
||||
static u32 ufs_query_desc_max_size[] = {
|
||||
QUERY_DESC_DEVICE_MAX_SIZE,
|
||||
QUERY_DESC_CONFIGURAION_MAX_SIZE,
|
||||
QUERY_DESC_UNIT_MAX_SIZE,
|
||||
QUERY_DESC_RFU_MAX_SIZE,
|
||||
QUERY_DESC_INTERCONNECT_MAX_SIZE,
|
||||
QUERY_DESC_STRING_MAX_SIZE,
|
||||
QUERY_DESC_RFU_MAX_SIZE,
|
||||
QUERY_DESC_GEOMETRY_MAX_SIZE,
|
||||
QUERY_DESC_POWER_MAX_SIZE,
|
||||
QUERY_DESC_RFU_MAX_SIZE,
|
||||
};
|
||||
|
||||
enum {
|
||||
UFSHCD_MAX_CHANNEL = 0,
|
||||
UFSHCD_MAX_ID = 1,
|
||||
@@ -1973,7 +1960,7 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*buf_len <= QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) {
|
||||
if (*buf_len < QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) {
|
||||
dev_err(hba->dev, "%s: descriptor buffer size (%d) is out of range\n",
|
||||
__func__, *buf_len);
|
||||
err = -EINVAL;
|
||||
@@ -2052,6 +2039,92 @@ int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
|
||||
}
|
||||
EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
|
||||
|
||||
/**
|
||||
* ufshcd_read_desc_length - read the specified descriptor length from header
|
||||
* @hba: Pointer to adapter instance
|
||||
* @desc_id: descriptor idn value
|
||||
* @desc_index: descriptor index
|
||||
* @desc_length: pointer to variable to read the length of descriptor
|
||||
*
|
||||
* Return 0 in case of success, non-zero otherwise
|
||||
*/
|
||||
static int ufshcd_read_desc_length(struct ufs_hba *hba,
|
||||
enum desc_idn desc_id,
|
||||
int desc_index,
|
||||
int *desc_length)
|
||||
{
|
||||
int ret;
|
||||
u8 header[QUERY_DESC_HDR_SIZE];
|
||||
int header_len = QUERY_DESC_HDR_SIZE;
|
||||
|
||||
if (desc_id >= QUERY_DESC_IDN_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
|
||||
desc_id, desc_index, 0, header,
|
||||
&header_len);
|
||||
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: Failed to get descriptor header id %d",
|
||||
__func__, desc_id);
|
||||
return ret;
|
||||
} else if (desc_id != header[QUERY_DESC_DESC_TYPE_OFFSET]) {
|
||||
dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch",
|
||||
__func__, header[QUERY_DESC_DESC_TYPE_OFFSET],
|
||||
desc_id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
*desc_length = header[QUERY_DESC_LENGTH_OFFSET];
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_map_desc_id_to_length - map descriptor IDN to its length
|
||||
* @hba: Pointer to adapter instance
|
||||
* @desc_id: descriptor idn value
|
||||
* @desc_len: mapped desc length (out)
|
||||
*
|
||||
* Return 0 in case of success, non-zero otherwise
|
||||
*/
|
||||
int ufshcd_map_desc_id_to_length(struct ufs_hba *hba,
|
||||
enum desc_idn desc_id, int *desc_len)
|
||||
{
|
||||
switch (desc_id) {
|
||||
case QUERY_DESC_IDN_DEVICE:
|
||||
*desc_len = hba->desc_size.dev_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_POWER:
|
||||
*desc_len = hba->desc_size.pwr_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_GEOMETRY:
|
||||
*desc_len = hba->desc_size.geom_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_CONFIGURATION:
|
||||
*desc_len = hba->desc_size.conf_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_UNIT:
|
||||
*desc_len = hba->desc_size.unit_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_INTERCONNECT:
|
||||
*desc_len = hba->desc_size.interc_desc;
|
||||
break;
|
||||
case QUERY_DESC_IDN_STRING:
|
||||
*desc_len = QUERY_DESC_MAX_SIZE;
|
||||
break;
|
||||
case QUERY_DESC_IDN_RFU_0:
|
||||
case QUERY_DESC_IDN_RFU_1:
|
||||
*desc_len = 0;
|
||||
break;
|
||||
default:
|
||||
*desc_len = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ufshcd_map_desc_id_to_length);
|
||||
|
||||
/**
|
||||
* ufshcd_read_desc_param - read the specified descriptor parameter
|
||||
* @hba: Pointer to adapter instance
|
||||
@@ -2066,50 +2139,64 @@ EXPORT_SYMBOL(ufshcd_query_descriptor_retry);
|
||||
static int ufshcd_read_desc_param(struct ufs_hba *hba,
|
||||
enum desc_idn desc_id,
|
||||
int desc_index,
|
||||
u32 param_offset,
|
||||
u8 param_offset,
|
||||
u8 *param_read_buf,
|
||||
u32 param_size)
|
||||
u8 param_size)
|
||||
{
|
||||
int ret;
|
||||
u8 *desc_buf;
|
||||
u32 buff_len;
|
||||
int buff_len;
|
||||
bool is_kmalloc = true;
|
||||
|
||||
/* safety checks */
|
||||
if (desc_id >= QUERY_DESC_IDN_MAX)
|
||||
/* Safety check */
|
||||
if (desc_id >= QUERY_DESC_IDN_MAX || !param_size)
|
||||
return -EINVAL;
|
||||
|
||||
buff_len = ufs_query_desc_max_size[desc_id];
|
||||
if ((param_offset + param_size) > buff_len)
|
||||
return -EINVAL;
|
||||
/* Get the max length of descriptor from structure filled up at probe
|
||||
* time.
|
||||
*/
|
||||
ret = ufshcd_map_desc_id_to_length(hba, desc_id, &buff_len);
|
||||
|
||||
if (!param_offset && (param_size == buff_len)) {
|
||||
/* memory space already available to hold full descriptor */
|
||||
desc_buf = param_read_buf;
|
||||
is_kmalloc = false;
|
||||
} else {
|
||||
/* allocate memory to hold full descriptor */
|
||||
/* Sanity checks */
|
||||
if (ret || !buff_len) {
|
||||
dev_err(hba->dev, "%s: Failed to get full descriptor length",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check whether we need temp memory */
|
||||
if (param_offset != 0 || param_size < buff_len) {
|
||||
desc_buf = kmalloc(buff_len, GFP_KERNEL);
|
||||
if (!desc_buf)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
desc_buf = param_read_buf;
|
||||
is_kmalloc = false;
|
||||
}
|
||||
|
||||
/* Request for full descriptor */
|
||||
ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
|
||||
desc_id, desc_index, 0, desc_buf,
|
||||
&buff_len);
|
||||
|
||||
if (ret || (buff_len < ufs_query_desc_max_size[desc_id]) ||
|
||||
(desc_buf[QUERY_DESC_LENGTH_OFFSET] !=
|
||||
ufs_query_desc_max_size[desc_id])
|
||||
|| (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id)) {
|
||||
dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d param_offset %d buff_len %d ret %d",
|
||||
__func__, desc_id, param_offset, buff_len, ret);
|
||||
if (!ret)
|
||||
ret = -EINVAL;
|
||||
desc_id, desc_index, 0,
|
||||
desc_buf, &buff_len);
|
||||
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d",
|
||||
__func__, desc_id, desc_index, param_offset, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id) {
|
||||
dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header",
|
||||
__func__, desc_buf[QUERY_DESC_DESC_TYPE_OFFSET]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check wherher we will not copy more data, than available */
|
||||
if (is_kmalloc && param_size > buff_len)
|
||||
param_size = buff_len;
|
||||
|
||||
if (is_kmalloc)
|
||||
memcpy(param_read_buf, &desc_buf[param_offset], param_size);
|
||||
out:
|
||||
@@ -4818,8 +4905,8 @@ out:
|
||||
static void ufshcd_init_icc_levels(struct ufs_hba *hba)
|
||||
{
|
||||
int ret;
|
||||
int buff_len = QUERY_DESC_POWER_MAX_SIZE;
|
||||
u8 desc_buf[QUERY_DESC_POWER_MAX_SIZE];
|
||||
int buff_len = hba->desc_size.pwr_desc;
|
||||
u8 desc_buf[hba->desc_size.pwr_desc];
|
||||
|
||||
ret = ufshcd_read_power_desc(hba, desc_buf, buff_len);
|
||||
if (ret) {
|
||||
@@ -4912,16 +4999,15 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_get_device_info(struct ufs_hba *hba,
|
||||
struct ufs_device_info *card_data)
|
||||
static int ufs_get_device_desc(struct ufs_hba *hba,
|
||||
struct ufs_dev_desc *dev_desc)
|
||||
{
|
||||
int err;
|
||||
u8 model_index;
|
||||
u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
|
||||
u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
|
||||
u8 str_desc_buf[QUERY_DESC_MAX_SIZE + 1] = {0};
|
||||
u8 desc_buf[hba->desc_size.dev_desc];
|
||||
|
||||
err = ufshcd_read_device_desc(hba, desc_buf,
|
||||
QUERY_DESC_DEVICE_MAX_SIZE);
|
||||
err = ufshcd_read_device_desc(hba, desc_buf, hba->desc_size.dev_desc);
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: Failed reading Device Desc. err = %d\n",
|
||||
__func__, err);
|
||||
@@ -4932,50 +5018,40 @@ static int ufs_get_device_info(struct ufs_hba *hba,
|
||||
* getting vendor (manufacturerID) and Bank Index in big endian
|
||||
* format
|
||||
*/
|
||||
card_data->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 |
|
||||
dev_desc->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 |
|
||||
desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1];
|
||||
|
||||
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
|
||||
|
||||
err = ufshcd_read_string_desc(hba, model_index, str_desc_buf,
|
||||
QUERY_DESC_STRING_MAX_SIZE, ASCII_STD);
|
||||
QUERY_DESC_MAX_SIZE, ASCII_STD);
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
str_desc_buf[QUERY_DESC_STRING_MAX_SIZE] = '\0';
|
||||
strlcpy(card_data->model, (str_desc_buf + QUERY_DESC_HDR_SIZE),
|
||||
str_desc_buf[QUERY_DESC_MAX_SIZE] = '\0';
|
||||
strlcpy(dev_desc->model, (str_desc_buf + QUERY_DESC_HDR_SIZE),
|
||||
min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET],
|
||||
MAX_MODEL_LEN));
|
||||
|
||||
/* Null terminate the model string */
|
||||
card_data->model[MAX_MODEL_LEN] = '\0';
|
||||
dev_desc->model[MAX_MODEL_LEN] = '\0';
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void ufs_advertise_fixup_device(struct ufs_hba *hba)
|
||||
static void ufs_fixup_device_setup(struct ufs_hba *hba,
|
||||
struct ufs_dev_desc *dev_desc)
|
||||
{
|
||||
int err;
|
||||
struct ufs_dev_fix *f;
|
||||
struct ufs_device_info card_data;
|
||||
|
||||
card_data.wmanufacturerid = 0;
|
||||
|
||||
err = ufs_get_device_info(hba, &card_data);
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: Failed getting device info. err = %d\n",
|
||||
__func__, err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (f = ufs_fixups; f->quirk; f++) {
|
||||
if (((f->card.wmanufacturerid == card_data.wmanufacturerid) ||
|
||||
(f->card.wmanufacturerid == UFS_ANY_VENDOR)) &&
|
||||
(STR_PRFX_EQUAL(f->card.model, card_data.model) ||
|
||||
if ((f->card.wmanufacturerid == dev_desc->wmanufacturerid ||
|
||||
f->card.wmanufacturerid == UFS_ANY_VENDOR) &&
|
||||
(STR_PRFX_EQUAL(f->card.model, dev_desc->model) ||
|
||||
!strcmp(f->card.model, UFS_ANY_MODEL)))
|
||||
hba->dev_quirks |= f->quirk;
|
||||
}
|
||||
@@ -5145,6 +5221,51 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
|
||||
ufshcd_vops_apply_dev_quirks(hba);
|
||||
}
|
||||
|
||||
static void ufshcd_init_desc_sizes(struct ufs_hba *hba)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_DEVICE, 0,
|
||||
&hba->desc_size.dev_desc);
|
||||
if (err)
|
||||
hba->desc_size.dev_desc = QUERY_DESC_DEVICE_DEF_SIZE;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_POWER, 0,
|
||||
&hba->desc_size.pwr_desc);
|
||||
if (err)
|
||||
hba->desc_size.pwr_desc = QUERY_DESC_POWER_DEF_SIZE;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_INTERCONNECT, 0,
|
||||
&hba->desc_size.interc_desc);
|
||||
if (err)
|
||||
hba->desc_size.interc_desc = QUERY_DESC_INTERCONNECT_DEF_SIZE;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_CONFIGURATION, 0,
|
||||
&hba->desc_size.conf_desc);
|
||||
if (err)
|
||||
hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_UNIT, 0,
|
||||
&hba->desc_size.unit_desc);
|
||||
if (err)
|
||||
hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE;
|
||||
|
||||
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_GEOMETRY, 0,
|
||||
&hba->desc_size.geom_desc);
|
||||
if (err)
|
||||
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
|
||||
}
|
||||
|
||||
static void ufshcd_def_desc_sizes(struct ufs_hba *hba)
|
||||
{
|
||||
hba->desc_size.dev_desc = QUERY_DESC_DEVICE_DEF_SIZE;
|
||||
hba->desc_size.pwr_desc = QUERY_DESC_POWER_DEF_SIZE;
|
||||
hba->desc_size.interc_desc = QUERY_DESC_INTERCONNECT_DEF_SIZE;
|
||||
hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE;
|
||||
hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE;
|
||||
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_probe_hba - probe hba to detect device and initialize
|
||||
* @hba: per-adapter instance
|
||||
@@ -5153,6 +5274,7 @@ static void ufshcd_tune_unipro_params(struct ufs_hba *hba)
|
||||
*/
|
||||
static int ufshcd_probe_hba(struct ufs_hba *hba)
|
||||
{
|
||||
struct ufs_dev_desc card = {0};
|
||||
int ret;
|
||||
|
||||
ret = ufshcd_link_startup(hba);
|
||||
@@ -5176,7 +5298,17 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ufs_advertise_fixup_device(hba);
|
||||
/* Init check for device descriptor sizes */
|
||||
ufshcd_init_desc_sizes(hba);
|
||||
|
||||
ret = ufs_get_device_desc(hba, &card);
|
||||
if (ret) {
|
||||
dev_err(hba->dev, "%s: Failed getting device info. err = %d\n",
|
||||
__func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ufs_fixup_device_setup(hba, &card);
|
||||
ufshcd_tune_unipro_params(hba);
|
||||
|
||||
ret = ufshcd_set_vccq_rail_unused(hba,
|
||||
@@ -5202,6 +5334,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
|
||||
|
||||
/* set the state as operational after switching to desired gear */
|
||||
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
|
||||
|
||||
/*
|
||||
* If we are in error handling context or in power management callbacks
|
||||
* context, no need to scan the host
|
||||
@@ -6634,6 +6767,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
|
||||
hba->mmio_base = mmio_base;
|
||||
hba->irq = irq;
|
||||
|
||||
/* Set descriptor lengths to specification defaults */
|
||||
ufshcd_def_desc_sizes(hba);
|
||||
|
||||
err = ufshcd_hba_init(hba);
|
||||
if (err)
|
||||
goto out_error;
|
||||
|
||||
@@ -205,6 +205,15 @@ struct ufs_dev_cmd {
|
||||
struct ufs_query query;
|
||||
};
|
||||
|
||||
struct ufs_desc_size {
|
||||
int dev_desc;
|
||||
int pwr_desc;
|
||||
int geom_desc;
|
||||
int interc_desc;
|
||||
int unit_desc;
|
||||
int conf_desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ufs_clk_info - UFS clock related info
|
||||
* @list: list headed by hba->clk_list_head
|
||||
@@ -388,6 +397,7 @@ struct ufs_init_prefetch {
|
||||
* @clk_list_head: UFS host controller clocks list node head
|
||||
* @pwr_info: holds current power mode
|
||||
* @max_pwr_info: keeps the device max valid pwm
|
||||
* @desc_size: descriptor sizes reported by device
|
||||
* @urgent_bkops_lvl: keeps track of urgent bkops level for device
|
||||
* @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
|
||||
* device is known or not.
|
||||
@@ -563,6 +573,9 @@ struct ufs_hba {
|
||||
|
||||
enum bkops_status urgent_bkops_lvl;
|
||||
bool is_urgent_bkops_lvl_checked;
|
||||
|
||||
struct ufs_desc_size desc_size;
|
||||
|
||||
int latency_hist_enabled;
|
||||
struct io_latency_state io_lat_read;
|
||||
struct io_latency_state io_lat_write;
|
||||
@@ -739,6 +752,10 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
|
||||
enum flag_idn idn, bool *flag_res);
|
||||
int ufshcd_hold(struct ufs_hba *hba, bool async);
|
||||
void ufshcd_release(struct ufs_hba *hba);
|
||||
|
||||
int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
|
||||
int *desc_length);
|
||||
|
||||
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba);
|
||||
|
||||
/* Wrapper functions for safely calling variant operations */
|
||||
|
||||
@@ -2320,12 +2320,67 @@ static int __init pl011_console_setup(struct console *co, char *options)
|
||||
return uart_set_options(&uap->port, co, baud, parity, bits, flow);
|
||||
}
|
||||
|
||||
/**
|
||||
* pl011_console_match - non-standard console matching
|
||||
* @co: registering console
|
||||
* @name: name from console command line
|
||||
* @idx: index from console command line
|
||||
* @options: ptr to option string from console command line
|
||||
*
|
||||
* Only attempts to match console command lines of the form:
|
||||
* console=pl011,mmio|mmio32,<addr>[,<options>]
|
||||
* console=pl011,0x<addr>[,<options>]
|
||||
* This form is used to register an initial earlycon boot console and
|
||||
* replace it with the amba_console at pl011 driver init.
|
||||
*
|
||||
* Performs console setup for a match (as required by interface)
|
||||
* If no <options> are specified, then assume the h/w is already setup.
|
||||
*
|
||||
* Returns 0 if console matches; otherwise non-zero to use default matching
|
||||
*/
|
||||
static int __init pl011_console_match(struct console *co, char *name, int idx,
|
||||
char *options)
|
||||
{
|
||||
unsigned char iotype;
|
||||
resource_size_t addr;
|
||||
int i;
|
||||
|
||||
if (strcmp(name, "pl011") != 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (uart_parse_earlycon(options, &iotype, &addr, &options))
|
||||
return -ENODEV;
|
||||
|
||||
if (iotype != UPIO_MEM && iotype != UPIO_MEM32)
|
||||
return -ENODEV;
|
||||
|
||||
/* try to match the port specified on the command line */
|
||||
for (i = 0; i < ARRAY_SIZE(amba_ports); i++) {
|
||||
struct uart_port *port;
|
||||
|
||||
if (!amba_ports[i])
|
||||
continue;
|
||||
|
||||
port = &amba_ports[i]->port;
|
||||
|
||||
if (port->mapbase != addr)
|
||||
continue;
|
||||
|
||||
co->index = i;
|
||||
port->cons = co;
|
||||
return pl011_console_setup(co, options);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct uart_driver amba_reg;
|
||||
static struct console amba_console = {
|
||||
.name = "ttyAMA",
|
||||
.write = pl011_console_write,
|
||||
.device = uart_console_device,
|
||||
.setup = pl011_console_setup,
|
||||
.match = pl011_console_match,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &amba_reg,
|
||||
|
||||
@@ -33,7 +33,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
|
||||
static void cp210x_close(struct usb_serial_port *);
|
||||
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
|
||||
static void cp210x_get_termios_port(struct usb_serial_port *port,
|
||||
unsigned int *cflagp, unsigned int *baudp);
|
||||
tcflag_t *cflagp, unsigned int *baudp);
|
||||
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
|
||||
struct ktermios *);
|
||||
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
|
||||
@@ -728,7 +728,7 @@ static void cp210x_get_termios(struct tty_struct *tty,
|
||||
&tty->termios.c_cflag, &baud);
|
||||
tty_encode_baud_rate(tty, baud, baud);
|
||||
} else {
|
||||
unsigned int cflag;
|
||||
tcflag_t cflag;
|
||||
cflag = 0;
|
||||
cp210x_get_termios_port(port, &cflag, &baud);
|
||||
}
|
||||
@@ -739,10 +739,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
|
||||
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
|
||||
*/
|
||||
static void cp210x_get_termios_port(struct usb_serial_port *port,
|
||||
unsigned int *cflagp, unsigned int *baudp)
|
||||
tcflag_t *cflagp, unsigned int *baudp)
|
||||
{
|
||||
struct device *dev = &port->dev;
|
||||
unsigned int cflag;
|
||||
tcflag_t cflag;
|
||||
struct cp210x_flow_ctl flow_ctl;
|
||||
u32 baud;
|
||||
u16 bits;
|
||||
|
||||
3
fs/aio.c
3
fs/aio.c
@@ -636,9 +636,8 @@ static void free_ioctx_users(struct percpu_ref *ref)
|
||||
while (!list_empty(&ctx->active_reqs)) {
|
||||
req = list_first_entry(&ctx->active_reqs,
|
||||
struct aio_kiocb, ki_list);
|
||||
|
||||
list_del_init(&req->ki_list);
|
||||
kiocb_cancel(req);
|
||||
list_del_init(&req->ki_list);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&ctx->ctx_lock);
|
||||
|
||||
@@ -2034,6 +2034,93 @@ xfs_alloc_space_available(
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the agfl fields of the agf for inconsistency or corruption. The purpose
|
||||
* is to detect an agfl header padding mismatch between current and early v5
|
||||
* kernels. This problem manifests as a 1-slot size difference between the
|
||||
* on-disk flcount and the active [first, last] range of a wrapped agfl. This
|
||||
* may also catch variants of agfl count corruption unrelated to padding. Either
|
||||
* way, we'll reset the agfl and warn the user.
|
||||
*
|
||||
* Return true if a reset is required before the agfl can be used, false
|
||||
* otherwise.
|
||||
*/
|
||||
static bool
|
||||
xfs_agfl_needs_reset(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_agf *agf)
|
||||
{
|
||||
uint32_t f = be32_to_cpu(agf->agf_flfirst);
|
||||
uint32_t l = be32_to_cpu(agf->agf_fllast);
|
||||
uint32_t c = be32_to_cpu(agf->agf_flcount);
|
||||
int agfl_size = XFS_AGFL_SIZE(mp);
|
||||
int active;
|
||||
|
||||
/* no agfl header on v4 supers */
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The agf read verifier catches severe corruption of these fields.
|
||||
* Repeat some sanity checks to cover a packed -> unpacked mismatch if
|
||||
* the verifier allows it.
|
||||
*/
|
||||
if (f >= agfl_size || l >= agfl_size)
|
||||
return true;
|
||||
if (c > agfl_size)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Check consistency between the on-disk count and the active range. An
|
||||
* agfl padding mismatch manifests as an inconsistent flcount.
|
||||
*/
|
||||
if (c && l >= f)
|
||||
active = l - f + 1;
|
||||
else if (c)
|
||||
active = agfl_size - f + l + 1;
|
||||
else
|
||||
active = 0;
|
||||
|
||||
return active != c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the agfl to an empty state. Ignore/drop any existing blocks since the
|
||||
* agfl content cannot be trusted. Warn the user that a repair is required to
|
||||
* recover leaked blocks.
|
||||
*
|
||||
* The purpose of this mechanism is to handle filesystems affected by the agfl
|
||||
* header padding mismatch problem. A reset keeps the filesystem online with a
|
||||
* relatively minor free space accounting inconsistency rather than suffer the
|
||||
* inevitable crash from use of an invalid agfl block.
|
||||
*/
|
||||
static void
|
||||
xfs_agfl_reset(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_buf *agbp,
|
||||
struct xfs_perag *pag)
|
||||
{
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
|
||||
|
||||
ASSERT(pag->pagf_agflreset);
|
||||
trace_xfs_agfl_reset(mp, agf, 0, _RET_IP_);
|
||||
|
||||
xfs_warn(mp,
|
||||
"WARNING: Reset corrupted AGFL on AG %u. %d blocks leaked. "
|
||||
"Please unmount and run xfs_repair.",
|
||||
pag->pag_agno, pag->pagf_flcount);
|
||||
|
||||
agf->agf_flfirst = 0;
|
||||
agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
|
||||
agf->agf_flcount = 0;
|
||||
xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLLAST |
|
||||
XFS_AGF_FLCOUNT);
|
||||
|
||||
pag->pagf_flcount = 0;
|
||||
pag->pagf_agflreset = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide whether to use this allocation group for this allocation.
|
||||
* If so, fix up the btree freelist's size.
|
||||
@@ -2095,6 +2182,10 @@ xfs_alloc_fix_freelist(
|
||||
}
|
||||
}
|
||||
|
||||
/* reset a padding mismatched agfl before final free space check */
|
||||
if (pag->pagf_agflreset)
|
||||
xfs_agfl_reset(tp, agbp, pag);
|
||||
|
||||
/* If there isn't enough total space or single-extent, reject it. */
|
||||
need = xfs_alloc_min_freelist(mp, pag);
|
||||
if (!xfs_alloc_space_available(args, need, flags))
|
||||
@@ -2251,6 +2342,7 @@ xfs_alloc_get_freelist(
|
||||
agf->agf_flfirst = 0;
|
||||
|
||||
pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
|
||||
ASSERT(!pag->pagf_agflreset);
|
||||
be32_add_cpu(&agf->agf_flcount, -1);
|
||||
xfs_trans_agflist_delta(tp, -1);
|
||||
pag->pagf_flcount--;
|
||||
@@ -2362,6 +2454,7 @@ xfs_alloc_put_freelist(
|
||||
agf->agf_fllast = 0;
|
||||
|
||||
pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
|
||||
ASSERT(!pag->pagf_agflreset);
|
||||
be32_add_cpu(&agf->agf_flcount, 1);
|
||||
xfs_trans_agflist_delta(tp, 1);
|
||||
pag->pagf_flcount++;
|
||||
@@ -2568,6 +2661,7 @@ xfs_alloc_read_agf(
|
||||
pag->pagb_count = 0;
|
||||
pag->pagb_tree = RB_ROOT;
|
||||
pag->pagf_init = 1;
|
||||
pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else if (!XFS_FORCED_SHUTDOWN(mp)) {
|
||||
|
||||
@@ -368,6 +368,7 @@ typedef struct xfs_perag {
|
||||
char pagi_inodeok; /* The agi is ok for inodes */
|
||||
__uint8_t pagf_levels[XFS_BTNUM_AGF];
|
||||
/* # of levels in bno & cnt btree */
|
||||
bool pagf_agflreset; /* agfl requires reset before use */
|
||||
__uint32_t pagf_flcount; /* count of blocks in freelist */
|
||||
xfs_extlen_t pagf_freeblks; /* total free blocks */
|
||||
xfs_extlen_t pagf_longest; /* longest free space */
|
||||
|
||||
@@ -1516,7 +1516,7 @@ TRACE_EVENT(xfs_trans_commit_lsn,
|
||||
__entry->lsn)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xfs_agf,
|
||||
DECLARE_EVENT_CLASS(xfs_agf_class,
|
||||
TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags,
|
||||
unsigned long caller_ip),
|
||||
TP_ARGS(mp, agf, flags, caller_ip),
|
||||
@@ -1572,6 +1572,13 @@ TRACE_EVENT(xfs_agf,
|
||||
__entry->longest,
|
||||
(void *)__entry->caller_ip)
|
||||
);
|
||||
#define DEFINE_AGF_EVENT(name) \
|
||||
DEFINE_EVENT(xfs_agf_class, name, \
|
||||
TP_PROTO(struct xfs_mount *mp, struct xfs_agf *agf, int flags, \
|
||||
unsigned long caller_ip), \
|
||||
TP_ARGS(mp, agf, flags, caller_ip))
|
||||
DEFINE_AGF_EVENT(xfs_agf);
|
||||
DEFINE_AGF_EVENT(xfs_agfl_reset);
|
||||
|
||||
TRACE_EVENT(xfs_free_extent,
|
||||
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno,
|
||||
|
||||
@@ -338,7 +338,7 @@ struct tcp_sock {
|
||||
|
||||
/* Receiver queue space */
|
||||
struct {
|
||||
int space;
|
||||
u32 space;
|
||||
u32 seq;
|
||||
u32 time;
|
||||
} rcvq_space;
|
||||
|
||||
@@ -2379,7 +2379,7 @@ enum nl80211_attrs {
|
||||
#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
|
||||
#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
|
||||
|
||||
#define NL80211_WIPHY_NAME_MAXLEN 128
|
||||
#define NL80211_WIPHY_NAME_MAXLEN 64
|
||||
|
||||
#define NL80211_MAX_SUPP_RATES 32
|
||||
#define NL80211_MAX_SUPP_HT_RATES 77
|
||||
|
||||
@@ -481,9 +481,10 @@ clear_event_triggers(struct trace_array *tr)
|
||||
struct trace_event_file *file;
|
||||
|
||||
list_for_each_entry(file, &tr->events, list) {
|
||||
struct event_trigger_data *data;
|
||||
list_for_each_entry_rcu(data, &file->triggers, list) {
|
||||
struct event_trigger_data *data, *n;
|
||||
list_for_each_entry_safe(data, n, &file->triggers, list) {
|
||||
trace_event_trigger_enable_disable(file, 0);
|
||||
list_del_rcu(&data->list);
|
||||
if (data->ops->free)
|
||||
data->ops->free(data->ops, data);
|
||||
}
|
||||
|
||||
@@ -1393,7 +1393,7 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode)
|
||||
return ret;
|
||||
|
||||
mapping = page_mapping(page);
|
||||
migrate_dirty = mapping && mapping->a_ops->migratepage;
|
||||
migrate_dirty = !mapping || mapping->a_ops->migratepage;
|
||||
unlock_page(page);
|
||||
if (!migrate_dirty)
|
||||
return ret;
|
||||
|
||||
@@ -582,8 +582,8 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk,
|
||||
void tcp_rcv_space_adjust(struct sock *sk)
|
||||
{
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
u32 copied;
|
||||
int time;
|
||||
int copied;
|
||||
|
||||
time = tcp_time_stamp - tp->rcvq_space.time;
|
||||
if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0)
|
||||
@@ -605,12 +605,13 @@ void tcp_rcv_space_adjust(struct sock *sk)
|
||||
|
||||
if (sysctl_tcp_moderate_rcvbuf &&
|
||||
!(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
|
||||
int rcvwin, rcvmem, rcvbuf;
|
||||
int rcvmem, rcvbuf;
|
||||
u64 rcvwin;
|
||||
|
||||
/* minimal window to cope with packet losses, assuming
|
||||
* steady state. Add some cushion because of small variations.
|
||||
*/
|
||||
rcvwin = (copied << 1) + 16 * tp->advmss;
|
||||
rcvwin = ((u64)copied << 1) + 16 * tp->advmss;
|
||||
|
||||
/* If rate increased by 25%,
|
||||
* assume slow start, rcvwin = 3 * copied
|
||||
@@ -630,7 +631,8 @@ void tcp_rcv_space_adjust(struct sock *sk)
|
||||
while (tcp_win_from_space(rcvmem) < tp->advmss)
|
||||
rcvmem += 128;
|
||||
|
||||
rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]);
|
||||
do_div(rcvwin, tp->advmss);
|
||||
rcvbuf = min_t(u64, rcvwin * rcvmem, sysctl_tcp_rmem[2]);
|
||||
if (rcvbuf > sk->sk_rcvbuf) {
|
||||
sk->sk_rcvbuf = rcvbuf;
|
||||
|
||||
|
||||
@@ -389,14 +389,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
|
||||
xattr_value_len);
|
||||
if (result == 1) {
|
||||
bool digsig;
|
||||
|
||||
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
|
||||
return -EINVAL;
|
||||
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
|
||||
if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE))
|
||||
return -EPERM;
|
||||
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
|
||||
ima_reset_appraise_flags(d_backing_inode(dentry),
|
||||
(xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -1435,7 +1435,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
|
||||
scontext_len, &context, def_sid);
|
||||
if (rc == -EINVAL && force) {
|
||||
context.str = str;
|
||||
context.len = scontext_len;
|
||||
context.len = strlen(str) + 1;
|
||||
str = NULL;
|
||||
} else if (rc)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -274,7 +274,6 @@ int sst_dma_new(struct sst_dsp *sst)
|
||||
struct sst_pdata *sst_pdata = sst->pdata;
|
||||
struct sst_dma *dma;
|
||||
struct resource mem;
|
||||
const char *dma_dev_name;
|
||||
int ret = 0;
|
||||
|
||||
if (sst->pdata->resindex_dma_base == -1)
|
||||
@@ -285,7 +284,6 @@ int sst_dma_new(struct sst_dsp *sst)
|
||||
* is attached to the ADSP IP. */
|
||||
switch (sst->pdata->dma_engine) {
|
||||
case SST_DMA_TYPE_DW:
|
||||
dma_dev_name = "dw_dmac";
|
||||
break;
|
||||
default:
|
||||
dev_err(sst->dev, "error: invalid DMA engine %d\n",
|
||||
|
||||
Reference in New Issue
Block a user