Merge branch 'fixes' of git://git.linaro.org/people/rmk/linux-arm
Pull ARM fixes from Russell King. * 'fixes' of git://git.linaro.org/people/rmk/linux-arm: ARM: 7406/1: hotplug: copy the affinity mask when forcefully migrating IRQs ARM: 7405/1: kexec: call platform_cpu_kill on the killer rather than the victim ARM: 7403/1: tls: remove covert channel via TPIDRURW ARM: 7401/1: mm: Fix section mismatches ARM: OMAP: fix DMA vs memory ordering ARM: 7390/1: dts: versatile-pb/ab fix MMC IRQs ARM: 7400/1: vfp: clear fpscr length and stride bits on entry to sig handler ARM: 7399/1: vfp: move user vfp state save/restore code out of signal.c ARM: 7398/1: l2x0: only write to debug registers on PL310 ARM: 7397/1: l2x0: only apply workaround for erratum #753970 on PL310 ARM: 7396/1: errata: only handle ARM erratum #326103 on affected cores
This commit is contained in:
commit
655861e328
|
@ -1186,6 +1186,15 @@ if !MMU
|
||||||
source "arch/arm/Kconfig-nommu"
|
source "arch/arm/Kconfig-nommu"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
config ARM_ERRATA_326103
|
||||||
|
bool "ARM errata: FSR write bit incorrect on a SWP to read-only memory"
|
||||||
|
depends on CPU_V6
|
||||||
|
help
|
||||||
|
Executing a SWP instruction to read-only memory does not set bit 11
|
||||||
|
of the FSR on the ARM 1136 prior to r1p0. This causes the kernel to
|
||||||
|
treat the access as a read, preventing a COW from occurring and
|
||||||
|
causing the faulting task to livelock.
|
||||||
|
|
||||||
config ARM_ERRATA_411920
|
config ARM_ERRATA_411920
|
||||||
bool "ARM errata: Invalidation of the Instruction Cache operation can fail"
|
bool "ARM errata: Invalidation of the Instruction Cache operation can fail"
|
||||||
depends on CPU_V6 || CPU_V6K
|
depends on CPU_V6 || CPU_V6K
|
||||||
|
|
|
@ -173,7 +173,7 @@
|
||||||
mmc@5000 {
|
mmc@5000 {
|
||||||
compatible = "arm,primecell";
|
compatible = "arm,primecell";
|
||||||
reg = < 0x5000 0x1000>;
|
reg = < 0x5000 0x1000>;
|
||||||
interrupts = <22>;
|
interrupts = <22 34>;
|
||||||
};
|
};
|
||||||
kmi@6000 {
|
kmi@6000 {
|
||||||
compatible = "arm,pl050", "arm,primecell";
|
compatible = "arm,pl050", "arm,primecell";
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
mmc@b000 {
|
mmc@b000 {
|
||||||
compatible = "arm,primecell";
|
compatible = "arm,primecell";
|
||||||
reg = <0xb000 0x1000>;
|
reg = <0xb000 0x1000>;
|
||||||
interrupts = <23>;
|
interrupts = <23 34>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,6 +118,13 @@ extern void iwmmxt_task_switch(struct thread_info *);
|
||||||
extern void vfp_sync_hwstate(struct thread_info *);
|
extern void vfp_sync_hwstate(struct thread_info *);
|
||||||
extern void vfp_flush_hwstate(struct thread_info *);
|
extern void vfp_flush_hwstate(struct thread_info *);
|
||||||
|
|
||||||
|
struct user_vfp;
|
||||||
|
struct user_vfp_exc;
|
||||||
|
|
||||||
|
extern int vfp_preserve_user_clear_hwstate(struct user_vfp __user *,
|
||||||
|
struct user_vfp_exc __user *);
|
||||||
|
extern int vfp_restore_user_hwstate(struct user_vfp __user *,
|
||||||
|
struct user_vfp_exc __user *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
.macro set_tls_v6k, tp, tmp1, tmp2
|
.macro set_tls_v6k, tp, tmp1, tmp2
|
||||||
mcr p15, 0, \tp, c13, c0, 3 @ set TLS register
|
mcr p15, 0, \tp, c13, c0, 3 @ set TLS register
|
||||||
|
mov \tmp1, #0
|
||||||
|
mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro set_tls_v6, tp, tmp1, tmp2
|
.macro set_tls_v6, tp, tmp1, tmp2
|
||||||
|
@ -15,6 +17,8 @@
|
||||||
mov \tmp2, #0xffff0fff
|
mov \tmp2, #0xffff0fff
|
||||||
tst \tmp1, #HWCAP_TLS @ hardware TLS available?
|
tst \tmp1, #HWCAP_TLS @ hardware TLS available?
|
||||||
mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register
|
mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register
|
||||||
|
movne \tmp1, #0
|
||||||
|
mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
|
||||||
streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
|
streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
|
|
@ -155,10 +155,10 @@ static bool migrate_one_irq(struct irq_desc *desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
c = irq_data_get_irq_chip(d);
|
c = irq_data_get_irq_chip(d);
|
||||||
if (c->irq_set_affinity)
|
if (!c->irq_set_affinity)
|
||||||
c->irq_set_affinity(d, affinity, true);
|
|
||||||
else
|
|
||||||
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
|
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
|
||||||
|
else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret)
|
||||||
|
cpumask_copy(d->affinity, affinity);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,44 +180,23 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
|
||||||
|
|
||||||
static int preserve_vfp_context(struct vfp_sigframe __user *frame)
|
static int preserve_vfp_context(struct vfp_sigframe __user *frame)
|
||||||
{
|
{
|
||||||
struct thread_info *thread = current_thread_info();
|
|
||||||
struct vfp_hard_struct *h = &thread->vfpstate.hard;
|
|
||||||
const unsigned long magic = VFP_MAGIC;
|
const unsigned long magic = VFP_MAGIC;
|
||||||
const unsigned long size = VFP_STORAGE_SIZE;
|
const unsigned long size = VFP_STORAGE_SIZE;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
vfp_sync_hwstate(thread);
|
|
||||||
__put_user_error(magic, &frame->magic, err);
|
__put_user_error(magic, &frame->magic, err);
|
||||||
__put_user_error(size, &frame->size, err);
|
__put_user_error(size, &frame->size, err);
|
||||||
|
|
||||||
/*
|
if (err)
|
||||||
* Copy the floating point registers. There can be unused
|
return -EFAULT;
|
||||||
* registers see asm/hwcap.h for details.
|
|
||||||
*/
|
|
||||||
err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs,
|
|
||||||
sizeof(h->fpregs));
|
|
||||||
/*
|
|
||||||
* Copy the status and control register.
|
|
||||||
*/
|
|
||||||
__put_user_error(h->fpscr, &frame->ufp.fpscr, err);
|
|
||||||
|
|
||||||
/*
|
return vfp_preserve_user_clear_hwstate(&frame->ufp, &frame->ufp_exc);
|
||||||
* Copy the exception registers.
|
|
||||||
*/
|
|
||||||
__put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err);
|
|
||||||
__put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
|
|
||||||
__put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
|
|
||||||
|
|
||||||
return err ? -EFAULT : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int restore_vfp_context(struct vfp_sigframe __user *frame)
|
static int restore_vfp_context(struct vfp_sigframe __user *frame)
|
||||||
{
|
{
|
||||||
struct thread_info *thread = current_thread_info();
|
|
||||||
struct vfp_hard_struct *h = &thread->vfpstate.hard;
|
|
||||||
unsigned long magic;
|
unsigned long magic;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
unsigned long fpexc;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
__get_user_error(magic, &frame->magic, err);
|
__get_user_error(magic, &frame->magic, err);
|
||||||
|
@ -228,33 +207,7 @@ static int restore_vfp_context(struct vfp_sigframe __user *frame)
|
||||||
if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
|
if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
vfp_flush_hwstate(thread);
|
return vfp_restore_user_hwstate(&frame->ufp, &frame->ufp_exc);
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy the floating point registers. There can be unused
|
|
||||||
* registers see asm/hwcap.h for details.
|
|
||||||
*/
|
|
||||||
err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs,
|
|
||||||
sizeof(h->fpregs));
|
|
||||||
/*
|
|
||||||
* Copy the status and control register.
|
|
||||||
*/
|
|
||||||
__get_user_error(h->fpscr, &frame->ufp.fpscr, err);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sanitise and restore the exception registers.
|
|
||||||
*/
|
|
||||||
__get_user_error(fpexc, &frame->ufp_exc.fpexc, err);
|
|
||||||
/* Ensure the VFP is enabled. */
|
|
||||||
fpexc |= FPEXC_EN;
|
|
||||||
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
|
|
||||||
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
|
|
||||||
h->fpexc = fpexc;
|
|
||||||
|
|
||||||
__get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
|
|
||||||
__get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
|
|
||||||
|
|
||||||
return err ? -EFAULT : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -510,10 +510,6 @@ static void ipi_cpu_stop(unsigned int cpu)
|
||||||
local_fiq_disable();
|
local_fiq_disable();
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
|
|
||||||
#ifdef CONFIG_HOTPLUG_CPU
|
|
||||||
platform_cpu_kill(cpu);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
|
@ -576,17 +572,25 @@ void smp_send_reschedule(int cpu)
|
||||||
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
|
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_HOTPLUG_CPU
|
||||||
|
static void smp_kill_cpus(cpumask_t *mask)
|
||||||
|
{
|
||||||
|
unsigned int cpu;
|
||||||
|
for_each_cpu(cpu, mask)
|
||||||
|
platform_cpu_kill(cpu);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void smp_kill_cpus(cpumask_t *mask) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
void smp_send_stop(void)
|
void smp_send_stop(void)
|
||||||
{
|
{
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
if (num_online_cpus() > 1) {
|
|
||||||
struct cpumask mask;
|
struct cpumask mask;
|
||||||
|
|
||||||
cpumask_copy(&mask, cpu_online_mask);
|
cpumask_copy(&mask, cpu_online_mask);
|
||||||
cpumask_clear_cpu(smp_processor_id(), &mask);
|
cpumask_clear_cpu(smp_processor_id(), &mask);
|
||||||
|
|
||||||
smp_cross_call(&mask, IPI_CPU_STOP);
|
smp_cross_call(&mask, IPI_CPU_STOP);
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait up to one second for other CPUs to stop */
|
/* Wait up to one second for other CPUs to stop */
|
||||||
timeout = USEC_PER_SEC;
|
timeout = USEC_PER_SEC;
|
||||||
|
@ -595,6 +599,8 @@ void smp_send_stop(void)
|
||||||
|
|
||||||
if (num_online_cpus() > 1)
|
if (num_online_cpus() > 1)
|
||||||
pr_warning("SMP: failed to stop secondary CPUs\n");
|
pr_warning("SMP: failed to stop secondary CPUs\n");
|
||||||
|
|
||||||
|
smp_kill_cpus(&mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -26,18 +26,23 @@ ENTRY(v6_early_abort)
|
||||||
mrc p15, 0, r1, c5, c0, 0 @ get FSR
|
mrc p15, 0, r1, c5, c0, 0 @ get FSR
|
||||||
mrc p15, 0, r0, c6, c0, 0 @ get FAR
|
mrc p15, 0, r0, c6, c0, 0 @ get FAR
|
||||||
/*
|
/*
|
||||||
* Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR (erratum 326103).
|
* Faulty SWP instruction on 1136 doesn't set bit 11 in DFSR.
|
||||||
* The test below covers all the write situations, including Java bytecodes
|
|
||||||
*/
|
*/
|
||||||
bic r1, r1, #1 << 11 @ clear bit 11 of FSR
|
#ifdef CONFIG_ARM_ERRATA_326103
|
||||||
tst r5, #PSR_J_BIT @ Java?
|
ldr ip, =0x4107b36
|
||||||
|
mrc p15, 0, r3, c0, c0, 0 @ get processor id
|
||||||
|
teq ip, r3, lsr #4 @ r0 ARM1136?
|
||||||
bne do_DataAbort
|
bne do_DataAbort
|
||||||
do_thumb_abort fsr=r1, pc=r4, psr=r5, tmp=r3
|
tst r5, #PSR_J_BIT @ Java?
|
||||||
ldreq r3, [r4] @ read aborted ARM instruction
|
tsteq r5, #PSR_T_BIT @ Thumb?
|
||||||
|
bne do_DataAbort
|
||||||
|
bic r1, r1, #1 << 11 @ clear bit 11 of FSR
|
||||||
|
ldr r3, [r4] @ read aborted ARM instruction
|
||||||
#ifdef CONFIG_CPU_ENDIAN_BE8
|
#ifdef CONFIG_CPU_ENDIAN_BE8
|
||||||
reveq r3, r3
|
rev r3, r3
|
||||||
#endif
|
#endif
|
||||||
do_ldrd_abort tmp=ip, insn=r3
|
do_ldrd_abort tmp=ip, insn=r3
|
||||||
tst r3, #1 << 20 @ L = 0 -> write
|
tst r3, #1 << 20 @ L = 0 -> write
|
||||||
orreq r1, r1, #1 << 11 @ yes.
|
orreq r1, r1, #1 << 11 @ yes.
|
||||||
|
#endif
|
||||||
b do_DataAbort
|
b do_DataAbort
|
||||||
|
|
|
@ -32,6 +32,7 @@ static void __iomem *l2x0_base;
|
||||||
static DEFINE_RAW_SPINLOCK(l2x0_lock);
|
static DEFINE_RAW_SPINLOCK(l2x0_lock);
|
||||||
static u32 l2x0_way_mask; /* Bitmask of active ways */
|
static u32 l2x0_way_mask; /* Bitmask of active ways */
|
||||||
static u32 l2x0_size;
|
static u32 l2x0_size;
|
||||||
|
static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
|
||||||
|
|
||||||
struct l2x0_regs l2x0_saved_regs;
|
struct l2x0_regs l2x0_saved_regs;
|
||||||
|
|
||||||
|
@ -61,12 +62,7 @@ static inline void cache_sync(void)
|
||||||
{
|
{
|
||||||
void __iomem *base = l2x0_base;
|
void __iomem *base = l2x0_base;
|
||||||
|
|
||||||
#ifdef CONFIG_PL310_ERRATA_753970
|
writel_relaxed(0, base + sync_reg_offset);
|
||||||
/* write to an unmmapped register */
|
|
||||||
writel_relaxed(0, base + L2X0_DUMMY_REG);
|
|
||||||
#else
|
|
||||||
writel_relaxed(0, base + L2X0_CACHE_SYNC);
|
|
||||||
#endif
|
|
||||||
cache_wait(base + L2X0_CACHE_SYNC, 1);
|
cache_wait(base + L2X0_CACHE_SYNC, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +81,13 @@ static inline void l2x0_inv_line(unsigned long addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
|
#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
|
||||||
|
static inline void debug_writel(unsigned long val)
|
||||||
|
{
|
||||||
|
if (outer_cache.set_debug)
|
||||||
|
outer_cache.set_debug(val);
|
||||||
|
}
|
||||||
|
|
||||||
#define debug_writel(val) outer_cache.set_debug(val)
|
static void pl310_set_debug(unsigned long val)
|
||||||
|
|
||||||
static void l2x0_set_debug(unsigned long val)
|
|
||||||
{
|
{
|
||||||
writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
|
writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +97,7 @@ static inline void debug_writel(unsigned long val)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#define l2x0_set_debug NULL
|
#define pl310_set_debug NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PL310_ERRATA_588369
|
#ifdef CONFIG_PL310_ERRATA_588369
|
||||||
|
@ -331,6 +330,11 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
|
||||||
else
|
else
|
||||||
ways = 8;
|
ways = 8;
|
||||||
type = "L310";
|
type = "L310";
|
||||||
|
#ifdef CONFIG_PL310_ERRATA_753970
|
||||||
|
/* Unmapped register. */
|
||||||
|
sync_reg_offset = L2X0_DUMMY_REG;
|
||||||
|
#endif
|
||||||
|
outer_cache.set_debug = pl310_set_debug;
|
||||||
break;
|
break;
|
||||||
case L2X0_CACHE_ID_PART_L210:
|
case L2X0_CACHE_ID_PART_L210:
|
||||||
ways = (aux >> 13) & 0xf;
|
ways = (aux >> 13) & 0xf;
|
||||||
|
@ -379,7 +383,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
|
||||||
outer_cache.flush_all = l2x0_flush_all;
|
outer_cache.flush_all = l2x0_flush_all;
|
||||||
outer_cache.inv_all = l2x0_inv_all;
|
outer_cache.inv_all = l2x0_inv_all;
|
||||||
outer_cache.disable = l2x0_disable;
|
outer_cache.disable = l2x0_disable;
|
||||||
outer_cache.set_debug = l2x0_set_debug;
|
|
||||||
|
|
||||||
printk(KERN_INFO "%s cache controller enabled\n", type);
|
printk(KERN_INFO "%s cache controller enabled\n", type);
|
||||||
printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
|
printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
|
||||||
|
|
|
@ -293,11 +293,11 @@ EXPORT_SYMBOL(pfn_valid);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_SPARSEMEM
|
#ifndef CONFIG_SPARSEMEM
|
||||||
static void arm_memory_present(void)
|
static void __init arm_memory_present(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static void arm_memory_present(void)
|
static void __init arm_memory_present(void)
|
||||||
{
|
{
|
||||||
struct memblock_region *reg;
|
struct memblock_region *reg;
|
||||||
|
|
||||||
|
|
|
@ -618,8 +618,8 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
|
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
|
||||||
unsigned long phys, const struct mem_type *type)
|
unsigned long end, unsigned long phys, const struct mem_type *type)
|
||||||
{
|
{
|
||||||
pud_t *pud = pud_offset(pgd, addr);
|
pud_t *pud = pud_offset(pgd, addr);
|
||||||
unsigned long next;
|
unsigned long next;
|
||||||
|
|
|
@ -916,6 +916,13 @@ void omap_start_dma(int lch)
|
||||||
l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
|
l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
|
||||||
l |= OMAP_DMA_CCR_EN;
|
l |= OMAP_DMA_CCR_EN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As dma_write() uses IO accessors which are weakly ordered, there
|
||||||
|
* is no guarantee that data in coherent DMA memory will be visible
|
||||||
|
* to the DMA device. Add a memory barrier here to ensure that any
|
||||||
|
* such data is visible prior to enabling DMA.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
p->dma_write(l, CCR, lch);
|
p->dma_write(l, CCR, lch);
|
||||||
|
|
||||||
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
|
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
|
||||||
|
@ -965,6 +972,13 @@ void omap_stop_dma(int lch)
|
||||||
p->dma_write(l, CCR, lch);
|
p->dma_write(l, CCR, lch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that data transferred by DMA is visible to any access
|
||||||
|
* after DMA has been disabled. This is important for coherent
|
||||||
|
* DMA regions.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
|
|
||||||
if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
|
if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
|
||||||
int next_lch, cur_lch = lch;
|
int next_lch, cur_lch = lch;
|
||||||
char dma_chan_link_map[dma_lch_count];
|
char dma_chan_link_map[dma_lch_count];
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/user.h>
|
||||||
|
|
||||||
#include <asm/cp15.h>
|
#include <asm/cp15.h>
|
||||||
#include <asm/cputype.h>
|
#include <asm/cputype.h>
|
||||||
|
@ -528,6 +530,103 @@ void vfp_flush_hwstate(struct thread_info *thread)
|
||||||
put_cpu();
|
put_cpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save the current VFP state into the provided structures and prepare
|
||||||
|
* for entry into a new function (signal handler).
|
||||||
|
*/
|
||||||
|
int vfp_preserve_user_clear_hwstate(struct user_vfp __user *ufp,
|
||||||
|
struct user_vfp_exc __user *ufp_exc)
|
||||||
|
{
|
||||||
|
struct thread_info *thread = current_thread_info();
|
||||||
|
struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Ensure that the saved hwstate is up-to-date. */
|
||||||
|
vfp_sync_hwstate(thread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the floating point registers. There can be unused
|
||||||
|
* registers see asm/hwcap.h for details.
|
||||||
|
*/
|
||||||
|
err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs,
|
||||||
|
sizeof(hwstate->fpregs));
|
||||||
|
/*
|
||||||
|
* Copy the status and control register.
|
||||||
|
*/
|
||||||
|
__put_user_error(hwstate->fpscr, &ufp->fpscr, err);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the exception registers.
|
||||||
|
*/
|
||||||
|
__put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err);
|
||||||
|
__put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err);
|
||||||
|
__put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/* Ensure that VFP is disabled. */
|
||||||
|
vfp_flush_hwstate(thread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As per the PCS, clear the length and stride bits for function
|
||||||
|
* entry.
|
||||||
|
*/
|
||||||
|
hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable VFP in the hwstate so that we can detect if it gets
|
||||||
|
* used.
|
||||||
|
*/
|
||||||
|
hwstate->fpexc &= ~FPEXC_EN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sanitise and restore the current VFP state from the provided structures. */
|
||||||
|
int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
|
||||||
|
struct user_vfp_exc __user *ufp_exc)
|
||||||
|
{
|
||||||
|
struct thread_info *thread = current_thread_info();
|
||||||
|
struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
|
||||||
|
unsigned long fpexc;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If VFP has been used, then disable it to avoid corrupting
|
||||||
|
* the new thread state.
|
||||||
|
*/
|
||||||
|
if (hwstate->fpexc & FPEXC_EN)
|
||||||
|
vfp_flush_hwstate(thread);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the floating point registers. There can be unused
|
||||||
|
* registers see asm/hwcap.h for details.
|
||||||
|
*/
|
||||||
|
err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs,
|
||||||
|
sizeof(hwstate->fpregs));
|
||||||
|
/*
|
||||||
|
* Copy the status and control register.
|
||||||
|
*/
|
||||||
|
__get_user_error(hwstate->fpscr, &ufp->fpscr, err);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sanitise and restore the exception registers.
|
||||||
|
*/
|
||||||
|
__get_user_error(fpexc, &ufp_exc->fpexc, err);
|
||||||
|
|
||||||
|
/* Ensure the VFP is enabled. */
|
||||||
|
fpexc |= FPEXC_EN;
|
||||||
|
|
||||||
|
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
|
||||||
|
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
|
||||||
|
hwstate->fpexc = fpexc;
|
||||||
|
|
||||||
|
__get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err);
|
||||||
|
__get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err);
|
||||||
|
|
||||||
|
return err ? -EFAULT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VFP hardware can lose all context when a CPU goes offline.
|
* VFP hardware can lose all context when a CPU goes offline.
|
||||||
* As we will be running in SMP mode with CPU hotplug, we will save the
|
* As we will be running in SMP mode with CPU hotplug, we will save the
|
||||||
|
|
Loading…
Reference in New Issue