mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 09:05:12 +07:00
527a1d1ede
According to the found documentation, data cache flushes and sync
instructions are needed on the PCX-U+ (PA8200, e.g. C200/C240)
platforms, while PCX-W (PA8500, e.g. C360) platforms aparently don't
need those flushes when changing the IO PDIR data structures.
We have no documentation for PCX-W+ (PA8600) and PCX-W2 (PA8700) CPUs,
but Carlo Pisani reported that his C3600 machine (PA8600, PCX-W+) fails
when the fdc instructions were removed. His firmware didn't set the NIOP
bit, so one may assume it's a firmware bug since other C3750 machines
had the bit set.
Even if documentation (as mentioned above) states that PCX-W (PA8500,
e.g. J5000) does not need fdc flushes, Sven could show that an Adaptec
29320A PCI-X SCSI controller reliably failed on a dd command during the
first five minutes in his J5000 when fdc flushes were missing.
Going forward, we will now NOT replace the fdc and sync assembler
instructions by NOPS if:
a) the NP iopdir_fdc bit was set by firmware, or
b) we find a CPU up to and including a PCX-W+ (PA8600).
This fixes the HPMC crashes on a C240 and C36XX machines. For other
machines we rely on the firmware to set the bit when needed.
In case one finds HPMC issues, people could try to boot their machines
with the "no-alternatives" kernel option to turn off any alternative
patching.
Reported-by: Sven Schnelle <svens@stackframe.org>
Reported-by: Carlo Pisani <carlojpisani@gmail.com>
Tested-by: Sven Schnelle <svens@stackframe.org>
Fixes: 3847dab774
("parisc: Add alternative coding infrastructure")
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: stable@vger.kernel.org # 5.0+
100 lines
2.5 KiB
C
100 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Alternative live-patching for parisc.
|
|
* Copyright (C) 2018 Helge Deller <deller@gmx.de>
|
|
*
|
|
*/
|
|
|
|
#include <asm/processor.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/alternative.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
static int no_alternatives;
|
|
static int __init setup_no_alternatives(char *str)
|
|
{
|
|
no_alternatives = 1;
|
|
return 1;
|
|
}
|
|
__setup("no-alternatives", setup_no_alternatives);
|
|
|
|
void __init_or_module apply_alternatives(struct alt_instr *start,
|
|
struct alt_instr *end, const char *module_name)
|
|
{
|
|
struct alt_instr *entry;
|
|
int index = 0, applied = 0;
|
|
int num_cpus = num_online_cpus();
|
|
|
|
for (entry = start; entry < end; entry++, index++) {
|
|
|
|
u32 *from, len, cond, replacement;
|
|
|
|
from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
|
|
len = entry->len;
|
|
cond = entry->cond;
|
|
replacement = entry->replacement;
|
|
|
|
WARN_ON(!cond);
|
|
|
|
if (cond != ALT_COND_ALWAYS && no_alternatives)
|
|
continue;
|
|
|
|
pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
|
|
index, cond, len, from, replacement);
|
|
|
|
if ((cond & ALT_COND_NO_SMP) && (num_cpus != 1))
|
|
continue;
|
|
if ((cond & ALT_COND_NO_DCACHE) && (cache_info.dc_size != 0))
|
|
continue;
|
|
if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
|
|
continue;
|
|
|
|
/*
|
|
* If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
|
|
* set (bit #61, big endian), we have to flush and sync every
|
|
* time IO-PDIR is changed in Ike/Astro.
|
|
*/
|
|
if ((cond & ALT_COND_NO_IOC_FDC) &&
|
|
((boot_cpu_data.cpu_type <= pcxw_) ||
|
|
(boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC)))
|
|
continue;
|
|
|
|
/* Want to replace pdtlb by a pdtlb,l instruction? */
|
|
if (replacement == INSN_PxTLB) {
|
|
replacement = *from;
|
|
if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */
|
|
replacement |= (1 << 10); /* set el bit */
|
|
}
|
|
|
|
/*
|
|
* Replace instruction with NOPs?
|
|
* For long distance insert a branch instruction instead.
|
|
*/
|
|
if (replacement == INSN_NOP && len > 1)
|
|
replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
|
|
|
|
pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
|
|
index, cond, len, from, replacement);
|
|
|
|
/* Replace instruction */
|
|
*from = replacement;
|
|
applied++;
|
|
}
|
|
|
|
pr_info("%s%salternatives: applied %d out of %d patches\n",
|
|
module_name ? : "", module_name ? " " : "",
|
|
applied, index);
|
|
}
|
|
|
|
|
|
void __init apply_alternatives_all(void)
|
|
{
|
|
set_kernel_text_rw(1);
|
|
|
|
apply_alternatives((struct alt_instr *) &__alt_instructions,
|
|
(struct alt_instr *) &__alt_instructions_end, NULL);
|
|
|
|
set_kernel_text_rw(0);
|
|
}
|