Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,9 @@ if(EXISTS ${CMAKE_BINARY_DIR}/zephyr_modules.txt)
set(ZEPHYR_CURRENT_MODULE_DIR)
endif()

set(syscall_list_h ${CMAKE_CURRENT_BINARY_DIR}/include/generated/syscall_list.h)
set(syscalls_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/syscalls.json)
set(subsys_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/subsystems.json)
set(syscall_list_h ${CMAKE_CURRENT_BINARY_DIR}/include/generated/syscall_list.h)
set(syscalls_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/syscalls.json)
set(struct_tags_json ${CMAKE_CURRENT_BINARY_DIR}/misc/generated/struct_tags.json)

# The syscalls subdirs txt file is constructed by python containing a list of folders to use for
# dependency handling, including empty folders.
Expand Down Expand Up @@ -589,20 +589,22 @@ endforeach()
add_custom_command(
OUTPUT
${syscalls_json}
${subsys_json}
${struct_tags_json}
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/parse_syscalls.py
--include ${ZEPHYR_BASE}/include # Read files from this dir
--include ${ZEPHYR_BASE}/drivers # For net sockets
--include ${ZEPHYR_BASE}/subsys/net # More net sockets
${parse_syscalls_include_args} # Read files from these dirs also
--json-file ${syscalls_json} # Write this file
--subsystem-file ${subsys_json} # Write subsystem list to this file
--tag-struct-file ${struct_tags_json} # Write subsystem list to this file
DEPENDS ${syscalls_subdirs_trigger} ${PARSE_SYSCALLS_HEADER_DEPENDS}
)

add_custom_target(${SYSCALL_LIST_H_TARGET} DEPENDS ${syscall_list_h})
add_custom_target(${PARSE_SYSCALLS_TARGET}
DEPENDS ${syscalls_json} ${subsys_json})
DEPENDS ${syscalls_json} ${struct_tags_json})

# 64-bit systems do not require special handling of 64-bit system call
# parameters or return values, indicate this to the system call boilerplate
Expand Down Expand Up @@ -632,7 +634,7 @@ add_custom_command(OUTPUT include/generated/syscall_dispatch.c ${syscall_list_h}
)

# This is passed into all calls to the gen_kobject_list.py script.
set(gen_kobject_list_include_args --include ${subsys_json})
set(gen_kobject_list_include_args --include ${struct_tags_json})

set(DRV_VALIDATION ${PROJECT_BINARY_DIR}/include/generated/driver-validation.h)
add_custom_command(
Expand All @@ -646,7 +648,7 @@ add_custom_command(
DEPENDS
${ZEPHYR_BASE}/scripts/gen_kobject_list.py
${PARSE_SYSCALLS_TARGET}
${subsys_json}
${struct_tags_json}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_custom_target(${DRIVER_VALIDATION_H_TARGET} DEPENDS ${DRV_VALIDATION})
Expand Down
2 changes: 1 addition & 1 deletion drivers/modem/modem_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
extern "C" {
#endif

struct modem_socket {
__net_socket struct modem_socket {
sa_family_t family;
enum net_sock_type type;
enum net_ip_protocol ip_proto;
Expand Down
31 changes: 30 additions & 1 deletion include/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,27 @@ void k_object_access_all_grant(void *object);
__syscall void *k_object_alloc(enum k_objects otype);

#ifdef CONFIG_DYNAMIC_OBJECTS
/**
* Allocate memory and install as a generic kernel object
*
* This is a low-level function to allocate some memory, and register that
* allocated memory in the kernel object lookup tables with type K_OBJ_ANY.
* Initialization state and thread permissions will be cleared. The
* returned z_object's data value will be uninitialized.
*
* Most users will want to use k_object_alloc() instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stupid question, what are the objects not supported by k_object_alloc ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. futexes and sys_mutex, they live in user and not kernel memory
  2. thread stacks, as we do not have an aligned allocator
  3. net sockets (this patch) as they are a family of objects of no specific size

There is a switch statement in z_impl_k_object_alloc() specifically for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be better documented by the way, I'll send a separate PR to augment the doxygen.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks !

*
* Memory allocated will be drawn from the calling thread's reasource pool
* and may be freed later by passing the actual object pointer (found
* in the returned z_object's 'name' member) to k_object_free().
*
* @param size Size of the allocated object
* @return NULL on insufficient memory
* @return A pointer to the associated z_object that is installed in the
* kernel object tables
*/
struct z_object *z_dynamic_object_create(size_t size);

/**
* Free a kernel object previously allocated with k_object_alloc()
*
Expand All @@ -370,12 +391,20 @@ static inline void *z_impl_k_object_alloc(enum k_objects otype)

return NULL;
}

static inline struct z_object *z_dynamic_object_create(size_t size)
{
ARG_UNUSED(size);

return NULL;
}

/**
* @brief Free an object
*
* @param obj
*/
static inline void k_obj_free(void *obj)
static inline void k_object_free(void *obj)
{
ARG_UNUSED(obj);
}
Expand Down
2 changes: 1 addition & 1 deletion include/net/net_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ struct tls_context;
* If there is no such source address there, the packet cannot be sent
* anyway. This saves 12 bytes / context in IPv6.
*/
struct net_context {
__net_socket struct net_context {
/** User data.
*
* First member of the structure to let users either have user data
Expand Down
43 changes: 43 additions & 0 deletions include/net/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,44 @@ struct zsock_addrinfo {
char _ai_canonname[DNS_MAX_NAME_SIZE + 1];
};

/**
* @brief Obtain a file descriptor's associated net context
*
* With CONFIG_USERSPACE enabled, the kernel's object permission system
* must apply to socket file descriptors. When a socket is opened, by default
* only the caller has permission, access by other threads will fail unless
* they have been specifically granted permission.
*
* This is achieved by tagging data structure definitions that implement the
* underlying object associated with a network socket file descriptor with
* '__net_socket`. All pointers to instances of these will be known to the
* kernel as kernel objects with type K_OBJ_NET_SOCKET.
*
* This API is intended for threads that need to grant access to the object
* associated with a particular file descriptor to another thread. The
* returned pointer represents the underlying K_OBJ_NET_SOCKET and
* may be passed to APIs like k_object_access_grant().
*
* In a system like Linux which has the notion of threads running in processes
* in a shared virtual address space, this sort of management is unnecessary as
* the scope of file descriptors is implemented at the process level.
*
* However in Zephyr the file descriptor scope is global, and MPU-based systems
* are not able to implement a process-like model due to the lack of memory
* virtualization hardware. They use discrete object permissions and memory
* domains instead to define thread access scope.
*
* User threads will have no direct access to the returned object
* and will fault if they try to access its memory; the pointer can only be
* used to make permission assignment calls, which follow exactly the rules
* for other kernel objects like device drivers and IPC.
*
* @param sock file descriptor
* @return pointer to associated network socket object, or NULL if the
* file descriptor wasn't valid or the caller had no access permission
*/
__syscall void *zsock_get_context_object(int sock);

/**
* @brief Create a network socket
*
Expand All @@ -156,6 +194,11 @@ struct zsock_addrinfo {
* This function is also exposed as ``socket()``
* if :option:`CONFIG_NET_SOCKETS_POSIX_NAMES` is defined.
* @endrst
*
* If CONFIG_USERSPACE is enabled, the caller will be granted access to the
* context object associated with the returned file descriptor.
* @see zsock_get_context_object()
*
*/
__syscall int zsock_socket(int family, int type, int proto);

Expand Down
32 changes: 32 additions & 0 deletions include/syscall_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,38 @@ enum _obj_init_check {
_OBJ_INIT_ANY = 1
};

/**
* Return true if we are currently handling a system call from user mode
*
* Inside z_vrfy functions, we always know that we are handling
* a system call invoked from user context.
*
* However, some checks that are only relevant to user mode must
* instead be placed deeper within the implementation. This
* API is useful to conditionally make these checks.
*
* For performance reasons, whenever possible, checks should be placed
* in the relevant z_vrfy function since these are completely skipped
* when a syscall is invoked.
*
* This will return true only if we are handling a syscall for a
* user thread. If the system call was invoked from supervisor mode,
* or we are not handling a system call, this will return false.
*
* @return whether the current context is handling a syscall for a user
* mode thread
*/
static inline bool z_is_in_user_syscall(void)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be called from ISR @andrewboie ?

Copy link
Contributor Author

@andrewboie andrewboie Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it cannot, good catch. I'll add a check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be ok now.
You could optionally add a comment in the description that the function should return false if called in isr context

{
/* This gets set on entry to the syscall's generasted z_mrsh
* function and then cleared on exit. This code path is only
* encountered when a syscall is made from user mode, system
* calls from supervisor mode bypass everything directly to
* the implementation function.
*/
return !k_is_in_isr() && _current->syscall_frame != NULL;
}

/**
* Ensure a system object is a valid object of the expected type
*
Expand Down
10 changes: 8 additions & 2 deletions include/toolchain/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,17 @@
#define __syscall
#endif /* #ifndef ZTEST_UNITTEST */

/* Used as a sentinel by parse_syscalls.py to identify what API structs
* define driver subsystems.
/* Definitions for struct declaration tags. These are sentinel values used by
* parse_syscalls.py to gather a list of names of struct declarations that
* have these tags applied for them.
*/

/* Indicates this is a driver subsystem */
#define __subsystem

/* Indicates this is a network socket object */
#define __net_socket

#ifndef BUILD_ASSERT
/* Compile-time assertion that makes the build to fail.
* Common implementation swallows the message.
Expand Down
1 change: 1 addition & 0 deletions kernel/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ void z_setup_new_thread(struct k_thread *new_thread,
z_object_init(stack);
new_thread->stack_obj = stack;
new_thread->mem_domain_info.mem_domain = NULL;
new_thread->syscall_frame = NULL;

/* Any given thread has access to itself */
k_object_access_grant(new_thread, new_thread);
Expand Down
93 changes: 66 additions & 27 deletions kernel/userspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ const char *otype_to_str(enum k_objects otype)
/* otype-to-str.h is generated automatically during build by
* gen_kobject_list.py
*/
case K_OBJ_ANY:
ret = "generic";
break;
#include <otype-to-str.h>
default:
ret = "?";
Expand Down Expand Up @@ -252,51 +255,81 @@ static void thread_idx_free(uintptr_t tidx)
sys_bitfield_set_bit((mem_addr_t)_thread_idx_map, tidx);
}

void *z_impl_k_object_alloc(enum k_objects otype)
struct z_object *z_dynamic_object_create(size_t size)
{
struct dyn_obj *dyn_obj;
uintptr_t tidx;

/* Stacks are not supported, we don't yet have mem pool APIs
* to request memory that is aligned
*/
__ASSERT(otype > K_OBJ_ANY && otype < K_OBJ_LAST &&
otype != K_OBJ_THREAD_STACK_ELEMENT,
"bad object type requested");

dyn_obj = z_thread_malloc(sizeof(*dyn_obj) + obj_size_get(otype));
dyn_obj = z_thread_malloc(sizeof(*dyn_obj) + size);
if (dyn_obj == NULL) {
LOG_WRN("could not allocate kernel object");
LOG_ERR("could not allocate kernel object, out of memory");
return NULL;
}

dyn_obj->kobj.name = (char *)&dyn_obj->data;
dyn_obj->kobj.type = otype;
dyn_obj->kobj.flags = K_OBJ_FLAG_ALLOC;
dyn_obj->kobj.name = &dyn_obj->data;
dyn_obj->kobj.type = K_OBJ_ANY;
dyn_obj->kobj.flags = 0;
(void)memset(dyn_obj->kobj.perms, 0, CONFIG_MAX_THREAD_BYTES);

/* Need to grab a new thread index for k_thread */
if (otype == K_OBJ_THREAD) {
k_spinlock_key_t key = k_spin_lock(&lists_lock);

rb_insert(&obj_rb_tree, &dyn_obj->node);
sys_dlist_append(&obj_list, &dyn_obj->obj_list);
k_spin_unlock(&lists_lock, key);

return &dyn_obj->kobj;
}

void *z_impl_k_object_alloc(enum k_objects otype)
{
struct z_object *zo;
uintptr_t tidx;

if (otype <= K_OBJ_ANY || otype >= K_OBJ_LAST) {
LOG_ERR("bad object type %d requested", otype);
return NULL;
}

switch (otype) {
case K_OBJ_THREAD:
if (!thread_idx_alloc(&tidx)) {
k_free(dyn_obj);
LOG_ERR("out of free thread indexes");
return NULL;
}
break;
/* The following are currently not allowed at all */
case K_OBJ_FUTEX: /* Lives in user memory */
case K_OBJ_SYS_MUTEX: /* Lives in user memory */
case K_OBJ_THREAD_STACK_ELEMENT: /* No aligned allocator */
case K_OBJ_NET_SOCKET: /* Indeterminate size */
LOG_ERR("forbidden object type '%s' requested",
otype_to_str(otype));
return NULL;
default:
/* Remainder within bounds are permitted */
break;
}

dyn_obj->kobj.data.thread_id = tidx;
zo = z_dynamic_object_create(obj_size_get(otype));
if (zo == NULL) {
return NULL;
}
zo->type = otype;

if (otype == K_OBJ_THREAD) {
zo->data.thread_id = tidx;
}

/* The allocating thread implicitly gets permission on kernel objects
* that it allocates
*/
z_thread_perms_set(&dyn_obj->kobj, _current);
z_thread_perms_set(zo, _current);

k_spinlock_key_t key = k_spin_lock(&lists_lock);

rb_insert(&obj_rb_tree, &dyn_obj->node);
sys_dlist_append(&obj_list, &dyn_obj->obj_list);
k_spin_unlock(&lists_lock, key);
/* Activates reference counting logic for automatic disposal when
* all permissions have been revoked
*/
zo->flags |= K_OBJ_FLAG_ALLOC;

return dyn_obj->kobj.name;
return zo->name;
}

void k_object_free(void *obj)
Expand Down Expand Up @@ -508,6 +541,12 @@ void z_dump_object_error(int retval, void *obj, struct z_object *ko,
switch (retval) {
case -EBADF:
LOG_ERR("%p is not a valid %s", obj, otype_to_str(otype));
if (ko == NULL) {
LOG_ERR("address is not a known kernel object");
} else {
LOG_ERR("address is actually a %s",
otype_to_str(ko->type));
}
break;
case -EPERM:
dump_permission_error(ko);
Expand Down Expand Up @@ -785,7 +824,7 @@ static uintptr_t handler_bad_syscall(uintptr_t bad_id, uintptr_t arg2,
void *ssf)
{
LOG_ERR("Bad system call id %" PRIuPTR " invoked", bad_id);
arch_syscall_oops(_current->syscall_frame);
arch_syscall_oops(ssf);
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}

Expand All @@ -794,7 +833,7 @@ static uintptr_t handler_no_syscall(uintptr_t arg1, uintptr_t arg2,
uintptr_t arg5, uintptr_t arg6, void *ssf)
{
LOG_ERR("Unimplemented system call");
arch_syscall_oops(_current->syscall_frame);
arch_syscall_oops(ssf);
CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
}

Expand Down
Loading