Thread (21 messages) 21 messages, 4 authors, 2022-09-20

Re: [PATCH 1/6] powerpc/code-patching: Implement generic text patching function

From: Christophe Leroy <hidden>
Date: 2022-09-20 05:45:16


Le 20/09/2022 à 04:32, Benjamin Gray a écrit :
On Mon, 2022-09-19 at 07:16 +0000, Christophe Leroy wrote:
quoted
Why would it be unpredictable ? Only one page is mapped. If it
crosses
the boundary, __put_kernel_nofault() will fail in a controled manner.
I see no point in doing the check before every write.
Oh I didn't see that get_vm_area automatically adds a guard. You're
right then, it's redundant. I had assumed there could be a writeable
mapping directly after.
quoted
And while you are thinking about alignment, don't forget that dcbst
and
icbi apply on a give cacheline. If your memory crosses a cacheline
you
may have a problem.
Yeah, though this applies to the existing patch_instruction too (in
theory; prefixed instructions cannot cross a 64 byte boundary, but the
ISA does not specify minimum cacheline size). I don't have a nice
solution right now though. The flush needs to be done on the effective
address (i.e. text poke address) according to the ISA, but the text
poke address is only valid within the IRQ save region. So non-prefixed
instruction patching would either pay for some kind of check, or need
special casing.
As far as I know, cachelines are minimum 64 bytes on PPC64 aren't they ?
Maybe an "is aligned" flag in a generic __patch_text to make the extra
flush conditional is good enough?
Well, if the cacheline is already flushed, the operation will be a nop 
anyway so it shouldn't cost much to flush both addresses, after all it 
is the sync that costs and you'll still have only one.

Related to EA based flushing, data patching ought to run 'dcbst' on the
'exec_addr' too. So given the icache flush only needs to be applied to
instruction patching, and data flush only to data patching, I plan to
move those exec_addr syncs outside of __patch_text to the relevant
public instruction/data specific entry points.
Why should it run 'dcbst' on the 'exec_addr' at all ? We have not 
written anything there.

Anyway, powerpc handles cachelines by physical address, so no matter 
which EA you use as far as it is done.

And dcbst is handled as a write by the MMU, so you just can't apply it 
on the read-only exec address.

quoted
quoted
quoted
quoted
+               case 8:
+                       __put_kernel_nofault(dest, src, u64,
failed);
+                       break;
Is case 8 needed for PPC32 ?
I don't have a particular need for it, but the underlying
__put_kernel_nofault is capable of it so I included it.
Well, not including it will allow you to pass the source as a 'long'
as
mentionned above.
I checked the machine code of a 32 bit build, but it still passes the
pointer in a register. I also checked all 3 ABI docs and they say a
pointer is the same size as a long. Could you clarify when a pointer is
passed on the stack but not a long?

Or do you mean that we could pass the pointed-to data in a register and
skip the pointer altogether? That seems like a good choice, but
__put_kernel_nofault takes a pointer to the source and the
implementation is very complex. I don't think I can safely write the
modified version we'd need at this point.
Yes I meant to pass the value instead of passing a pointer to the value.
When you pass a pointer to a value, it forces gcc to put that value in 
memory, namely in the stack. While when you pass the value directly , 
then it gets passed in a register.

So I think you have to pass the value and only change it to a pointer to 
that value at the time you are calling __put_kernel_nofault(). That way 
gcc is able to handle it efficiently and most of the time voids going 
via memory.

Today raw_patch_instruction() is :

c0017ebc <raw_patch_instruction>:
c0017ebc:	90 83 00 00 	stw     r4,0(r3)
c0017ec0:	7c 00 18 6c 	dcbst   0,r3
c0017ec4:	7c 00 04 ac 	hwsync
c0017ec8:	7c 00 1f ac 	icbi    0,r3
c0017ecc:	7c 00 04 ac 	hwsync
c0017ed0:	4c 00 01 2c 	isync
c0017ed4:	38 60 00 00 	li      r3,0
c0017ed8:	4e 80 00 20 	blr
c0017edc:	38 60 ff ff 	li      r3,-1
c0017ee0:	4e 80 00 20 	blr

Here r4 is the value to be written.


With your patch, extract from __patch_text() is:

c0017fdc:	28 05 00 04 	cmplwi  r5,4
c0017fe0:	41 82 00 74 	beq     c0018054 <__patch_text+0x98>
c0017fe4:	41 81 00 40 	bgt     c0018024 <__patch_text+0x68>
c0017fe8:	28 05 00 01 	cmplwi  r5,1
c0017fec:	41 82 00 74 	beq     c0018060 <__patch_text+0xa4>
c0017ff0:	28 05 00 02 	cmplwi  r5,2
c0017ff4:	40 82 00 0c 	bne     c0018000 <__patch_text+0x44>
c0017ff8:	a1 24 00 00 	lhz     r9,0(r4)
c0017ffc:	b1 23 00 00 	sth     r9,0(r3)
c0018000:	7c 00 18 6c 	dcbst   0,r3
c0018004:	7c 00 04 ac 	hwsync
c0018008:	2c 06 00 00 	cmpwi   r6,0
c001800c:	38 60 00 00 	li      r3,0
c0018010:	4d 82 00 20 	beqlr
c0018014:	7c 00 3f ac 	icbi    0,r7
c0018018:	7c 00 04 ac 	hwsync
c001801c:	4c 00 01 2c 	isync
c0018020:	4e 80 00 20 	blr
c0018024:	28 05 00 08 	cmplwi  r5,8
c0018028:	40 a2 ff d8 	bne     c0018000 <__patch_text+0x44>
c001802c:	81 44 00 00 	lwz     r10,0(r4)
c0018030:	81 64 00 04 	lwz     r11,4(r4)
c0018034:	91 43 00 00 	stw     r10,0(r3)
c0018038:	91 63 00 04 	stw     r11,4(r3)
c001803c:	7c 00 18 6c 	dcbst   0,r3
c0018040:	7c 00 04 ac 	hwsync
c0018044:	2c 06 00 00 	cmpwi   r6,0
c0018048:	38 60 00 00 	li      r3,0
c001804c:	4d 82 00 20 	beqlr
c0018050:	4b ff ff c4 	b       c0018014 <__patch_text+0x58>
c0018054:	81 24 00 00 	lwz     r9,0(r4)
c0018058:	91 23 00 00 	stw     r9,0(r3)
c001805c:	4b ff ff a4 	b       c0018000 <__patch_text+0x44>
c0018060:	89 24 00 00 	lbz     r9,0(r4)
c0018064:	99 23 00 00 	stb     r9,0(r3)
c0018068:	4b ff ff 98 	b       c0018000 <__patch_text+0x44>
c001806c:	38 60 ff ff 	li      r3,-1
c0018070:	4e 80 00 20 	blr
c0018074:	38 60 ff f2 	li      r3,-14
c0018078:	4e 80 00 20 	blr

So as you can see, r4 now is a memory pointer and the data has to be 
loaded from there.


Christophe
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help