Diff below reintroduce the `kv_executable' flag for km_alloc(9) with a
different meaning. Instead of mapping the pages RWX, the flags allows
the caller to change the protection of the mapping to include PROT_EXEC.
This allows sti(4) to be converted to km_alloc(9), without introducing
side-effect on the actual consumers of the API and without allows RWX
mappings by default.
ok?
Index: sys/uvm/uvm_km.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_km.c,v
retrieving revision 1.132
diff -u -p -r1.132 uvm_km.c
--- sys/uvm/uvm_km.c 18 Jul 2019 23:47:33 -0000 1.132
+++ sys/uvm/uvm_km.c 5 Nov 2019 09:02:34 -0000
@@ -813,7 +813,7 @@ km_alloc(size_t sz, const struct kmem_va
struct vm_page *pg;
struct pglist pgl;
int mapflags = 0;
- vm_prot_t prot;
+ vm_prot_t prot, maxprot;
paddr_t pla_align;
int pla_flags;
int pla_maxseg;
@@ -861,7 +861,9 @@ km_alloc(size_t sz, const struct kmem_va
}
#endif
alloc_va:
- prot = PROT_READ | PROT_WRITE;
+ maxprot = prot = PROT_READ | PROT_WRITE;
+ if (kv->kv_executable)
+ maxprot |= PROT_EXEC;
if (kp->kp_pageable) {
KASSERT(kp->kp_object);
@@ -906,7 +908,7 @@ try_map:
map = *kv->kv_map;
va = vm_map_min(map);
if (uvm_map(map, &va, sz, uobj, kd->kd_prefer,
- kv->kv_align, UVM_MAPFLAG(prot, prot, MAP_INHERIT_NONE,
+ kv->kv_align, UVM_MAPFLAG(prot, maxprot, MAP_INHERIT_NONE,
MADV_RANDOM, mapflags))) {
if (kv->kv_wait && kd->kd_waitok) {
tsleep(map, PVM, "km_allocva", 0);
Index: sys/uvm/uvm_extern.h
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_extern.h,v
retrieving revision 1.149
diff -u -p -r1.149 uvm_extern.h
--- sys/uvm/uvm_extern.h 5 Nov 2019 08:18:47 -0000 1.149
+++ sys/uvm/uvm_extern.h 5 Nov 2019 09:02:35 -0000
@@ -318,13 +318,14 @@ struct vm_map *uvm_km_suballoc(vm_map_t
* kmem_map is usually fatal. Special maps like exec_map are specifically
* limited, so waiting for space in them is necessary.
* kv_singlepage - use the single page allocator.
- * kv_executable - map the physical pages with PROT_EXEC.
+ * kv_executable - allow protection to be changed to PROT_EXEC
*/
struct kmem_va_mode {
struct vm_map **kv_map;
vsize_t kv_align;
char kv_wait;
char kv_singlepage;
+ char kv_executable;
};
/*
Index: sys/dev/ic/sti.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/sti.c,v
retrieving revision 1.78
diff -u -p -r1.78 sti.c
--- sys/dev/ic/sti.c 11 Jun 2017 02:06:36 -0000 1.78
+++ sys/dev/ic/sti.c 5 Nov 2019 09:02:35 -0000
@@ -210,6 +210,10 @@ int
sti_rom_setup(struct sti_rom *rom, bus_space_tag_t iot, bus_space_tag_t memt,
bus_space_handle_t romh, bus_addr_t *bases, u_int codebase)
{
+ const struct kmem_va_mode kv_exec = {
+ .kv_map = &kernel_map,
+ .kv_executable = 1,
+ };
struct sti_dd *dd;
int error, size, i;
@@ -315,7 +319,9 @@ sti_rom_setup(struct sti_rom *rom, bus_s
return (EINVAL);
}
- if (!(rom->rom_code = uvm_km_alloc(kernel_map, round_page(size)))) {
+ rom->rom_code = (vaddr_t)km_alloc(round_page(size), &kv_exec, &kp_dirty,
+ &kd_nowait);
+ if (!rom->rom_code) {
printf(": cannot allocate %u bytes for code\n", size);
return (ENOMEM);
}
@@ -347,7 +353,8 @@ sti_rom_setup(struct sti_rom *rom, bus_s
if ((error = uvm_map_protect(kernel_map, rom->rom_code,
rom->rom_code + round_page(size), PROT_READ | PROT_EXEC, FALSE))) {
printf(": uvm_map_protect failed (%d)\n", error);
- uvm_km_free(kernel_map, rom->rom_code, round_page(size));
+ km_free((void *)rom->rom_code, round_page(size), &kv_any,
+ &kp_dirty);
return (error);
}
Index: share/man/man9/km_alloc.9
===================================================================
RCS file: /cvs/src/share/man/man9/km_alloc.9,v
retrieving revision 1.8
diff -u -p -r1.8 km_alloc.9
--- share/man/man9/km_alloc.9 24 Sep 2015 13:18:39 -0000 1.8
+++ share/man/man9/km_alloc.9 5 Nov 2019 09:02:35 -0000
@@ -58,13 +58,17 @@ must match those that were used to obtai
Typically a user will use certain predefined modes for memory allocation.
For virtual space the predefined modes are:
.Pp
-.Bl -tag -width kv_intrsafe -offset 3n -compact
+.Bl -tag -width kv_executable -offset 3n -compact
.It kv_any
Allocates the virtual space anywhere.
.It kv_intrsafe
Allocates the virtual space in the interrupt safe map.
.It kv_page
Allocates single pages.
+.It kv_executable
+A flag indicating that the protection of the mapped memory can be set
executable
+via
+.Fn uvm_map_protect .
.El
.Pp
For physical pages the predefined modes are:
@@ -178,4 +182,5 @@ returns a kernel virtual address or
if the allocation cannot be satisfied.
.Sh SEE ALSO
.Xr malloc 9 ,
+.Xr uvm_map_protect 9 ,
.Xr uvm 9