implement sys_mprotect()
This commit is contained in:
@@ -120,8 +120,17 @@ int add_process_memory_range(struct process *process,
|
||||
unsigned long phys, unsigned long flag);
|
||||
int remove_process_memory_range(
|
||||
struct process *process, unsigned long start, unsigned long end);
|
||||
int split_process_memory_range(struct process *process,
|
||||
struct vm_range *range, uintptr_t addr, struct vm_range **splitp);
|
||||
int join_process_memory_range(struct process *process, struct vm_range *surviving,
|
||||
struct vm_range *merging);
|
||||
int change_prot_process_memory_range(
|
||||
struct process *process, struct vm_range *range,
|
||||
unsigned long newflag);
|
||||
struct vm_range *lookup_process_memory_range(
|
||||
struct process *proc, uintptr_t start, uintptr_t end);
|
||||
struct process_vm *vm, uintptr_t start, uintptr_t end);
|
||||
struct vm_range *next_process_memory_range(
|
||||
struct process_vm *vm, struct vm_range *range);
|
||||
int remove_process_region(struct process *proc,
|
||||
unsigned long start, unsigned long end);
|
||||
struct program_load_desc;
|
||||
|
||||
236
kernel/process.c
236
kernel/process.c
@@ -13,9 +13,11 @@
|
||||
//#define DEBUG_PRINT_PROCESS
|
||||
|
||||
#ifdef DEBUG_PRINT_PROCESS
|
||||
#define dkprintf kprintf
|
||||
#define dkprintf(...) kprintf(__VA_ARGS__)
|
||||
#define ekprintf(...) kprintf(__VA_ARGS__)
|
||||
#else
|
||||
#define dkprintf(...)
|
||||
#define ekprintf(...) kprintf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -114,9 +116,6 @@ struct process *clone_process(struct process *org, unsigned long pc,
|
||||
return proc;
|
||||
}
|
||||
|
||||
extern void __host_update_process_range(struct process *process,
|
||||
struct vm_range *range);
|
||||
|
||||
int update_process_page_table(struct process *process,
|
||||
struct vm_range *range, uint64_t phys,
|
||||
enum ihk_mc_pt_attribute flag)
|
||||
@@ -190,6 +189,72 @@ err:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int split_process_memory_range(struct process *proc, struct vm_range *range,
|
||||
uintptr_t addr, struct vm_range **splitp)
|
||||
{
|
||||
int error;
|
||||
struct vm_range *newrange = NULL;
|
||||
|
||||
dkprintf("split_process_memory_range(%p,%lx-%lx,%lx,%p)\n",
|
||||
proc, range->start, range->end, addr, splitp);
|
||||
|
||||
newrange = kmalloc(sizeof(struct vm_range), IHK_MC_AP_NOWAIT);
|
||||
if (!newrange) {
|
||||
ekprintf("split_process_memory_range(%p,%lx-%lx,%lx,%p):"
|
||||
"kmalloc failed\n",
|
||||
proc, range->start, range->end, addr, splitp);
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
newrange->start = addr;
|
||||
newrange->end = range->end;
|
||||
newrange->flag = range->flag;
|
||||
|
||||
range->end = addr;
|
||||
|
||||
list_add(&newrange->list, &range->list);
|
||||
|
||||
error = 0;
|
||||
if (splitp != NULL) {
|
||||
*splitp = newrange;
|
||||
}
|
||||
|
||||
out:
|
||||
dkprintf("split_process_memory_range(%p,%lx-%lx,%lx,%p): %d %p %lx-%lx\n",
|
||||
proc, range->start, range->end, addr, splitp,
|
||||
error, newrange,
|
||||
newrange? newrange->start: 0, newrange? newrange->end: 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
int join_process_memory_range(struct process *proc,
|
||||
struct vm_range *surviving, struct vm_range *merging)
|
||||
{
|
||||
int error;
|
||||
|
||||
dkprintf("join_process_memory_range(%p,%lx-%lx,%lx-%lx)\n",
|
||||
proc, surviving->start, surviving->end,
|
||||
merging->start, merging->end);
|
||||
|
||||
if ((surviving->end != merging->start)
|
||||
|| (surviving->flag != merging->flag)) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
surviving->end = merging->end;
|
||||
|
||||
list_del(&merging->list);
|
||||
ihk_mc_free(merging);
|
||||
|
||||
error = 0;
|
||||
out:
|
||||
dkprintf("join_process_memory_range(%p,%lx-%lx,%p): %d\n",
|
||||
proc, surviving->start, surviving->end, merging, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
int remove_process_memory_range(struct process *process, unsigned long start, unsigned long end)
|
||||
{
|
||||
struct process_vm * const vm = process->vm;
|
||||
@@ -287,12 +352,65 @@ int remove_process_memory_range(struct process *process, unsigned long start, un
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void insert_vm_range_list(struct process_vm *vm, struct vm_range *newrange)
|
||||
{
|
||||
struct list_head *next;
|
||||
struct vm_range *range;
|
||||
|
||||
next = &vm->vm_range_list;
|
||||
list_for_each_entry(range, &vm->vm_range_list, list) {
|
||||
if ((newrange->start < range->end) && (range->start < newrange->end)) {
|
||||
ekprintf("insert_vm_range_list(%p,%lx-%lx %lx):overlap %lx-%lx %lx\n",
|
||||
vm, newrange->start, newrange->end, newrange->flag,
|
||||
range->start, range->end, range->flag);
|
||||
panic("insert_vm_range_list\n");
|
||||
}
|
||||
|
||||
if (newrange->end <= range->start) {
|
||||
next = &range->list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&newrange->list, next);
|
||||
return;
|
||||
}
|
||||
|
||||
enum ihk_mc_pt_attribute vrflag_to_ptattr(unsigned long flag)
|
||||
{
|
||||
enum ihk_mc_pt_attribute attr;
|
||||
|
||||
attr = PTATTR_USER | PTATTR_FOR_USER;
|
||||
|
||||
if (flag & VR_REMOTE) {
|
||||
attr |= IHK_PTA_REMOTE;
|
||||
}
|
||||
else if (flag & VR_IO_NOCACHE) {
|
||||
attr |= PTATTR_UNCACHABLE;
|
||||
}
|
||||
|
||||
if ((flag & VR_PROT_MASK) != VR_PROT_NONE) {
|
||||
attr |= PTATTR_ACTIVE;
|
||||
}
|
||||
|
||||
if (flag & VR_PROT_WRITE) {
|
||||
attr |= PTATTR_WRITABLE;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
|
||||
int add_process_memory_range(struct process *process,
|
||||
unsigned long start, unsigned long end,
|
||||
unsigned long phys, unsigned long flag)
|
||||
{
|
||||
struct vm_range *range;
|
||||
int rc;
|
||||
#if 0
|
||||
extern void __host_update_process_range(struct process *process,
|
||||
struct vm_range *range);
|
||||
#endif
|
||||
|
||||
if ((start < process->vm->region.user_start)
|
||||
|| (process->vm->region.user_end < end)) {
|
||||
@@ -330,6 +448,8 @@ int add_process_memory_range(struct process *process,
|
||||
//demand paging no need to update process table now
|
||||
kprintf("demand paging do not update process page table\n");
|
||||
rc = 0;
|
||||
} else if ((range->flag & VR_PROT_MASK) == VR_PROT_NONE) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = update_process_page_table(process, range, phys, 0);
|
||||
}
|
||||
@@ -344,7 +464,7 @@ int add_process_memory_range(struct process *process,
|
||||
}
|
||||
#endif
|
||||
|
||||
list_add_tail(&range->list, &process->vm->vm_range_list);
|
||||
insert_vm_range_list(process->vm, range);
|
||||
|
||||
/* Clear content! */
|
||||
if (!(flag & (VR_REMOTE | VR_DEMAND_PAGING))
|
||||
@@ -355,21 +475,114 @@ int add_process_memory_range(struct process *process,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vm_range *lookup_process_memory_range(struct process *proc, uintptr_t start, uintptr_t end)
|
||||
struct vm_range *lookup_process_memory_range(
|
||||
struct process_vm *vm, uintptr_t start, uintptr_t end)
|
||||
{
|
||||
struct vm_range *range;
|
||||
struct vm_range *range = NULL;
|
||||
|
||||
dkprintf("lookup_process_memory_range(%p,%lx,%lx)\n", vm, start, end);
|
||||
|
||||
if (end <= start) {
|
||||
return NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(range, &proc->vm->vm_range_list, list) {
|
||||
list_for_each_entry(range, &vm->vm_range_list, list) {
|
||||
if (end <= range->start) {
|
||||
break;
|
||||
}
|
||||
if ((start < range->end) && (range->start < end)) {
|
||||
return range;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
range = NULL;
|
||||
out:
|
||||
dkprintf("lookup_process_memory_range(%p,%lx,%lx): %p %lx-%lx\n",
|
||||
vm, start, end, range,
|
||||
range? range->start: 0, range? range->end: 0);
|
||||
return range;
|
||||
}
|
||||
|
||||
struct vm_range *next_process_memory_range(
|
||||
struct process_vm *vm, struct vm_range *range)
|
||||
{
|
||||
struct vm_range *next;
|
||||
|
||||
dkprintf("next_process_memory_range(%p,%lx-%lx)\n",
|
||||
vm, range->start, range->end);
|
||||
|
||||
if (list_is_last(&range->list, &vm->vm_range_list)) {
|
||||
next = NULL;
|
||||
}
|
||||
else {
|
||||
next = list_entry(range->list.next, struct vm_range, list);
|
||||
}
|
||||
|
||||
dkprintf("next_process_memory_range(%p,%lx-%lx): %p %lx-%lx\n",
|
||||
vm, range->start, range->end, next,
|
||||
next? next->start: 0, next? next->end: 0);
|
||||
return next;
|
||||
}
|
||||
|
||||
int change_prot_process_memory_range(struct process *proc,
|
||||
struct vm_range *range, unsigned long protflag)
|
||||
{
|
||||
unsigned long newflag;
|
||||
int error;
|
||||
enum ihk_mc_pt_attribute oldattr;
|
||||
enum ihk_mc_pt_attribute newattr;
|
||||
enum ihk_mc_pt_attribute clrattr;
|
||||
enum ihk_mc_pt_attribute setattr;
|
||||
|
||||
dkprintf("change_prot_process_memory_range(%p,%lx-%lx,%lx)\n",
|
||||
proc, range->start, range->end, protflag);
|
||||
|
||||
newflag = (range->flag & ~VR_PROT_MASK) | (protflag & VR_PROT_MASK);
|
||||
if (range->flag == newflag) {
|
||||
/* nothing to do */
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
oldattr = vrflag_to_ptattr(range->flag);
|
||||
newattr = vrflag_to_ptattr(newflag);
|
||||
|
||||
clrattr = oldattr & ~newattr;
|
||||
setattr = newattr & ~oldattr;
|
||||
|
||||
ihk_mc_spinlock_lock_noirq(&proc->vm->page_table_lock);
|
||||
error = ihk_mc_pt_change_attr_range(proc->vm->page_table,
|
||||
(void *)range->start, (void *)range->end,
|
||||
clrattr, setattr);
|
||||
ihk_mc_spinlock_unlock_noirq(&proc->vm->page_table_lock);
|
||||
if (error && (error != -ENOENT)) {
|
||||
ekprintf("change_prot_process_memory_range(%p,%lx-%lx,%lx):"
|
||||
"ihk_mc_pt_change_attr_range failed: %d\n",
|
||||
proc, range->start, range->end, protflag, error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (((range->flag & VR_PROT_MASK) == PROT_NONE)
|
||||
&& !(range->flag & VR_DEMAND_PAGING)) {
|
||||
ihk_mc_spinlock_lock_noirq(&proc->vm->page_table_lock);
|
||||
error = ihk_mc_pt_alloc_range(proc->vm->page_table,
|
||||
(void *)range->start, (void *)range->end,
|
||||
newattr);
|
||||
ihk_mc_spinlock_unlock_noirq(&proc->vm->page_table_lock);
|
||||
if (error) {
|
||||
ekprintf("change_prot_process_memory_range(%p,%lx-%lx,%lx):"
|
||||
"ihk_mc_pt_alloc_range failed: %d\n",
|
||||
proc, range->start, range->end, protflag, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
range->flag = newflag;
|
||||
error = 0;
|
||||
out:
|
||||
dkprintf("change_prot_process_memory_range(%p,%lx-%lx,%lx): %d\n",
|
||||
proc, range->start, range->end, protflag, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
int init_process_stack(struct process *process, struct program_load_desc *pn,
|
||||
@@ -544,7 +757,6 @@ int remove_process_region(struct process *proc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void print_free_list(void);
|
||||
void free_process_memory(struct process *proc)
|
||||
{
|
||||
struct vm_range *range, *next;
|
||||
|
||||
134
kernel/syscall.c
134
kernel/syscall.c
@@ -205,7 +205,7 @@ static int search_free_space(size_t len, intptr_t hint, intptr_t *addrp)
|
||||
goto out;
|
||||
}
|
||||
|
||||
range = lookup_process_memory_range(proc, addr, addr+len);
|
||||
range = lookup_process_memory_range(proc->vm, addr, addr+len);
|
||||
if (range == NULL) {
|
||||
break;
|
||||
}
|
||||
@@ -437,17 +437,145 @@ SYSCALL_DECLARE(munmap)
|
||||
const size_t len = ihk_mc_syscall_arg1(ctx);
|
||||
int error;
|
||||
|
||||
dkprintf("[%d]sys_munmap(%lx,%lx)\n",
|
||||
ihk_mc_get_processor_id(), addr, len);
|
||||
|
||||
ihk_mc_spinlock_lock_noirq(&cpu_local_var(current)->vm->memory_range_lock);
|
||||
error = do_munmap(addr, len);
|
||||
ihk_mc_spinlock_unlock_noirq(&cpu_local_var(current)->vm->memory_range_lock);
|
||||
|
||||
dkprintf("[%d]sys_munmap(%lx,%lx): %d\n",
|
||||
ihk_mc_get_processor_id(), addr, len, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DECLARE(mprotect)
|
||||
{
|
||||
dkprintf("mprotect returns 0\n");
|
||||
return 0;
|
||||
const intptr_t start = ihk_mc_syscall_arg0(ctx);
|
||||
const size_t len0 = ihk_mc_syscall_arg1(ctx);
|
||||
const int prot = ihk_mc_syscall_arg2(ctx);
|
||||
struct process *proc = cpu_local_var(current);
|
||||
struct vm_regions *region = &proc->vm->region;
|
||||
size_t len;
|
||||
intptr_t end;
|
||||
struct vm_range *first;
|
||||
intptr_t addr;
|
||||
struct vm_range *range;
|
||||
int error;
|
||||
struct vm_range *changed;
|
||||
const unsigned long protflags = PROT_TO_VR_FLAG(prot);
|
||||
|
||||
dkprintf("[%d]sys_mprotect(%lx,%lx,%x)\n",
|
||||
ihk_mc_get_processor_id(), start, len0, prot);
|
||||
|
||||
len = (len0 + PAGE_SIZE - 1) & PAGE_MASK;
|
||||
end = start + len;
|
||||
|
||||
/* check arguments */
|
||||
if ((start & (PAGE_SIZE - 1))
|
||||
|| (start < region->user_start)
|
||||
|| (region->user_end <= start)
|
||||
|| (len > (region->user_end - region->user_start)
|
||||
|| ((region->user_end - len) < start))) {
|
||||
ekprintf("[%d]sys_mprotect(%lx,%lx,%x): -EINVAL\n",
|
||||
ihk_mc_get_processor_id(), start, len0, prot);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ihk_mc_spinlock_lock_noirq(&proc->vm->memory_range_lock);
|
||||
|
||||
/* check contiguous map */
|
||||
first = NULL;
|
||||
for (addr = start; addr < end; addr = range->end) {
|
||||
if (first == NULL) {
|
||||
range = lookup_process_memory_range(proc->vm, start, start+PAGE_SIZE);
|
||||
first = range;
|
||||
}
|
||||
else {
|
||||
range = next_process_memory_range(proc->vm, range);
|
||||
}
|
||||
|
||||
if ((range == NULL) || (addr < range->start)) {
|
||||
/* not contiguous */
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):not contiguous\n",
|
||||
start, len0, prot);
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (range->flag & (VR_REMOTE | VR_RESERVED | VR_IO_NOCACHE)) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):cannot change\n",
|
||||
start, len0, prot);
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* do the mprotect */
|
||||
changed = NULL;
|
||||
for (addr = start; addr < end; addr = changed->end) {
|
||||
if (changed == NULL) {
|
||||
range = first;
|
||||
}
|
||||
else {
|
||||
range = next_process_memory_range(proc->vm, changed);
|
||||
}
|
||||
if (range == NULL) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):next(%lx) failed.\n",
|
||||
start, len0, prot,
|
||||
(changed)? changed->end: -1);
|
||||
panic("sys_mprotect:next\n");
|
||||
}
|
||||
|
||||
if (range->start < addr) {
|
||||
error = split_process_memory_range(proc, range, addr, &range);
|
||||
if (error) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):split failed. %d\n",
|
||||
start, len0, prot, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (end < range->end) {
|
||||
error = split_process_memory_range(proc, range, end, NULL);
|
||||
if (error) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):split failed. %d\n",
|
||||
start, len0, prot, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
error = change_prot_process_memory_range(proc, range, protflags);
|
||||
if (error) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):change failed. %d\n",
|
||||
start, len0, prot, error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (changed == NULL) {
|
||||
changed = range;
|
||||
}
|
||||
else {
|
||||
error = join_process_memory_range(proc, changed, range);
|
||||
if (error) {
|
||||
ekprintf("sys_mprotect(%lx,%lx,%x):join failed. %d\n",
|
||||
start, len0, prot, error);
|
||||
changed = range;
|
||||
/* through */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = 0;
|
||||
out:
|
||||
ihk_mc_spinlock_unlock_noirq(&proc->vm->memory_range_lock);
|
||||
dkprintf("[%d]sys_mprotect(%lx,%lx,%x): %d\n",
|
||||
ihk_mc_get_processor_id(), start, len0, prot, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
SYSCALL_DECLARE(brk)
|
||||
|
||||
Reference in New Issue
Block a user