sos-code-article9.5.old/sos/main.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/main.c (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
|| (cur_thr->fixup_uaccess.return_vaddr)) | || (cur_thr->fixup_uaccess.return_vaddr)) |
{ | { |
__label__ unforce_address_space; | __label__ unforce_address_space; |
sos_bool_t need_to_setup_mmu; | sos_bool_t need_to_prepare_user_space_access; |
| |
/* Make sure to always stay in the interrupted thread's MMU | /* Do we already try to access an address space ? */ |
configuration */ | need_to_prepare_user_space_access |
need_to_setup_mmu = (cur_thr->squatted_mm_context | = (sos_thread_get_current()->squatted_address_space == NULL); |
!= sos_process_get_mm_context(cur_thr->process)); | |
if (need_to_setup_mmu) | if (need_to_prepare_user_space_access) |
| /* No: Need to configure the interrupted thread user space */ |
| |
if (SOS_OK == | if (SOS_OK == |
|
|
goto unforce_address_space; | goto unforce_address_space; |
} | } |
| |
if (need_to_setup_mmu) | if (need_to_prepare_user_space_access) |
| |
sos_bochs_printf("\e[35mTHR %p: Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\e[m\n", | sos_bochs_printf("\e[35mTHR %p: Unresolved USER page Fault at instruction 0x%x on access to address 0x%x (info=%x)!\e[m\n", |
|
|
sos_thread_exit(); | sos_thread_exit(); |
| |
unforce_address_space: | unforce_address_space: |
if (need_to_setup_mmu) | if (need_to_prepare_user_space_access) |
return; | return; |
} | } |
|
|
struct sos_process *proc_init; | struct sos_process *proc_init; |
struct sos_thread *new_thr; | struct sos_thread *new_thr; |
sos_uaddr_t ustack, start_uaddr; | sos_uaddr_t ustack, start_uaddr; |
| int fake_args[32]; |
| sos_ret_t ret; |
struct sos_fs_opened_file * init_root, * init_cwd, * unused_of; | struct sos_fs_opened_file * init_root, * init_cwd, * unused_of; |
| |
/* Create the new process */ | /* Create the new process */ |
|
|
} | } |
| |
| |
| |
start_uaddr = sos_binfmt_elf32_map(as_init, "init"); | start_uaddr = sos_binfmt_elf32_map(as_init, "init"); |
if (0 == start_uaddr) | if (0 == start_uaddr) |
|
|
return -SOS_ENOMEM; | return -SOS_ENOMEM; |
} | } |
| |
| /* Compute the address of the stack that will be used to initialize |
| the user thread */ |
| ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE |
| - sizeof(fake_args)), 4); |
| |
| /* Build fake argv/envp arguments for the init process. See |
| userland/crt.c for the format. */ |
| fake_args[0] = 0x1; /* argc */ |
| fake_args[1] = 4 * sizeof(fake_args[0]); /* offset of argv[0] */ |
| fake_args[2] = 0x0; /* delimiter between argv and envp */ |
| fake_args[3] = 0x0; /* end of envp */ |
| strzcpy ((char *) & fake_args[4], /* argv[0] contents */ |
| "init", 5); |
| |
| /* Copy the arguments to the user thread stack */ |
| ret = sos_memcpy_to_specified_userspace (as_init, |
| ustack, |
| (sos_vaddr_t) fake_args, |
| sizeof(fake_args)); |
| if (sizeof(fake_args) != ret) |
| { |
| sos_bochs_printf ("sos_memcpy_to_specified_userspace() failed, returned %d\n", ret); |
| sos_process_unref(proc_init); |
| return ret; |
| } |
| |
/* Now create the user thread */ | /* Now create the user thread */ |
new_thr = sos_create_user_thread(NULL, | new_thr = sos_create_user_thread(NULL, |
proc_init, | proc_init, |
start_uaddr, | start_uaddr, |
0, 0, | 0, 0, |
ustack + SOS_DEFAULT_USER_STACK_SIZE - 4, | ustack, |
if (! new_thr) | if (! new_thr) |
{ | { |
| |
sos-code-article9.5.old/sos/syscall.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/syscall.c (2006-02-26 19:39:40.000000000 +0100
) |
|
|
|
#include <sos/kmalloc.h> | #include <sos/kmalloc.h> |
#include <sos/klibc.h> | #include <sos/klibc.h> |
#include <drivers/bochs.h> | #include <drivers/bochs.h> |
#include <hwcore/paging.h> | |
#include <sos/umem_vmm.h> | #include <sos/umem_vmm.h> |
#include <drivers/zero.h> | #include <drivers/zero.h> |
|
|
{ | { |
sos_ret_t retval; | sos_ret_t retval; |
| |
| |
switch(syscall_id) | switch(syscall_id) |
{ | { |
case SOS_SYSCALL_ID_EXIT: | case SOS_SYSCALL_ID_EXIT: |
|
|
struct sos_process * proc; | struct sos_process * proc; |
struct sos_umem_vmm_as *new_as; | struct sos_umem_vmm_as *new_as; |
sos_uaddr_t user_str, ustack, start_uaddr; | sos_uaddr_t user_str, ustack, start_uaddr; |
| sos_uaddr_t src_argaddr; |
| sos_size_t len_args; |
sos_size_t len; | sos_size_t len; |
char * str; | char * str; |
| |
|
|
} | } |
| |
/* Get the user arguments */ | /* Get the user arguments */ |
retval = sos_syscall_get2args(user_ctxt, & user_str, & len); | retval = sos_syscall_get4args(user_ctxt, & user_str, & len, |
| & src_argaddr, & len_args); |
break; | break; |
| |
| /* Don't go any further is the arg/env array is obviously too |
| large */ |
| if (SOS_DEFAULT_USER_STACK_SIZE <= len_args) |
| { |
| retval = -SOS_EINVAL; |
| break; |
| } |
| |
/* Copy the program name into kernel sppace */ | /* Copy the program name into kernel sppace */ |
retval = sos_strndup_from_user(& str, user_str, len + 1, 0); | retval = sos_strndup_from_user(& str, user_str, len + 1, 0); |
if (SOS_OK != retval) | if (SOS_OK != retval) |
|
|
break; | break; |
} | } |
| |
| /* ustack now referes to the initial stack pointer of the new |
| user thread: update it here */ |
| ustack = SOS_ALIGN_INF((ustack + SOS_DEFAULT_USER_STACK_SIZE |
| - len_args), 4); |
| |
| if (len_args != sos_usercpy(new_as, ustack, |
| NULL, src_argaddr, |
| len_args)) |
| { |
| sos_umem_vmm_delete_as(new_as); |
| sos_kfree((sos_vaddr_t)str); |
| retval = -SOS_EFAULT; |
| break; |
| } |
| |
/* Now create the user thread */ | /* Now create the user thread */ |
new_thr = sos_create_user_thread(NULL, | new_thr = sos_create_user_thread(NULL, |
proc, | proc, |
start_uaddr, | start_uaddr, |
0, 0, | 0, 0, |
ustack + SOS_DEFAULT_USER_STACK_SIZE | ustack, |
- 4, | |
if (! new_thr) | if (! new_thr) |
{ | { |
sos_umem_vmm_delete_as(new_as); | sos_umem_vmm_delete_as(new_as); |
sos_kfree((sos_vaddr_t)str); | sos_kfree((sos_vaddr_t)str); |
retval = -SOS_ENOMEM; | retval = -SOS_ENOMEM; |
break; | break; |
| |
sos_process_set_name(proc, str); | sos_process_set_name(proc, str); |
|
|
{ | { |
sos_umem_vmm_delete_as(new_as); | sos_umem_vmm_delete_as(new_as); |
sos_kfree((sos_vaddr_t)str); | sos_kfree((sos_vaddr_t)str); |
break; | break; |
| |
/* The current thread must exit now */ | /* The current thread must exit now */ |
|
|
} | } |
break; | break; |
| |
| |
case SOS_SYSCALL_ID_MSYNC: | case SOS_SYSCALL_ID_MSYNC: |
{ | { |
sos_uaddr_t start_uaddr; | sos_uaddr_t start_uaddr; |
|
|
} | } |
break; | break; |
| |
| |
case SOS_SYSCALL_ID_NEW_THREAD: | case SOS_SYSCALL_ID_NEW_THREAD: |
{ | { |
sos_uaddr_t start_func; | sos_uaddr_t start_func; |
|
|
sos_bochs_printf("Syscall: UNKNOWN[%d]\n", syscall_id); | sos_bochs_printf("Syscall: UNKNOWN[%d]\n", syscall_id); |
retval = -SOS_ENOSUP; | retval = -SOS_ENOSUP; |
} | } |
| |
} | } |
| |
sos-code-article9.5.old/sos/thread.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/thread.c (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
static struct sos_kslab_cache *cache_thread; | static struct sos_kslab_cache *cache_thread; |
| |
| |
/** | |
* (Forwad declaration) Helper function to change the MMU config of | |
* the current executing thread. Analogous to function | |
* sos_thread_change_current_mm_context() of article 7 | |
*/ | |
static sos_ret_t change_current_mm_context(struct sos_mm_context *mm_ctxt); | |
| |
| |
{ | { |
SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING); | SOS_ASSERT_FATAL(current_thread->state == SOS_THR_RUNNING); |
|
|
SOS_ASSERT_FATAL(the_thread->process != NULL); | SOS_ASSERT_FATAL(the_thread->process != NULL); |
| |
/* It should not squat any other's address space */ | /* It should not squat any other's address space */ |
SOS_ASSERT_FATAL(the_thread->squatted_mm_context == NULL); | SOS_ASSERT_FATAL(the_thread->squatted_address_space == NULL); |
/* Perform an MMU context switch if needed */ | /* Perform an MMU context switch if needed */ |
sos_mm_context_switch_to(sos_process_get_mm_context(the_thread->process)); | sos_umem_vmm_set_current_as(sos_process_get_address_space(the_thread->process)); |
| |
/* the_thread is a kernel thread squatting a precise address | /* Restore the address space currently in use */ |
space ? */ | else |
else if (the_thread->squatted_mm_context != NULL) | sos_umem_vmm_set_current_as(the_thread->squatted_address_space); |
sos_mm_context_switch_to(the_thread->squatted_mm_context); | |
| |
| |
|
|
| |
sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr); | sos_kfree((sos_vaddr_t) thr->kernel_stack_base_addr); |
| |
/* If the thread squats an address space, release it */ | /* Not allowed to squat any user space at deletion time */ |
if (thr->squatted_mm_context) | SOS_ASSERT_FATAL(NULL == thr->squatted_address_space); |
SOS_ASSERT_FATAL(SOS_OK == change_current_mm_context(NULL)); | |
/* For a user thread: remove the thread from the process threads' list */ | /* For a user thread: remove the thread from the process threads' list */ |
if (thr->process) | if (thr->process) |
|
|
*/ | */ |
| |
| |
static sos_ret_t | |
change_current_mm_context(struct sos_mm_context *mm_ctxt) | |
{ | |
/* Retrieve the previous mm context */ | |
struct sos_mm_context * prev_mm_ctxt | |
= current_thread->squatted_mm_context; | |
| |
/* Update current thread's squatted mm context */ | |
current_thread->squatted_mm_context = mm_ctxt; | |
| |
/* Update the reference counts and switch the MMU configuration if | |
needed */ | |
if (mm_ctxt != NULL) | |
{ | |
sos_mm_context_ref(mm_ctxt); /* Because it is now referenced as | |
the squatted_mm_context field of | |
the thread */ | |
sos_mm_context_switch_to(mm_ctxt); | |
} | |
else | |
sos_mm_context_unref(prev_mm_ctxt); /* Because it is not referenced as | |
the squatted_mm_context field of | |
the thread any more */ | |
| |
return SOS_OK; | |
} | |
| |
| |
sos_thread_prepare_user_space_access(struct sos_umem_vmm_as * dest_as, | sos_thread_prepare_user_space_access(struct sos_umem_vmm_as * dest_as, |
sos_vaddr_t fixup_retvaddr) | sos_vaddr_t fixup_retvaddr) |
|
|
| |
dest_as = sos_process_get_address_space(current_thread->process); | dest_as = sos_process_get_address_space(current_thread->process); |
} | } |
else | |
/* Don't allow to access to an address space different than that | |
of the current thread if the page fault are allowed ! */ | |
SOS_ASSERT_FATAL(! fixup_retvaddr); | |
sos_disable_IRQs(flags); | sos_disable_IRQs(flags); |
SOS_ASSERT_FATAL(NULL == current_thread->squatted_mm_context); | SOS_ASSERT_FATAL(NULL == current_thread->squatted_address_space); |
| |
/* Change the MMU configuration and init the fixup return address */ | /* Change the MMU configuration and init the fixup return address */ |
retval = change_current_mm_context(sos_umem_vmm_get_mm_context(dest_as)); | retval = sos_umem_vmm_set_current_as(dest_as); |
{ | { |
| current_thread->squatted_address_space = dest_as; |
current_thread->fixup_uaccess.return_vaddr = fixup_retvaddr; | current_thread->fixup_uaccess.return_vaddr = fixup_retvaddr; |
current_thread->fixup_uaccess.faulted_uaddr = 0; | current_thread->fixup_uaccess.faulted_uaddr = 0; |
| |
sos_restore_IRQs(flags); | sos_restore_IRQs(flags); |
|
|
sos_ui32_t flags; | sos_ui32_t flags; |
| |
sos_disable_IRQs(flags); | sos_disable_IRQs(flags); |
SOS_ASSERT_FATAL(NULL != current_thread->squatted_mm_context); | SOS_ASSERT_FATAL(NULL != current_thread->squatted_address_space); |
/* Don't impose anything regarding the current MMU configuration anymore */ | /* Don't impose anything regarding the current MMU configuration anymore */ |
retval = change_current_mm_context(NULL); | |
current_thread->fixup_uaccess.faulted_uaddr = 0; | current_thread->fixup_uaccess.faulted_uaddr = 0; |
| |
| retval = sos_umem_vmm_set_current_as(NULL); |
| current_thread->squatted_address_space = NULL; |
| |
sos_restore_IRQs(flags); | sos_restore_IRQs(flags); |
return retval; | return retval; |
} | } |
| |
sos-code-article9.5.old/sos/uaccess.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/uaccess.c (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
({ if (__uaccess_zero_fool_gcc) goto lbl; }) | ({ if (__uaccess_zero_fool_gcc) goto lbl; }) |
| |
| |
static sos_ret_t nocheck_user_memcpy(sos_vaddr_t dest, | /** |
| * @param user_as The user address space we want to transfer from/to |
| * |
| * @return >=0 : number of bytes successfully transferred, <0 an error code |
| */ |
| static sos_ret_t nocheck_user_memcpy(struct sos_umem_vmm_as * user_as, |
| sos_vaddr_t dest, |
sos_size_t size, | sos_size_t size, |
sos_bool_t transfer_from_user) | sos_bool_t transfer_from_user) |
|
|
if (size <= 0) | if (size <= 0) |
return 0; | return 0; |
| |
retval = sos_thread_prepare_user_space_access(NULL, | retval = sos_thread_prepare_user_space_access(user_as, |
if (SOS_OK != retval) | if (SOS_OK != retval) |
return retval; | return retval; |
|
|
sos_size_t size) | sos_size_t size) |
{ | { |
/* Make sure user is trying to access user space */ | /* Make sure user is trying to access user space */ |
if (user_from < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(user_from, size) ) |
| |
if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size) | /* Make sure the copy totally resides inside kernel space */ |
| if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) ) |
| |
return nocheck_user_memcpy(kernel_to, user_from, size, TRUE); | return nocheck_user_memcpy(NULL, kernel_to, user_from, size, TRUE); |
| |
| |
|
|
sos_vaddr_t kernel_from, | sos_vaddr_t kernel_from, |
sos_size_t size) | sos_size_t size) |
{ | { |
| /* Make sure the source totally resides inside kernel space */ |
| if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) ) |
| return -SOS_EPERM; |
| |
/* Make sure user is trying to access user space */ | /* Make sure user is trying to access user space */ |
if (user_to < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(user_to, size) ) |
| |
if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size) | return nocheck_user_memcpy(NULL, user_to, kernel_from, size, FALSE); |
| } |
| |
| |
| sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to, |
| struct sos_umem_vmm_as * src_as, |
| sos_uaddr_t user_from, |
| sos_size_t size) |
| { |
| if (NULL == src_as) |
| return -SOS_EINVAL; |
| |
| /* Make sure user is trying to access user space */ |
| if (! SOS_PAGING_IS_USER_AREA(user_from, size) ) |
| return -SOS_EPERM; |
| |
| /* Make sure the copy totally resides inside kernel space */ |
| if (! SOS_PAGING_IS_KERNEL_AREA(kernel_to, size) ) |
| return -SOS_EPERM; |
| |
| return nocheck_user_memcpy(src_as, kernel_to, user_from, size, TRUE); |
| } |
| |
| |
| sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as, |
| sos_uaddr_t user_to, |
| sos_vaddr_t kernel_from, |
| sos_size_t size) |
| { |
| if (NULL == dst_as) |
| return -SOS_EINVAL; |
| |
| /* Make sure the source totally resides inside kernel space */ |
| if (! SOS_PAGING_IS_KERNEL_AREA(kernel_from, size) ) |
| return -SOS_EPERM; |
| |
| /* Make sure user is trying to access user space */ |
| if (! SOS_PAGING_IS_USER_AREA(user_to, size) ) |
| return -SOS_EPERM; |
| |
| return nocheck_user_memcpy(dst_as, user_to, kernel_from, size, FALSE); |
| } |
| |
| |
| sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as, |
| sos_uaddr_t dst_uaddr, |
| struct sos_umem_vmm_as * src_as, |
| sos_uaddr_t src_uaddr, |
| sos_size_t size) |
| { |
| sos_vaddr_t kern_addr; |
| sos_ret_t retval; |
| |
| if (size <= 0) |
| return 0; |
| |
| /* Make sure user is trying to access user space */ |
| if (! SOS_PAGING_IS_USER_AREA(src_uaddr, size) ) |
| return -SOS_EPERM; |
| if (! SOS_PAGING_IS_USER_AREA(dst_uaddr, size) ) |
| |
return nocheck_user_memcpy(user_to, kernel_from, size, FALSE); | kern_addr = sos_kmalloc(size, 0); |
| if (! kern_addr) |
| return -SOS_ENOMEM; |
| |
| retval = nocheck_user_memcpy(src_as, kern_addr, src_uaddr, |
| size, TRUE); |
| if (retval <= 0) |
| { |
| sos_kfree((sos_vaddr_t)kern_addr); |
| return retval; |
| } |
| |
| retval = nocheck_user_memcpy(dst_as, dst_uaddr, kern_addr, |
| retval, FALSE); |
| |
| sos_kfree((sos_vaddr_t)kern_addr); |
| |
| return retval; |
| |
| |
|
|
return 0; | return 0; |
| |
/* Make sure user is trying to access user space */ | /* Make sure user is trying to access user space */ |
if (user_str < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(user_str, max_len) ) |
| |
/* Don't allow invalid max_len */ | |
if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) ) | |
return -SOS_EINVAL; | |
| |
(sos_vaddr_t) && catch_pgflt); | (sos_vaddr_t) && catch_pgflt); |
if (SOS_OK != retval) | if (SOS_OK != retval) |
|
|
sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from, | sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from, |
sos_size_t max_len) | sos_size_t max_len) |
{ | { |
| if (max_len <= 0) |
| return -SOS_EINVAL; |
| |
/* Make sure user is trying to access user space */ | /* Make sure user is trying to access user space */ |
if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(user_from, max_len) ) |
| |
/* Don't allow invalid max_len */ | /* Make sure the copy totally resides inside kernel space */ |
if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) ) | if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_to, max_len) ) |
return -SOS_EINVAL; | return -SOS_EPERM; |
return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len); | return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len); |
} | } |
|
|
sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from, | sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from, |
sos_size_t max_len) | sos_size_t max_len) |
{ | { |
/* Make sure user is trying to access user space */ | if (max_len <= 0) |
if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS) | return -SOS_EINVAL; |
| |
| /* Make sure the source totally resides inside kernel space */ |
| if (! SOS_PAGING_IS_KERNEL_AREA((sos_vaddr_t)kernel_from, max_len) ) |
| |
/* Don't allow invalid max_len */ | /* Make sure user is trying to access user space */ |
if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) ) | if (! SOS_PAGING_IS_USER_AREA(user_to, max_len) ) |
return -SOS_EINVAL; | return -SOS_EPERM; |
return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len); | return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len); |
} | } |
| |
sos-code-article9.5.old/sos/uaccess.h (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/uaccess.h (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
| |
#include <sos/types.h> | #include <sos/types.h> |
#include <sos/errno.h> | #include <sos/errno.h> |
| #include <sos/umem_vmm.h> |
/** | /** |
* Retrieve a bunch of data from the user space of the | * Retrieve a bunch of data from the user space of the |
|
|
| |
| |
/** | /** |
* Transfer a bunch of data to the user space of the | * Transfer a bunch of data from kernel space into the user space of |
* current_thread->process | * the current_thread->process |
* @return <0 n error ! Return the number of bytes successfully copied | * @return <0 n error ! Return the number of bytes successfully copied |
* otherwise. | * otherwise. |
|
|
| |
| |
/** | /** |
| * Variant of sos_memcpy_from_user() |
| * Retrieve a bunch of data from the user space of the given src_as |
| * address space into kernel space |
| * |
| * @return NULL on error (including unresolved page fault during |
| * transfer) |
| * |
| * @note src_as MUST NOT be NULL |
| */ |
| sos_ret_t sos_memcpy_from_specified_userspace(sos_vaddr_t kernel_to, |
| struct sos_umem_vmm_as * src_as, |
| sos_uaddr_t user_from, |
| sos_size_t size); |
| |
| |
| /** |
| * Variant of sos_memcpy_to_user() |
| * Transfer a bunch of data from kernel space into the user space of |
| * the given dst_as address space |
| * |
| * @return <0 n error ! Return the number of bytes successfully copied |
| * otherwise. |
| * |
| * @note dst_as MUST NOT be NULL |
| */ |
| sos_ret_t sos_memcpy_to_specified_userspace(struct sos_umem_vmm_as * dst_as, |
| sos_uaddr_t user_to, |
| sos_vaddr_t kernel_from, |
| sos_size_t size); |
| |
| /** |
| * Copy data from an user space into another |
| * @return The number of bytes successfuly copied |
| * @note dst_as and src_as may be NULL, in which case the current |
| * thread->process address space is used for the copy |
| */ |
| sos_ret_t sos_usercpy(struct sos_umem_vmm_as * dst_as, |
| sos_uaddr_t dst_uaddr, |
| struct sos_umem_vmm_as * src_as, |
| sos_uaddr_t src_uaddr, |
| sos_size_t size); |
| |
| |
| /** |
* @return the length of the given user space string user_str | * @return the length of the given user space string user_str |
* (excluding the trailing \0), up to max_len bytes. <0 on error | * (excluding the trailing \0), up to max_len bytes. <0 on error |
* (unresolved page fault, etc.) | * (unresolved page fault, etc.) |
| |
sos-code-article9.5.old/sos/umem_vmm.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/umem_vmm.c (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
#include <drivers/bochs.h> | #include <drivers/bochs.h> |
#include <hwcore/mm_context.h> | #include <hwcore/mm_context.h> |
#include <hwcore/paging.h> | #include <hwcore/paging.h> |
| #include <hwcore/irq.h> |
#include <drivers/zero.h> | #include <drivers/zero.h> |
| |
#include "umem_vmm.h" | #include "umem_vmm.h" |
|
|
} | } |
| |
| |
| static struct sos_umem_vmm_as * current_address_space = NULL; |
| struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void) |
| { |
| return current_address_space; |
| } |
| |
| |
| sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as) |
| { |
| sos_ui32_t flags; |
| struct sos_umem_vmm_as *prev_as = current_address_space; |
| |
| if (current_address_space == as) |
| return SOS_OK; |
| |
| if (NULL != as) |
| { |
| sos_disable_IRQs(flags); |
| sos_process_ref(sos_umem_vmm_get_process(as)); |
| sos_mm_context_switch_to(sos_umem_vmm_get_mm_context(as)); |
| current_address_space = as; |
| sos_restore_IRQs(flags); |
| } |
| else |
| current_address_space = as; |
| |
| if (prev_as) |
| sos_process_unref(sos_umem_vmm_get_process(prev_as)); |
| |
| return SOS_OK; |
| } |
| |
| |
struct sos_umem_vmm_as * | struct sos_umem_vmm_as * |
sos_umem_vmm_create_empty_as(struct sos_process *owner) | sos_umem_vmm_create_empty_as(struct sos_process *owner) |
{ | { |
|
|
| |
| |
struct sos_umem_vmm_as * | struct sos_umem_vmm_as * |
sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner) | sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as, |
| struct sos_process *for_owner) |
__label__ undo_creation; | __label__ undo_creation; |
struct sos_umem_vmm_as * my_as; | |
int nb_vr; | int nb_vr; |
| |
|
|
if (! new_as) | if (! new_as) |
return NULL; | return NULL; |
| |
my_as = sos_process_get_address_space(sos_thread_get_current()->process); | new_as->process = for_owner; |
new_as->process = owner; | |
| |
/* | /* |
|
|
* COW) | * COW) |
*/ | */ |
SOS_ASSERT_FATAL(SOS_OK | SOS_ASSERT_FATAL(SOS_OK |
== sos_thread_prepare_user_space_access(my_as, | == sos_thread_prepare_user_space_access(model_as, |
NULL)); | NULL)); |
| |
/* Copy the virtual regions */ | /* Copy the virtual regions */ |
list_foreach_named(my_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as) | list_foreach_named(model_as->list_vr, model_vr, nb_vr, prev_in_as, next_in_as) |
struct sos_umem_vmm_vr * vr; | struct sos_umem_vmm_vr * vr; |
| |
|
|
} | } |
| |
/* Now copy the current MMU configuration */ | /* Now copy the current MMU configuration */ |
new_as->mm_context = sos_mm_context_duplicate(my_as->mm_context); | new_as->mm_context = sos_mm_context_duplicate(model_as->mm_context); |
goto undo_creation; | goto undo_creation; |
| |
/* Correct behavior */ | /* Correct behavior */ |
new_as->heap_start = my_as->heap_start; | new_as->heap_start = model_as->heap_start; |
new_as->heap_size = my_as->heap_size; | new_as->heap_size = model_as->heap_size; |
new_as->phys_total = my_as->phys_total; | new_as->phys_total = model_as->phys_total; |
memcpy(& new_as->vm_total, & my_as->vm_total, sizeof(struct vm_usage)); | memcpy(& new_as->vm_total, & model_as->vm_total, sizeof(struct vm_usage)); |
memcpy(& new_as->vm_shrd, & my_as->vm_shrd, sizeof(struct vm_usage)); | memcpy(& new_as->vm_shrd, & model_as->vm_shrd, sizeof(struct vm_usage)); |
return new_as; | return new_as; |
| |
|
|
*/ | */ |
| |
/* Make sure the hint_uaddr hint is valid */ | /* Make sure the hint_uaddr hint is valid */ |
if (hint_uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(hint_uaddr, size) ) |
{ retval = -SOS_EINVAL; goto return_mmap; } | |
if (hint_uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) | |
| |
/* Unmap any overlapped VR */ | /* Unmap any overlapped VR */ |
|
|
sos_uaddr_t uaddr, sos_size_t size) | sos_uaddr_t uaddr, sos_size_t size) |
{ | { |
struct sos_umem_vmm_vr *vr, *preallocated_vr; | struct sos_umem_vmm_vr *vr, *preallocated_vr; |
sos_bool_t need_to_setup_mmu; | |
| sos_bool_t need_to_change_address_space = FALSE; |
| |
if (! SOS_IS_PAGE_ALIGNED(uaddr)) | if (! SOS_IS_PAGE_ALIGNED(uaddr)) |
return -SOS_EINVAL; | return -SOS_EINVAL; |
|
|
size = SOS_PAGE_ALIGN_SUP(size); | size = SOS_PAGE_ALIGN_SUP(size); |
| |
/* Make sure the uaddr is valid */ | /* Make sure the uaddr is valid */ |
if (uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(uaddr, size) ) |
return -SOS_EINVAL; | |
if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) | |
| |
/* In some cases, the unmapping might imply a VR to be split into | /* In some cases, the unmapping might imply a VR to be split into |
|
|
uaddr, size, vr->start, vr->size); | uaddr, size, vr->start, vr->size); |
} | } |
| |
need_to_setup_mmu = (sos_thread_get_current()->squatted_mm_context | /* When called from mresize, the address space is already squatted, |
!= as->mm_context); | so we don't have to change it again */ |
if (need_to_setup_mmu) | need_to_change_address_space |
| = (as != sos_thread_get_current()->squatted_address_space); |
| |
| if (need_to_change_address_space) |
== sos_thread_prepare_user_space_access(as, | == sos_thread_prepare_user_space_access(as, |
(sos_vaddr_t) | (sos_vaddr_t) |
NULL)); | NULL)); |
| |
| |
| /* Begin independent sub-block */ |
sos_ret_t sz_unmapped = sos_paging_unmap_interval(uaddr, size); | sos_ret_t sz_unmapped = sos_paging_unmap_interval(uaddr, size); |
SOS_ASSERT_FATAL(sz_unmapped >= 0); | SOS_ASSERT_FATAL(sz_unmapped >= 0); |
as->phys_total -= sz_unmapped; | as->phys_total -= sz_unmapped; |
} | } |
if (need_to_setup_mmu) | /* End independent sub-block */ |
| |
| if (need_to_change_address_space) |
| |
if (! used_preallocated_vr) | if (! used_preallocated_vr) |
|
|
size = SOS_PAGE_ALIGN_SUP(size); | size = SOS_PAGE_ALIGN_SUP(size); |
| |
/* Make sure the uaddr is valid */ | /* Make sure the uaddr is valid */ |
if (uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(uaddr, size) ) |
return -SOS_EINVAL; | |
if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) | |
| |
/* Pre-allocate 2 new VRs (same reason as for unmap). Because chprot | /* Pre-allocate 2 new VRs (same reason as for unmap). Because chprot |
|
|
size = SOS_PAGE_ALIGN_SUP(size); | size = SOS_PAGE_ALIGN_SUP(size); |
| |
/* Make sure the uaddr is valid */ | /* Make sure the uaddr is valid */ |
if (uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(uaddr, size) ) |
return -SOS_EINVAL; | |
if (uaddr > SOS_PAGING_TOP_USER_ADDRESS - size) | |
| |
/* Go from page to page, and for each dirty page in the region, call | /* Go from page to page, and for each dirty page in the region, call |
|
|
struct sos_umem_vmm_vr *vr, *prev_vr, *next_vr; | struct sos_umem_vmm_vr *vr, *prev_vr, *next_vr; |
| |
/* Make sure the new uaddr is valid */ | /* Make sure the new uaddr is valid */ |
if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) ) |
return -SOS_EINVAL; | |
if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size) | |
| |
old_uaddr = SOS_PAGE_ALIGN_INF(old_uaddr); | old_uaddr = SOS_PAGE_ALIGN_INF(old_uaddr); |
|
|
must_move_vr |= TRUE; | must_move_vr |= TRUE; |
| |
/* If VR would be out-of-user-space, it must be moved */ | /* If VR would be out-of-user-space, it must be moved */ |
if (*new_uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(*new_uaddr, new_size) ) |
must_move_vr |= TRUE; | |
if (*new_uaddr > SOS_PAGING_TOP_USER_ADDRESS - new_size) | |
| |
/* The VR must be moved but the user forbids it */ | /* The VR must be moved but the user forbids it */ |
|
|
sos_bool_t write_access, | sos_bool_t write_access, |
sos_bool_t user_access) | sos_bool_t user_access) |
{ | { |
struct sos_process *process = sos_thread_get_current()->process; | |
struct sos_umem_vmm_vr *vr; | struct sos_umem_vmm_vr *vr; |
| |
if (! process) | as = current_address_space; |
return -SOS_EFAULT; | |
| |
as = sos_process_get_address_space(process); | |
return -SOS_EFAULT; | return -SOS_EFAULT; |
| |
|
|
struct sos_umem_vmm_vr *vr; | struct sos_umem_vmm_vr *vr; |
int nb_vr; | int nb_vr; |
| |
if (uaddr < SOS_PAGING_BASE_USER_ADDRESS) | if (! SOS_PAGING_IS_USER_AREA(uaddr, 1) ) |
return NULL; | |
if (uaddr > SOS_PAGING_TOP_USER_ADDRESS) | |
| |
list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as) | list_foreach_named(as->list_vr, vr, nb_vr, prev_in_as, next_in_as) |
| |
sos-code-article9.5.old/sos/umem_vmm.h (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/sos/umem_vmm.h (2006-02-26 19:39:41.000000000 +0100
) |
|
|
|
| |
| |
/** | /** |
| * Get the current effective address space. This may be the "normal" |
| * address space of the current thread, but not necessarilly: this |
| * might be the address space of just another process ! This may |
| * happen if a kernel thread of process P wants to access the address |
| * space of another process P2. This might also be NULL (no access to |
| * user space needed). |
| */ |
| struct sos_umem_vmm_as * sos_umem_vmm_get_current_as(void); |
| |
| |
| /** |
| * Change the current effective address space, eventually |
| * reconfiguring the MMU. This will increase the owning process's |
| * reference count of the given AS, and decrease that of the previous |
| * AS (when different). |
| * |
| * @param as may be NULL (no need to access user space) |
| */ |
| sos_ret_t sos_umem_vmm_set_current_as(struct sos_umem_vmm_as * as); |
| |
| |
| /** |
* Create a new, empty, address space | * Create a new, empty, address space |
* | * |
* @param owner The process that will own the new address space | * @param owner The process that will own the new address space |
|
|
| |
| |
/** | /** |
* Create a new address space, copy of the current thread's address | * Create a new address space, copy of the model_as address |
* marked 'read-only' to activate the "copy-on-write" semantics. | * marked 'read-only' to activate the "copy-on-write" semantics. |
* | * |
* @param owner The process that will hold the new address space | * @param model_as The address space to copy |
| * @param for_owner The process that will hold the new address space |
* @note automatically calls | * @note automatically calls |
* sos_thread_prepare_user_space_access()/sos_thread_end_user_space_access() | * sos_thread_prepare_user_space_access()/sos_thread_end_user_space_access() |
*/ | */ |
struct sos_umem_vmm_as * | struct sos_umem_vmm_as * |
sos_umem_vmm_duplicate_current_thread_as(struct sos_process *owner); | sos_umem_vmm_duplicate_as(struct sos_umem_vmm_as * model_as, |
| struct sos_process *for_owner); |
| |
/** | /** |
| |
sos-code-article9.5.old/userland/crt.c (2006-01-22 12:48:31.000000000 +0100
) |
|
sos-code-article9.5/userland/crt.c (2006-02-26 19:39:42.000000000 +0100
) |
|
|
|
| |
#include <hwcore/swintr.h> | #include <hwcore/swintr.h> |
#include <string.h> | #include <string.h> |
| |
| #include "libc.h" /* putenv */ |
#include "crt.h" | #include "crt.h" |
| |
/** | /** |
| * Macro used to retrieve the 2 parameters passed to any new thread |
| * (architecture-dependent) |
| */ |
| #define GET_THREAD_PARAMETERS(param1, param2) \ |
| asm volatile ("movl %%eax, %0 ; movl %%ebx, %1" \ |
| : "=g"(param1), "=g"(param2) : : "%eax", "%ebx" ) |
| |
| |
| /** |
| * Helper function to retrieve the arg array from the parameter area |
| * sent by the parent. |
| * |
| * Format of the parent's args area: |
| * Number of arguments |
| * Offset of arg 0 (program's name) |
| * Offset of arg 1 |
| * ... |
| * Offset of arg N (last argument) |
| * NULL |
| * Offset of environnement variable 0 |
| * Offset of environnement variable 1 |
| * ... |
| * Offset of environnement variable N |
| * NULL |
| * Contents of arg 0 with terminal \0 |
| * Contents of arg 1 with terminal \0 |
| * ... |
| * Contents of arg N with terminal \0 |
| * Contents of env 0 with terminal \0 |
| * Contents of env 1 with terminal \0 |
| * ... |
| * Contents of env N with terminal \0 |
| * |
| * This function simply transforms the N offsets into the real |
| * addresses of the arg X contents (ie it simply adds the base address |
| * of the area). |
| */ |
| static void unmarshall_argv_envp(void * args, |
| int * argc, |
| const char ** * argv, |
| const char ** * envp) |
| { |
| addr_t * offset = (addr_t*) args; |
| *argc = 0; |
| *argv = NULL; |
| |
| offset = args; |
| |
| /* Get argc */ |
| *argc = * (int*) offset; |
| offset ++; |
| |
| /* Get base of argv */ |
| *argv = (const char **) offset; |
| |
| /* Walk the offsets array and transform these offsets into memory |
| addresses */ |
| for ( ; 0 != *offset ; offset ++) |
| *offset += (addr_t) args; |
| |
| /* Skip the NULL separating argv from envp */ |
| offset ++; |
| |
| /* Get base of envp */ |
| *envp = (const char **) offset; |
| |
| /* Walk the offsets array and transform these offsets into memory |
| addresses */ |
| for ( ; 0 != *offset ; offset ++) |
| { |
| *offset += (addr_t) args; |
| |
| /* Register this environment variable */ |
| putenv((char*) *offset); |
| } |
| } |
| |
| |
| /** |
* Starter function ! | * Starter function ! |
*/ | */ |
void _start(void) __attribute__((noreturn)); | /** Function called by crt_asm.S:_start to start user program */ |
void _start(void) | void _cmain(const char* arg_env_area[]) __attribute__((noreturn)); |
| void _cmain(const char* arg_env_area[]) |
| |
| /* Will hold the parameters that will be passed to main */ |
| const char** argv; |
| const char** envp; |
| int argc; |
| |
| |
/* This starter function expects a main() function somewhere */ | /* This starter function expects a main() function somewhere */ |
extern int main(void); | extern int main(int argc, char const* argv[], |
| char const* envp[]); |
/* Reset the bss section */ | /* Setup the arguments list and the environment variables */ |
extern char _bbss, _ebss; | unmarshall_argv_envp (arg_env_area, & argc, & argv, & envp); |
memset(& _bbss, 0x0, (& _ebss) - (& _bbss)); | |
_sos_exit(main()); | _sos_exit(main(argc, argv, envp)); |
| |
| |
|
|
} | } |
| |
| |
int _sos_exec(const char * prog) | int _sos_exec(const char * prog, |
{ | void const* args, |
return _sos_syscall2(SOS_SYSCALL_ID_EXEC, (unsigned int)prog, | size_t arglen) |
(unsigned int)strlen(prog)); | { |
| return _sos_syscall4(SOS_SYSCALL_ID_EXEC, (unsigned int)prog, |
| (unsigned int)strlen(prog), |
| (unsigned int)args, |
| (unsigned int)arglen); |
| |
| |
|
|
*/ | */ |
static void thread_routine(void) | static void thread_routine(void) |
{ | { |
/* NOTE: variables as named registers is a gcc extension */ | sos_thread_func_t * func; |
register unsigned long int reg_arg1 asm("%eax"); | unsigned long int arg; |
register unsigned long int reg_arg2 asm("%ebx"); | |
sos_thread_func_t * func = (sos_thread_func_t*)reg_arg1; | GET_THREAD_PARAMETERS(func, arg); |
unsigned long int arg = reg_arg2; | |
func(arg); | func(arg); |
_sos_exit(0); | _sos_exit(0); |
| |
sos-code-article9.5.old/userland/libc.c (2006-01-22 12:48:32.000000000 +0100
) |
|
sos-code-article9.5/userland/libc.c (2006-02-26 19:39:42.000000000 +0100
) |
|
|
|
} | } |
| |
| |
int exec(const char *progname) | /** |
| * Helper to transform the array of argv and envp into an area |
| * suitable for the crt.c:unmarshall_argv_envp() function. |
| * @see crt.c for the format of this array. |
| */ |
| static void const* marshall_execve_parameters(char* const argv[], |
| char* const envp[], |
| /*out*/size_t * sz) |
| { |
| size_t i, i_offset, count_argv, count_envp, size; |
| char * str; |
| void * result; |
| addr_t * offset; |
| |
| *sz = 0; |
| |
| /* Count the number of argv parameters and the size to allocate */ |
| for (count_argv = 0, size = 0 ; argv && argv[count_argv] != NULL ; count_argv ++) |
| size += strlen(argv[count_argv]) + 1 /* trailing \0 */; |
| |
| /* Count the number of envp parameters and the size to allocate */ |
| for (count_envp = 0 ; envp && envp[count_envp] != NULL ; count_envp ++) |
| size += strlen(envp[count_envp]) + 1 /* trailing \0 */; |
| |
| size += (count_argv + count_envp + 3) * sizeof(addr_t); |
| result = malloc(size); |
| if (NULL == result) |
| return NULL; |
| |
| /* Build the array of offsets and the string contents */ |
| offset = (addr_t*)result; |
| str = (char*) & offset[count_argv + count_envp + 3]; |
| i_offset = 0; |
| |
| /* First element of the array is the number of argv entries */ |
| offset [i_offset++] = count_argv; |
| |
| /* Marshall the argv array */ |
| for (i = 0 ; i < count_argv ; i ++, i_offset ++) |
| { |
| char const* c; |
| |
| offset[i_offset] = (void*)str - result; |
| |
| for (c = argv[i] ; *c ; c ++, str ++) |
| *str = *c; |
| *str = '\0'; str ++; |
| } |
| |
| /* The NULL between argv and envp */ |
| offset [i_offset++] = 0; |
| |
| for (i = 0 ; i < count_envp ; i ++, i_offset ++) |
| { |
| char const* c; |
| |
| offset[i_offset] = (void*)str - result; |
| |
| for (c = envp[i] ; *c ; c ++, str ++) |
| *str = *c; |
| *str = '\0'; str ++; |
| } |
| |
| /* Final NULL */ |
| offset[count_argv + count_envp + 2] = 0; |
| *sz = size; |
| |
| return result; |
| } |
| |
| |
| int execve(const char *filename, |
| char *const argv [], |
| char *const envp[]) |
return _sos_exec(progname); | void const* args_area; |
| size_t args_sz; |
| int retval; |
| |
| args_area = marshall_execve_parameters(argv, envp, & args_sz); |
| |
| retval = _sos_exec(filename, |
| args_area, args_sz); |
| |
| if (NULL != args_area) |
| free((void*)args_area); |
| |
| return retval; |
| } |
| |
| |
| int execv(const char * filename, |
| char * const argv []) |
| { |
| return execve(filename, argv, environ); |
| } |
| |
| |
| static int compute_nargs(va_list ap) |
| { |
| int nargs = 0; |
| while (va_arg(ap, char*)) |
| nargs ++; |
| return nargs; |
| } |
| |
| |
| static int vexec(const char *path, va_list ap, int envp_in_list) |
| { |
| int retval; |
| char ** vector, **entry; |
| char *str; |
| char ** envp; |
| |
| size_t nargs = compute_nargs(ap); |
| |
| /* Allocate the char* vector */ |
| vector = malloc((nargs + 1)*sizeof(char*)); |
| if (! vector) |
| return -1; |
| |
| /* Fill it */ |
| entry = vector; |
| while ((str = va_arg(ap, char*)) != NULL) |
| { |
| *entry = str; |
| entry ++; |
| } |
| *entry = NULL; |
| |
| if (envp_in_list) |
| envp = (char**)va_arg(ap, char*); |
| else |
| envp = environ; |
| |
| retval = execve(path, vector, envp); |
| free(vector); |
| return retval; |
| } |
| |
| |
| int execl(const char *path, ...) |
| { |
| int retval; |
| va_list ap; |
| va_start(ap, path); |
| retval = vexec(path, ap, FALSE); |
| va_end(ap); |
| return retval; |
| } |
| |
| |
| int execle(const char *path, ...) |
| { |
| int retval; |
| va_list ap; |
| va_start(ap, path); |
| retval = vexec(path, ap, TRUE); |
| va_end(ap); |
| return retval; |
| } |
| |
| |
| int exec(char * const filename) |
| { |
| return execl(filename, filename, NULL); |
| |
| |
|
|
} | } |
| |
| |
| /** |
| * Max number of environment variables |
| */ |
| #define SOS_MAX_ENVVARS 1024 |
| |
| char **environ = NULL; |
| |
| |
| /** |
| * Compare the keys of two strings of the form "key=val" |
| */ |
| static int equal_key(const char *e1, |
| const char *e2) |
| { |
| for ( ; e1 && (*e1) && e2 && (*e2) && (*e1 == *e2) ; e1++, e2++) |
| if (*e1 == '=') |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| |
| /** |
| * Helper function to register an environment variable |
| */ |
| static int registerenv(char * string, int can_overwrite) |
| { |
| int i; |
| char ** overwritten; |
| |
| /* The first time: allocate the environ table */ |
| if (! environ) |
| { |
| environ = malloc(SOS_MAX_ENVVARS * sizeof(char*)); |
| if (! environ) |
| return 0; |
| |
| memset(environ, 0x0, SOS_MAX_ENVVARS * sizeof(char*)); |
| } |
| |
| /* Walk the environment variables */ |
| overwritten = NULL; |
| for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++) |
| { |
| /* Reached end of list ? */ |
| if (! environ[i]) |
| break; |
| |
| /* Variable already present ? */ |
| if (equal_key(environ[i], string)) |
| overwritten = & environ[i]; |
| } |
| |
| if (overwritten) |
| { |
| /* Not allowed to overwrite it ! */ |
| if (! can_overwrite) |
| return -1; |
| |
| /* Overwrite allowed: do it now */ |
| free(*overwritten); |
| *overwritten = string; |
| return 0; |
| } |
| |
| /* Must add the variable */ |
| |
| /* No place left ? */ |
| if (i >= SOS_MAX_ENVVARS) |
| return -1; |
| |
| /* Ok, add it at the end */ |
| environ[i] = string; |
| return 0; |
| } |
| |
| |
| /** |
| * @return TRUE when the key of the "keyvalpair" pair is "key" |
| */ |
| static int key_is(char * keyvalpair, const char * key, |
| char ** /*out*/value) |
| { |
| for ( ; keyvalpair && *keyvalpair && key && *key ; keyvalpair ++, key ++) |
| if (*key != *keyvalpair) |
| break; |
| |
| if (value) |
| *value = keyvalpair + 1; |
| |
| return (keyvalpair && (*keyvalpair == '=') && key && (*key == '\0')); |
| } |
| |
| |
| int putenv(char * string) |
| { |
| char * str; |
| if (! string) |
| return -1; |
| |
| str = strdup(string); |
| if (! str) |
| return -1; |
| |
| return registerenv(string, TRUE); |
| } |
| |
| |
| int setenv(const char *name, const char *value, int overwrite) |
| { |
| char * str, * c; |
| size_t sz_name, sz_value; |
| |
| if (!name || !value) |
| return -1; |
| |
| /* Allocate space for the "name=value" string */ |
| sz_name = strlen(name); |
| sz_value = strlen(value); |
| str = malloc(sz_name + 1 + sz_value + 1); |
| if (! str) |
| return -1; |
| |
| /* sprintf(str, "%s=%s", name, value); */ |
| c = str; |
| *c = '\0'; |
| strzcpy(c, name, sz_name+1); c += sz_name; |
| strzcpy(c, "=", 2); c += 1; |
| strzcpy(c, value, sz_value+1); |
| |
| return registerenv(str, overwrite); |
| } |
| |
| |
| char *getenv(const char *name) |
| { |
| int i; |
| |
| if (! environ) |
| return NULL; |
| |
| for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++) |
| { |
| char *value; |
| |
| /* Reached end of list ? */ |
| if (! environ[i]) |
| return NULL; |
| |
| /* Variable already present ? */ |
| if (key_is(environ[i], name, & value)) |
| return value; |
| } |
| |
| return NULL; |
| } |
| |
| |
| void unsetenv(const char *name) |
| { |
| int i; |
| char ** entry, ** last_entry; |
| |
| if (! environ) |
| return; |
| |
| /* Walk the environment variables */ |
| entry = last_entry = NULL; |
| for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++) |
| { |
| /* Reached end of list ? */ |
| if (! environ[i]) |
| break; |
| |
| /* Variable already present ? */ |
| if (key_is(environ[i], name, NULL)) |
| entry = & environ[i]; |
| else if (entry) |
| last_entry = & environ[i]; |
| } |
| |
| /* Found nothing ! */ |
| if (! entry) |
| return; |
| |
| /* Found it: erase it... */ |
| free(*entry); |
| *entry = NULL; |
| |
| /* ... and replace it with the last entry */ |
| if (last_entry) |
| { |
| *entry = *last_entry; |
| *last_entry = NULL; |
| } |
| } |
| |
| |
| int clearenv(void) |
| { |
| int i; |
| |
| if (! environ) |
| return 0; |
| |
| for (i = 0 ; i < SOS_MAX_ENVVARS ; i ++) |
| { |
| if (! environ[i]) |
| break; |
| |
| free(environ[i]); |
| environ[i] = NULL; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
int munmap(void * start, size_t length) | int munmap(void * start, size_t length) |
{ | { |
| |