Enable STRICT_KERNEL_RWX for PPC64/BOOK3S
These patches enable RX mappings of kernel text.
rodata is mapped RX as well as a trade-off, there
are more details in the patch description
As a prerequisite for R/O text, patch_instruction
is moved over to using a separate mapping that
allows write to kernel text. xmon/ftrace/kprobes
have been moved over to work with patch_instruction
There is a bug fix, the updatepp and updateboltedpp
(pseries) providers, did not use flags as described in
PAPR
There are patches for 32 bit support from Christophe Leroy
at http://patchwork.ozlabs.org/patch/768257/. The patches
for map_page to map_kernel_page are a pre-requisite to
this series being applied.
Another build failure was reported, because instead
of using ARCH_HAS_SET_MEMORY as a gate for set_memory.h
inclusion, some of the infrastructure in the core kernel
uses CONFIG_STRICT_KERNEL_RWX. I've sent a fix to the
fix the latter.
Changelog v3:
Support radix
Drop ptdump patch, already picked from v2
Changelog v2:
Support optprobes via patch_instruction
Balbir Singh (9):
powerpc/lib/code-patching: Enhance code patching
powerpc/kprobes: Move kprobes over to patch_instruction
powerpc/kprobes/optprobes: Move over to patch_instruction
powerpc/xmon: Add patch_instruction supporf for xmon
powerpc/vmlinux.lds: Align __init_begin to 16M
powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp
powerpc/mm/hash: Implement mark_rodata_ro() for hash
powerpc/Kconfig: Enable STRICT_KERNEL_RWX
powerpc/mm/radix: Implement mark_rodata_ro() for radix
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/book3s/64/hash.h | 3 +
arch/powerpc/include/asm/book3s/64/radix.h | 4 +
arch/powerpc/kernel/kprobes.c | 4 +-
arch/powerpc/kernel/optprobes.c | 58 +++++++-----
arch/powerpc/kernel/vmlinux.lds.S | 10 ++-
arch/powerpc/lib/code-patching.c | 140 ++++++++++++++++++++++++++++-
arch/powerpc/mm/pgtable-hash64.c | 35 ++++++++
arch/powerpc/mm/pgtable-radix.c | 73 ++++++++++++++-
arch/powerpc/mm/pgtable_64.c | 9 ++
arch/powerpc/platforms/pseries/lpar.c | 13 ++-
arch/powerpc/xmon/xmon.c | 7 +-
12 files changed, 323 insertions(+), 34 deletions(-)
--
2.9.4
Today our patching happens via direct copy and
patch_instruction. The patching code is well
contained in the sense that copying bits are limited.
While considering implementation of CONFIG_STRICT_RWX,
the first requirement is to a create another mapping
that will allow for patching. We create the window using
text_poke_area, allocated via get_vm_area(), which might
be an overkill. text_poke_area is per CPU to avoid locking
Other arches do similar things, but use fixmaps. The reason
for not using fixmaps is to make use of any randomization in
the future. The code also relies on set_pte_at and pte_clear
to do the appropriate tlb flushing.
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/lib/code-patching.c | 140 +++++++++++++++++++++++++++++++++++++--
1 file changed, 136 insertions(+), 4 deletions(-)
@@ -12,23 +12,154 @@#include<linux/vmalloc.h>#include<linux/init.h>#include<linux/mm.h>+#include<linux/cpuhotplug.h>#include<asm/page.h>#include<asm/code-patching.h>#include<linux/uaccess.h>#include<linux/kprobes.h>+#include<asm/pgtable.h>+#include<asm/tlbflush.h>+staticDEFINE_PER_CPU(structvm_struct*,text_poke_area);+staticunsignedinttext_area_patch_avail;-intpatch_instruction(unsignedint*addr,unsignedintinstr)+staticinttext_area_cpu_up(unsignedintcpu)+{+structvm_struct*area;++area=get_vm_area(PAGE_SIZE,VM_ALLOC);+if(!area){+WARN_ONCE(1,"Failed to create text area for cpu %d\n",+cpu);+return-1;+}+this_cpu_write(text_poke_area,area);+return0;+}++staticinttext_area_cpu_down(unsignedintcpu)+{+free_vm_area(this_cpu_read(text_poke_area));+return0;+}++/*+*Thisisanearly_initcallandearly_initcallshappenattherighttime+*forus,afterslabisenabledandbeforewemarkropagesR/O.Inthe+*futureifget_vm_areaisrandomized,thiswillbemoreflexiblethan+*fixmap+*/+staticint__initsetup_text_poke_area(void){+structvm_struct*area;+intcpu;++for_each_online_cpu(cpu){+area=get_vm_area(PAGE_SIZE,VM_ALLOC);+if(!area){+WARN_ONCE(1,"Failed to create text area for cpu %d\n",+cpu);+/* Should we disable strict rwx? */+continue;+}+this_cpu_write(text_poke_area,area);+}+cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,+"powerpc/text_poke:online",text_area_cpu_up,+text_area_cpu_down);+text_area_patch_avail=1;+/*+*Thebarrierhereensuresthewriteisvisibleto+*patch_instruction()+*/+smp_wmb();+pr_info("text_poke area ready...\n");+return0;+}++/*+*Thiscanbecalledforkerneltextoramodule.+*/+staticintkernel_map_addr(void*addr)+{+unsignedlongpfn;interr;-__put_user_size(instr,addr,4,err);+if(is_vmalloc_addr(addr))+pfn=vmalloc_to_pfn(addr);+else+pfn=__pa_symbol(addr)>>PAGE_SHIFT;++err=map_kernel_page(+(unsignedlong)__this_cpu_read(text_poke_area)->addr,+(pfn<<PAGE_SHIFT),pgprot_val(PAGE_KERNEL));+pr_devel("Mapped addr %p with pfn %lx\n",+__this_cpu_read(text_poke_area)->addr,pfn);if(err)-returnerr;-asm("dcbst 0, %0; sync; icbi 0,%0; sync; isync"::"r"(addr));+return-1;return0;}+staticinlinevoidkernel_unmap_addr(void*addr)+{+pte_t*pte;+unsignedlongkaddr=(unsignedlong)addr;++pte=pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(kaddr),+kaddr),kaddr),kaddr);+pr_devel("clearing mm %p, pte %p, kaddr %lx\n",&init_mm,pte,kaddr);+pte_clear(&init_mm,kaddr,pte);+flush_tlb_kernel_range(kaddr,kaddr+PAGE_SIZE);+}++intpatch_instruction(unsignedint*addr,unsignedintinstr)+{+interr;+unsignedint*dest=NULL;+unsignedlongflags;+unsignedlongkaddr=(unsignedlong)addr;++/*+*Makesurewecanseeanywriteoftext_area_patch_avail+*/+smp_rmb();++/*+*Duringearlyearlybootpatch_instructioniscalled+*whentext_poke_areaisnotready,butwestillneed+*toallowpatching.Wejustdotheplainoldpatching+*Weusetext_area_patch_avail,sincepercpuread+*via__this_cpu_readoftext_poke_areamightnot+*yetbeavailable.+*TODO:Maketext_area_patch_availpercpu?+*/+if(!text_area_patch_avail){+__put_user_size(instr,addr,4,err);+if(err)+returnerr;+asm("dcbst 0, %0; sync; icbi 0,%0; sync; isync"::"r"(addr));+return0;+}++local_irq_save(flags);+if(kernel_map_addr(addr)){+err=-1;+gotoout;+}++dest=(unsignedint*)(__this_cpu_read(text_poke_area)->addr)++((kaddr&~PAGE_MASK)/sizeof(unsignedint));+__put_user_size(instr,dest,4,err);+if(!err)+asm("dcbst 0, %0; sync; icbi 0,%0; icbi 0,%1; sync; isync"+::"r"(dest),"r"(addr));+kernel_unmap_addr(__this_cpu_read(text_poke_area)->addr);+out:+local_irq_restore(flags);+returnerr;+}+NOKPROBE_SYMBOL(patch_instruction);+intpatch_branch(unsignedint*addr,unsignedlongtarget,intflags){returnpatch_instruction(addr,create_branch(addr,target,flags));
With text moving to read-only migrate optprobes to using
the patch_instruction infrastructure. Without this optprobes
will fail and complain.
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/kernel/optprobes.c | 58 ++++++++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 21 deletions(-)
@@ -231,8 +237,15 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)gotoerror;/* Setup template */-memcpy(buff,optprobe_template_entry,-TMPL_END_IDX*sizeof(kprobe_opcode_t));+/* We can optimize this via patch_instruction_window later */+size=(TMPL_END_IDX*sizeof(kprobe_opcode_t))/sizeof(int);+pr_devel("Copying template to %p, size %lu\n",buff,size);+for(i=0;i<size;i++){+rc=patch_instruction((unsignedint*)buff+i,+*((unsignedint*)(optprobe_template_entry)+i));+if(rc<0)+gotoerror;+}/**Fixupthetemplatewithinstructionsto:
For CONFIG_STRICT_KERNEL_RWX align __init_begin to 16M.
We use 16M since its the larger of 2M on radix and 16M
on hash for our linear mapping. The plan is to have
.text, .rodata and everything upto __init_begin marked
as RX. Note we still have executable read only data.
We could further align read only data to another 16M
boundary. I've used keeping text plus rodata as
read-only-executable as a trade-off to doing
read-only-executable for text and read-only for
rodata.
We don't use multi PT_LOAD in PHDRS because we are
not sure if all bootloaders support them
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/kernel/vmlinux.lds.S | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
PAPR has pp0 in bit 55, currently we assumed that bit
pp0 is bit 0 (all bits in IBM order). This patch fixes
the pp0 bits for both these routines that use H_PROTECT
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/platforms/pseries/lpar.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
@@ -301,7 +301,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,intssize,unsignedlonginv_flags){unsignedlonglpar_rc;-unsignedlongflags=(newpp&7)|H_AVPN;+unsignedlongflags;unsignedlongwant_v;want_v=hpte_encode_avpn(vpn,psize,ssize);
@@ -309,6 +309,11 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,pr_devel(" update: avpnv=%016lx, hash=%016lx, f=%lx, psize: %d ...",want_v,slot,flags,psize);+/*+*Movepp0andsetthemask,pp0isbit55+*Weignorethekeysfornow.+*/+flags=((newpp&HPTE_R_PP0)>>55)|(newpp&7)|H_AVPN;lpar_rc=plpar_pte_protect(flags,slot,want_v);if(lpar_rc==H_NOT_FOUND){
@@ -379,7 +384,11 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,slot=pSeries_lpar_hpte_find(vpn,psize,ssize);BUG_ON(slot==-1);-flags=newpp&7;+/*+*Movepp0andsetthemask,pp0isbit55+*Weignorethekeysfornow.+*/+flags=((newpp&HPTE_R_PP0)>>55)|(newpp&7);lpar_rc=plpar_pte_protect(flags,slot,0);BUG_ON(lpar_rc!=H_SUCCESS);
With hash we update the bolted pte to mark it read-only. We rely
on the MMU_FTR_KERNEL_RO to generate the correct permissions
for read-only text. The radix implementation just prints a warning
in this implementation
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/include/asm/book3s/64/hash.h | 3 +++
arch/powerpc/include/asm/book3s/64/radix.h | 4 ++++
arch/powerpc/mm/pgtable-hash64.c | 35 ++++++++++++++++++++++++++++++
arch/powerpc/mm/pgtable-radix.c | 7 ++++++
arch/powerpc/mm/pgtable_64.c | 9 ++++++++
5 files changed, 58 insertions(+)
@@ -342,3 +346,34 @@ int hash__has_transparent_hugepage(void)return1;}#endif /* CONFIG_TRANSPARENT_HUGEPAGE */++#ifdef CONFIG_STRICT_KERNEL_RWX+voidhash__mark_rodata_ro(void)+{+unsignedlongstart=(unsignedlong)_stext;+unsignedlongend=(unsignedlong)__init_begin;+unsignedlongidx;+unsignedintstep,shift;+unsignedlongnewpp=PP_RXXX;++if(!mmu_has_feature(MMU_FTR_KERNEL_RO)){+pr_info("R/O rodata not supported\n");+return;+}++shift=mmu_psize_defs[mmu_linear_psize].shift;+step=1<<shift;++start=((start+step-1)>>shift)<<shift;+end=(end>>shift)<<shift;++pr_devel("marking ro start %lx, end %lx, step %x\n",+start,end,step);++for(idx=start;idx<end;idx+=step)+/* Not sure if we can do much with the return value */+mmu_hash_ops.hpte_updateboltedpp(newpp,idx,mmu_linear_psize,+mmu_kernel_ssize);++}+#endif
@@ -108,6 +108,13 @@ int radix__map_kernel_page(unsigned long ea, unsigned long pa,return0;}+#ifdef CONFIG_STRICT_KERNEL_RWX+voidradix__mark_rodata_ro(void)+{+pr_warn("Not yet implemented for radix\n");+}+#endif+staticinlinevoid__meminitprint_mapping(unsignedlongstart,unsignedlongend,unsignedlongsize)
We have the basic support in the form of patching R/O
text sections, linker scripts for extending alignment
of text data. We've also got mark_rodata_ro()
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
The patch splits the linear page mapping such that
the ones with kernel text are mapped as 2M and others
are mapped with the largest possible size - 1G. The downside
of this is that we split a 1G mapping into 512 2M mappings
for the kernel, but in the absence of that we cannot support
R/O areas in 1G, the kernel size is much smaller and using
1G as the granularity will waste a lot of space at the cost
of optimizing the TLB. The text itself should fit into about
6-8 mappings, so the effect should not be all that bad.
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/mm/pgtable-radix.c | 68 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 66 insertions(+), 2 deletions(-)
@@ -111,7 +115,52 @@ int radix__map_kernel_page(unsigned long ea, unsigned long pa,#ifdef CONFIG_STRICT_KERNEL_RWXvoidradix__mark_rodata_ro(void){-pr_warn("Not yet implemented for radix\n");+unsignedlongstart=(unsignedlong)_stext;+unsignedlongend=(unsignedlong)__init_begin;+unsignedlongidx;+unsignedintstep,shift;+pgd_t*pgdp;+pud_t*pudp;+pmd_t*pmdp;+pte_t*ptep;++if(!mmu_has_feature(MMU_FTR_KERNEL_RO)){+pr_info("R/O rodata not supported\n");+return;+}++shift=mmu_psize_defs[mmu_radix_linear_psize].shift;+step=1<<shift;++start=((start+step-1)>>shift)<<shift;+end=(end>>shift)<<shift;++pr_devel("marking ro start %lx, end %lx, step %x\n",+start,end,step);++for(idx=start;idx<end;idx+=step){+pgdp=pgd_offset_k(idx);+pudp=pud_alloc(&init_mm,pgdp,idx);+if(!pudp)+continue;+if(pud_huge(*pudp)){+ptep=(pte_t*)pudp;+gotoupdate_the_pte;+}+pmdp=pmd_alloc(&init_mm,pudp,idx);+if(!pmdp)+continue;+if(pmd_huge(*pmdp)){+ptep=pmdp_ptep(pmdp);+gotoupdate_the_pte;+}+ptep=pte_alloc_kernel(pmdp,idx);+if(!ptep)+continue;+update_the_pte:+pte_update(&init_mm,idx,ptep,_PAGE_WRITE,0,0);+}+}#endif
@@ -129,6 +178,7 @@ static int __meminit create_physical_mapping(unsigned long start,unsignedlongend){unsignedlongaddr,mapping_size=0;+unsignedlongmax_mapping_size;start=_ALIGN_UP(start,PAGE_SIZE);for(addr=start;addr<end;addr+=mapping_size){
@@ -137,9 +187,12 @@ static int __meminit create_physical_mapping(unsigned long start,gap=end-addr;previous_size=mapping_size;+max_mapping_size=PUD_SIZE;+retry:if(IS_ALIGNED(addr,PUD_SIZE)&&gap>=PUD_SIZE&&-mmu_psize_defs[MMU_PAGE_1G].shift)+mmu_psize_defs[MMU_PAGE_1G].shift&&+PUD_SIZE<=max_mapping_size)mapping_size=PUD_SIZE;elseif(IS_ALIGNED(addr,PMD_SIZE)&&gap>=PMD_SIZE&&mmu_psize_defs[MMU_PAGE_2M].shift)
@@ -147,6 +200,17 @@ static int __meminit create_physical_mapping(unsigned long start,elsemapping_size=PAGE_SIZE;+if(mapping_size==PUD_SIZE&&+addr<=__pa_symbol(__init_begin)&&+(addr+mapping_size)>=__pa_symbol(_stext)){+max_mapping_size=PMD_SIZE;+gotoretry;+}++if(addr<=__pa_symbol(__init_begin)&&+(addr+mapping_size)>=__pa_symbol(_stext))+mmu_radix_linear_psize=mapping_size;+if(mapping_size!=previous_size){print_mapping(start,addr,previous_size);start=addr;
From: Naveen N. Rao <hidden> Date: 2017-06-06 19:14:18
On 2017/06/06 02:29PM, Balbir Singh wrote:
quoted hunk
With text moving to read-only migrate optprobes to using
the patch_instruction infrastructure. Without this optprobes
will fail and complain.
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/kernel/optprobes.c | 58 ++++++++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 21 deletions(-)
@@ -198,7 +203,8 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p) kprobe_opcode_t *buff, branch_op_callback, branch_emulate_step; kprobe_opcode_t *op_callback_addr, *emulate_step_addr; long b_offset;- unsigned long nip;+ unsigned long nip, size;+ int rc, i; kprobe_ppc_optinsn_slots.insn_size = MAX_OPTINSN_SIZE;
@@ -231,8 +237,15 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p) goto error; /* Setup template */- memcpy(buff, optprobe_template_entry,- TMPL_END_IDX * sizeof(kprobe_opcode_t));+ /* We can optimize this via patch_instruction_window later */
This probably needs a TODO just so it's clear. I do think this would be
good to add since we copy many instructions while setting up the
optprobe, so this is quite slow as it exists today.
+ pr_devel("Copying template to %p, size %lu\n", buff, size);
+ for (i = 0; i < size; i++) {
+ rc = patch_instruction((unsigned int *)buff + i,
+ *((unsigned int *)(optprobe_template_entry) + i));
+ if (rc < 0)
+ goto error;
+ }
/*
* Fixup the template with instructions to:
@@ -261,8 +274,10 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p) if (!branch_op_callback || !branch_emulate_step) goto error;- buff[TMPL_CALL_HDLR_IDX] = branch_op_callback;- buff[TMPL_EMULATE_IDX] = branch_emulate_step;+ patch_instruction((unsigned int *)buff + TMPL_CALL_HDLR_IDX,+ branch_op_callback);+ patch_instruction((unsigned int *)buff + TMPL_EMULATE_IDX,+ branch_emulate_step); /* * 3. load instruction to be emulated into relevant register, and
@@ -272,8 +287,9 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p) /* * 4. branch back from trampoline */- buff[TMPL_RET_IDX] = create_branch((unsigned int *)buff + TMPL_RET_IDX,- (unsigned long)nip, 0);+ patch_instruction((unsigned int *)buff + TMPL_RET_IDX,+ create_branch((unsigned int *)buff ++ TMPL_RET_IDX, (unsigned long)nip, 0)); flush_icache_range((unsigned long)buff, (unsigned long)(&buff[TMPL_END_IDX]));
On Wed, 2017-06-07 at 00:42 +0530, Naveen N. Rao wrote:
On 2017/06/06 02:29PM, Balbir Singh wrote:
quoted
With text moving to read-only migrate optprobes to using
the patch_instruction infrastructure. Without this optprobes
will fail and complain.
<snip>
quoted
+ /* We can optimize this via patch_instruction_window later */
This probably needs a TODO just so it's clear. I do think this would be
good to add since we copy many instructions while setting up the
optprobe, so this is quite slow as it exists today.
The entire kprobe_opcode_t and int types thing is a bit messy. The size
calculation assumes nothing for now in terms of sizes, but I guess the
patch_instruction does. Another TODO?
Thanks for the review
Balbir Singh.
From: Naveen N. Rao <hidden> Date: 2017-06-07 14:22:54
On 2017/06/07 03:46PM, Balbir Singh wrote:
On Wed, 2017-06-07 at 00:42 +0530, Naveen N. Rao wrote:
quoted
On 2017/06/06 02:29PM, Balbir Singh wrote:
quoted
With text moving to read-only migrate optprobes to using
the patch_instruction infrastructure. Without this optprobes
will fail and complain.
<snip>
quoted
quoted
+ /* We can optimize this via patch_instruction_window later */
This probably needs a TODO just so it's clear. I do think this would be
good to add since we copy many instructions while setting up the
optprobe, so this is quite slow as it exists today.
I made it read like a TODO, with a TOOD:
Yes, just a nit that it would be good to have an explicit 'TODO:' there,
without which it looks like any other comment...
But, thinking about this more, we could probably simplify this by having
optprobes set things up in a local buffer before copying it into the
instruction slot. That will also make it easier for the subsequent
support to patch many instructions at once.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-06-26 05:42:53
Balbir Singh [off-list ref] writes:
quoted hunk
Today our patching happens via direct copy and
patch_instruction. The patching code is well
contained in the sense that copying bits are limited.
While considering implementation of CONFIG_STRICT_RWX,
the first requirement is to a create another mapping
that will allow for patching. We create the window using
text_poke_area, allocated via get_vm_area(), which might
be an overkill. text_poke_area is per CPU to avoid locking
Other arches do similar things, but use fixmaps. The reason
for not using fixmaps is to make use of any randomization in
the future. The code also relies on set_pte_at and pte_clear
to do the appropriate tlb flushing.
All of this should be under #ifdef STRICT_RWX. So that when STRICT_RWX=n
we basically use the old code.
-int patch_instruction(unsigned int *addr, unsigned int instr)
+static int text_area_cpu_up(unsigned int cpu)
+{
+ struct vm_struct *area;
+
+ area = get_vm_area(PAGE_SIZE, VM_ALLOC);
+ if (!area) {
+ WARN_ONCE(1, "Failed to create text area for cpu %d\n",
+ cpu);
+ return -1;
This is good, it will block bringing up a CPU if we can't get the VM
area, which is the safe option.
+ }
+ this_cpu_write(text_poke_area, area);
+ return 0;
+}
+
+static int text_area_cpu_down(unsigned int cpu)
+{
+ free_vm_area(this_cpu_read(text_poke_area));
+ return 0;
+}
+
+/*
+ * This is an early_initcall and early_initcalls happen at the right time
+ * for us, after slab is enabled and before we mark ro pages R/O. In the
+ * future if get_vm_area is randomized, this will be more flexible than
+ * fixmap
+ */
+static int __init setup_text_poke_area(void)
{
+ struct vm_struct *area;
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ area = get_vm_area(PAGE_SIZE, VM_ALLOC);
+ if (!area) {
+ WARN_ONCE(1, "Failed to create text area for cpu %d\n",
+ cpu);
+ /* Should we disable strict rwx? */
+ continue;
+ }
+ this_cpu_write(text_poke_area, area);
+ }
Use the cpuhp_setup_state() version, which will call it on the boot CPU.
And then just BUG_ON() if it fails.
Also switch this to a late_initcall(), so that if we do BUG_ON() it's
nice and late and the kernel is mostly up.
+ text_area_patch_avail = 1;
Instead of this global flag, we should just check that each CPUs
text_poke_area is non-NULL before using it ...
+ /*
+ * The barrier here ensures the write is visible to
+ * patch_instruction()
+ */
+ smp_wmb();
+ pr_info("text_poke area ready...\n");
+ return 0;
+}
+
+/*
+ * This can be called for kernel text or a module.
+ */
+static int kernel_map_addr(void *addr)
map_patch_area() ?
+{
+ unsigned long pfn;
int err;
- __put_user_size(instr, addr, 4, err);
+ if (is_vmalloc_addr(addr))
+ pfn = vmalloc_to_pfn(addr);
+ else
+ pfn = __pa_symbol(addr) >> PAGE_SHIFT;
+
.. in here.
ie. if (!this_cpu_read(text_poke_area))
return -Exxx;
This is pretty fragile, I'd rather you checked each level returned
something sane.
+ pr_devel("clearing mm %p, pte %p, kaddr %lx\n", &init_mm, pte, kaddr);
+ pte_clear(&init_mm, kaddr, pte);
+ flush_tlb_kernel_range(kaddr, kaddr + PAGE_SIZE);
+}
+
+int patch_instruction(unsigned int *addr, unsigned int instr)
+{
+ int err;
+ unsigned int *dest = NULL;
+ unsigned long flags;
+ unsigned long kaddr = (unsigned long)addr;
+
+ /*
+ * Make sure we can see any write of text_area_patch_avail
+ */
+ smp_rmb();
+
+ /*
+ * During early early boot patch_instruction is called
+ * when text_poke_area is not ready, but we still need
+ * to allow patching. We just do the plain old patching
+ * We use text_area_patch_avail, since per cpu read
+ * via __this_cpu_read of text_poke_area might not
+ * yet be available.
+ * TODO: Make text_area_patch_avail per cpu?
+ */
+ if (!text_area_patch_avail) {
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-06-26 06:12:21
Balbir Singh [off-list ref] writes:
With text moving to read-only migrate optprobes to using
the patch_instruction infrastructure. Without this optprobes
will fail and complain.
Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
arch/powerpc/kernel/optprobes.c | 58 ++++++++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 21 deletions(-)