mips: Fix sub-word atomics implementation
These aligned the address but then always used the least significant bits of the value in memory, which is the wrong half 50% of the time for 16-bit atomics and the wrong quarter 75% of the time for 8-bit atomics. These bugs were all present in r178172, the commit that added the mips port, and have remained for its entire existence to date. Reviewed by: jhb (mentor) Approved by: jhb (mentor) Differential Revision: https://reviews.freebsd.org/D27343
This commit is contained in:
parent
c46f7610d4
commit
36a6905730
@ -90,6 +90,7 @@
|
|||||||
#include <sys/errno.h>
|
#include <sys/errno.h>
|
||||||
#include <machine/asm.h>
|
#include <machine/asm.h>
|
||||||
#include <machine/cpu.h>
|
#include <machine/cpu.h>
|
||||||
|
#include <machine/endian.h>
|
||||||
#include <machine/regnum.h>
|
#include <machine/regnum.h>
|
||||||
#include <machine/cpuregs.h>
|
#include <machine/cpuregs.h>
|
||||||
#include <machine/pcb.h>
|
#include <machine/pcb.h>
|
||||||
@ -578,9 +579,14 @@ END(ffs)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_set_16)
|
LEAF(atomic_set_16)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
/* NB: Only bit 1 is masked so the ll catches unaligned inputs */
|
||||||
sll a0, a0, 2
|
andi t0, a0, 2 # get unaligned offset
|
||||||
andi a1, a1, 0xffff
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 2
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right half
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
or t0, t0, a1
|
or t0, t0, a1
|
||||||
@ -600,17 +606,18 @@ END(atomic_set_16)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_clear_16)
|
LEAF(atomic_clear_16)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
/* NB: Only bit 1 is masked so the ll catches unaligned inputs */
|
||||||
sll a0, a0, 2
|
andi t0, a0, 2 # get unaligned offset
|
||||||
nor a1, zero, a1
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 2
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right half
|
||||||
|
not a1, a1
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
move t1, t0
|
and t0, t0, a1
|
||||||
andi t1, t1, 0xffff # t1 has the original lower 16 bits
|
|
||||||
and t1, t1, a1 # t1 has the new lower 16 bits
|
|
||||||
srl t0, t0, 16 # preserve original top 16 bits
|
|
||||||
sll t0, t0, 16
|
|
||||||
or t0, t0, t1
|
|
||||||
sc t0, 0(a0)
|
sc t0, 0(a0)
|
||||||
beq t0, zero, 1b
|
beq t0, zero, 1b
|
||||||
nop
|
nop
|
||||||
@ -628,17 +635,23 @@ END(atomic_clear_16)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_subtract_16)
|
LEAF(atomic_subtract_16)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
/* NB: Only bit 1 is masked so the ll catches unaligned inputs */
|
||||||
sll a0, a0, 2
|
andi t0, a0, 2 # get unaligned offset
|
||||||
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 2 # flip order for big-endian
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right half
|
||||||
|
li t2, 0xffff
|
||||||
|
sll t2, t2, t0 # compute mask
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
move t1, t0
|
subu t1, t0, a1
|
||||||
andi t1, t1, 0xffff # t1 has the original lower 16 bits
|
/* Exploit ((t0 & ~t2) | (t1 & t2)) = t0 ^ ((t0 ^ t1) & t2) */
|
||||||
subu t1, t1, a1
|
xor t1, t0, t1
|
||||||
andi t1, t1, 0xffff # t1 has the new lower 16 bits
|
and t1, t1, t2
|
||||||
srl t0, t0, 16 # preserve original top 16 bits
|
xor t0, t0, t1
|
||||||
sll t0, t0, 16
|
|
||||||
or t0, t0, t1
|
|
||||||
sc t0, 0(a0)
|
sc t0, 0(a0)
|
||||||
beq t0, zero, 1b
|
beq t0, zero, 1b
|
||||||
nop
|
nop
|
||||||
@ -655,17 +668,23 @@ END(atomic_subtract_16)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_add_16)
|
LEAF(atomic_add_16)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
/* NB: Only bit 1 is masked so the ll catches unaligned inputs */
|
||||||
sll a0, a0, 2
|
andi t0, a0, 2 # get unaligned offset
|
||||||
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 2 # flip order for big-endian
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right half
|
||||||
|
li t2, 0xffff
|
||||||
|
sll t2, t2, t0 # compute mask
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
move t1, t0
|
addu t1, t0, a1
|
||||||
andi t1, t1, 0xffff # t1 has the original lower 16 bits
|
/* Exploit ((t0 & ~t2) | (t1 & t2)) = t0 ^ ((t0 ^ t1) & t2) */
|
||||||
addu t1, t1, a1
|
xor t1, t0, t1
|
||||||
andi t1, t1, 0xffff # t1 has the new lower 16 bits
|
and t1, t1, t2
|
||||||
srl t0, t0, 16 # preserve original top 16 bits
|
xor t0, t0, t1
|
||||||
sll t0, t0, 16
|
|
||||||
or t0, t0, t1
|
|
||||||
sc t0, 0(a0)
|
sc t0, 0(a0)
|
||||||
beq t0, zero, 1b
|
beq t0, zero, 1b
|
||||||
nop
|
nop
|
||||||
@ -682,17 +701,22 @@ END(atomic_add_16)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_add_8)
|
LEAF(atomic_add_8)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
andi t0, a0, 3 # get unaligned offset
|
||||||
sll a0, a0, 2
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 3 # flip order for big-endian
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right quarter
|
||||||
|
li t2, 0xff
|
||||||
|
sll t2, t2, t0 # compute mask
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
move t1, t0
|
addu t1, t0, a1
|
||||||
andi t1, t1, 0xff # t1 has the original lower 8 bits
|
/* Exploit ((t0 & ~t2) | (t1 & t2)) = t0 ^ ((t0 ^ t1) & t2) */
|
||||||
addu t1, t1, a1
|
xor t1, t0, t1
|
||||||
andi t1, t1, 0xff # t1 has the new lower 8 bits
|
and t1, t1, t2
|
||||||
srl t0, t0, 8 # preserve original top 24 bits
|
xor t0, t0, t1
|
||||||
sll t0, t0, 8
|
|
||||||
or t0, t0, t1
|
|
||||||
sc t0, 0(a0)
|
sc t0, 0(a0)
|
||||||
beq t0, zero, 1b
|
beq t0, zero, 1b
|
||||||
nop
|
nop
|
||||||
@ -710,17 +734,22 @@ END(atomic_add_8)
|
|||||||
*/
|
*/
|
||||||
LEAF(atomic_subtract_8)
|
LEAF(atomic_subtract_8)
|
||||||
.set noreorder
|
.set noreorder
|
||||||
srl a0, a0, 2 # round down address to be 32-bit aligned
|
andi t0, a0, 3 # get unaligned offset
|
||||||
sll a0, a0, 2
|
xor a0, a0, t0 # align pointer
|
||||||
|
#if _BYTE_ORDER == BIG_ENDIAN
|
||||||
|
xori t0, t0, 3 # flip order for big-endian
|
||||||
|
#endif
|
||||||
|
sll t0, t0, 3 # convert byte offset to bit offset
|
||||||
|
sll a1, a1, t0 # put bits in the right quarter
|
||||||
|
li t2, 0xff
|
||||||
|
sll t2, t2, t0 # compute mask
|
||||||
1:
|
1:
|
||||||
ll t0, 0(a0)
|
ll t0, 0(a0)
|
||||||
move t1, t0
|
subu t1, t0, a1
|
||||||
andi t1, t1, 0xff # t1 has the original lower 8 bits
|
/* Exploit ((t0 & ~t2) | (t1 & t2)) = t0 ^ ((t0 ^ t1) & t2) */
|
||||||
subu t1, t1, a1
|
xor t1, t0, t1
|
||||||
andi t1, t1, 0xff # t1 has the new lower 8 bits
|
and t1, t1, t2
|
||||||
srl t0, t0, 8 # preserve original top 24 bits
|
xor t0, t0, t1
|
||||||
sll t0, t0, 8
|
|
||||||
or t0, t0, t1
|
|
||||||
sc t0, 0(a0)
|
sc t0, 0(a0)
|
||||||
beq t0, zero, 1b
|
beq t0, zero, 1b
|
||||||
nop
|
nop
|
||||||
|
Loading…
Reference in New Issue
Block a user