Re: [PATCH] powerpc/uaccess: Use flexible addressing with __put_user()/__get_user()

From: Segher Boessenkool
Date: Thu Apr 16 2020 - 21:39:35 EST


Hi!

On Thu, Apr 16, 2020 at 07:50:00AM +0200, Christophe Leroy wrote:
> Le 16/04/2020 Ã 00:06, Segher Boessenkool a ÃcritÂ:
> >On Wed, Apr 15, 2020 at 09:20:26AM +0000, Christophe Leroy wrote:
> >>At the time being, __put_user()/__get_user() and friends only use
> >>register indirect with immediate index addressing, with the index
> >>set to 0. Ex:
> >>
> >> lwz reg1, 0(reg2)
> >
> >This is called a "D-form" instruction, or sometimes "offset addressing".
> >Don't talk about an "index", it confuses things, because the *other*
> >kind is called "indexed" already, also in the ISA docs! (X-form, aka
> >indexed addressing, [reg+reg], where D-form does [reg+imm], and both
> >forms can do [reg]).
>
> In the "Programming Environments Manual for 32-Bit Implementations of
> the PowerPCâ Architecture", they list the following addressing modes:
>
> Load and store operations have three categories of effective address
> generation that depend on the
> operands specified:
> â Register indirect with immediate index mode
> â Register indirect with index mode
> â Register indirect mode

Huh. I must have pushed all that confusing terminology to the back of
my head :-)

> >%Un on an "m" operand doesn't do much: you need to make it "m<>" if you
> >want pre-modify ("update") insns to be generated. (You then will want
> >to make sure that operand is used in a way GCC can understand; since it
> >is used only once here, that works fine).
>
> Ah ? Indeed I got the idea from include/asm/io.h where there is:
>
> #define DEF_MMIO_IN_D(name, size, insn) \
> static inline u##size name(const volatile u##size __iomem *addr) \
> { \
> u##size ret; \
> __asm__ __volatile__("sync;"#insn"%U1%X1 %0,%1;twi 0,%0,0;isync"\
> : "=r" (ret) : "m" (*addr) : "memory"); \
> return ret; \
> }
>
> It should be "m<>" there as well ?

Yes, that will work here.

Long ago, "m" in inline assembler code did the same as "m<>" now does
(and "m" internal in GCC still does). To get a memory without pre-modify
addressing, you used "es".

Since people kept getting that wrong (it *is* surprising), it was changed
to the current scheme. But the kernel uses weren't updated (and no one
seems to have missed it).

> >> #else /* __powerpc64__ */
> >> #define __put_user_asm2(x, addr, err) \
> >> __asm__ __volatile__( \
> >>- "1: stw %1,0(%2)\n" \
> >>- "2: stw %1+1,4(%2)\n" \
> >>+ "1: stw%U2%X2 %1,%2\n" \
> >>+ "2: stw%U2%X2 %L1,%L2\n" \
> >> "3:\n" \
> >> ".section .fixup,\"ax\"\n" \
> >> "4: li %0,%3\n" \
> >>@@ -140,7 +140,7 @@ extern long __put_user_bad(void);
> >> EX_TABLE(1b, 4b) \
> >> EX_TABLE(2b, 4b) \
> >> : "=r" (err) \
> >>- : "r" (x), "b" (addr), "i" (-EFAULT), "0" (err))
> >>+ : "r" (x), "m" (*addr), "i" (-EFAULT), "0" (err))
> >
> >Here, it doesn't work. You don't want two consecutive update insns in
> >any case. Easiest is to just not use "m<>", and then, don't use %Un
> >(which won't do anything, but it is confusing).
>
> Can't we leave the Un on the second stw ?

That cannot work. You can use it on only the *first* though :-)

And this doesn't work on LE I think? (But the asm doesn't anyway?)

Or, you can decide this is all way too tricky, and not worth it.


Segher