zephyriot / zep-jira14

0 stars 0 forks source link

define set of architecture-specific memory protection APIs #2032

Open nashif opened 7 years ago

nashif commented 7 years ago

Reported by Andrew Boie:

There will necessarily be a bunch of architecture specific code to abstract away the details of whatever MMU or MPU is in use.

Determine what these APIs need to be. The core kernel will call into these APIs to implement memory protection policy.

These will be private APIs, only for use by the kernel to call into arch/ code.

Depending on how this shapes up, it may be better to define different APIs depending on whether the system has an MPU or an MMU.

(Imported from Jira ZEP-2194)

nashif commented 7 years ago

by Andrew Boie:

I am starting to think that we might use the currently internal arm_core_mpu_configure() as a model.

The only reprogramming of memory regions at runtime will be when we context switch threads.

For the case where we are doing full thread protection: we have 5 memory regions to worry about:

Thread stack

Thread region 1

Thread region 2

Thread region 3

Thread region 4

For just simple stack guarding, we just have 1

Thread stack guard region

Even with an MMU, from the arch-neutral kernel perspective I like the idea of "intents". So our generic API could be something like:

void k_mem_region_configure(struct k_thread thread, enum k_mem_region_type type, void base, size_t size);

With the types corresponding to the cases above. This would configure the set of regions for that thread. The actually reprogramming of the MPU would be in arch code, specifically __swap.

nashif commented 7 years ago

by Chunlin Han:

Hi Andrew,

I think to add a specific region to a thread could be done via memory domain APIs.

For arch_specific we might only need something like:

mem_protection_arch_init()
{
        /* e.g. for ARM, to disable all MPU regions first. */
        mem_protection_arch_init_hw();
        /* e.g. for ARM, load some default regions to MPU */
        mem_protection_arch_set_default_regions();
        /* e.g. for ARM, enable MPU here */
        mem_protection_arch_enable();
}

Maybe I'm wrong, since I'm only familiar with ARM Cortex-M arch.... :D

nashif commented 7 years ago

by Mark Linkmeyer:

Chunlin Han , is this story planned to be implemented in time for 1.9? If so, will you please change its status from New to "To Do"? This will indicate it's not in planning anymore and it's believed (with high confidence) to be feasible to get done in 1.9. Thx.

nashif commented 7 years ago

by Mark Linkmeyer:

Hi Chunlin Han . In efforts to understand where stories are within the development lifecycle (New --> To Do --> In Progress, ...) I'm wondering if you're actually working on this story now since the Feature Merge Window is planned to close in two weeks. If you're working on it, can you please update the status to In Progress. If not, we'll know it's in your queue to be done for 1.9. :-) Thanks!!

nashif commented 7 years ago

by Chunlin Han:

Currently, it seems we only need the following arch specific memory protection APIs:

/ configure thread's memory domain during context switch / void configure_mpu_mem_domain(struct k_thread thread); void configure_mmu_mem_domain(struct k_thread thread);

/ configure thread's stack permission during context switch / void configure_mpu_stack(struct k_thread thread); void configure_mmu_stack(struct k_thread thread);

But since three are lots of works haven't been done for memory protection, three might be new requrement that needs new arch specific APIs to support it. So, I would suggest move this story out from 1.9.

nashif commented 7 years ago

by Andrew Boie:

To support userspace we are going to need each arch to implement a few functions:

/**
 * Perform a one-way transition from supervisor to kernel mode.
 * CPU will drop to unprivileged mode and transition to _thread_entry with
 * supplied arguments.
 */
extern FUNC_NORETURN
void _arch_drop_to_user_mode(_thread_entry_t user_entry, void *p1, void *p2,
                 void *p3);

/* Indicate whether we are currently running in user mode
 *
 * @return nonzero if the CPU is currently running with user permissions
 */
extern int _arch_is_user_context(void);
nashif commented 7 years ago

by Chunlin Han:

Hi Andrew Boie , I am curious why your design of _arch_drop_to_user_mode() transits thread's control to another _thread_entry after dropping to unprivileged mode? Why not just keep thread in its original _thread_entry after dropping to unprivileged mode?

nashif commented 7 years ago

by Andrew Boie:

Good question! It's because we want, eventually, to support SMEP (Supervisor Mode Execution Protection) on x86. In this configuration, supervisor mode is not a superset of user mode, they are completely disjoint. So code that runs in supervisor context, and code that runs in user context, have to live in completely separate memory pages, and that means that an API which does a privilege change and then returns to the caller won't work.

Many, many other things need to happen before something like SMEP is workable, but I don't want any of the memory protection design to interfere with it.

In addition to dropping to user mode via the above API, it will also be possible to pass K_USER to the k_thread_create() options to start a thread directly in user mode.

nashif commented 7 years ago

by Chunlin Han:

I see. Thanks for your explanation.

nashif commented 7 years ago

by Andrew Boie:

This is what I'm currently working with:

/**
 * Perform a one-way transition from supervisor to kernel mode.
 *
 * Implementations of this function must do the following:
 * - Reset the thread's stack pointer to a suitable initial value. We do not
 *   need any prior context since this is a one-way operation.
 * - Set up any kernel stack region for the CPU to use during privilege
 *   elevation
 * - Put the CPU in whatever its equivalent of user mode is
 * - Transfer execution to _new_thread() passing along all the supplied
 *   arguments, in user mode.
 *
 * @param Entry point to start executing as a user thread
 * @param p1 1st parameter to user thread
 * @param p2 2nd parameter to user thread
 * @param p3 3rd parameter to user thread
 */
extern FUNC_NORETURN
void _arch_user_mode_enter(_thread_entry_t user_entry, void *p1, void *p2,
               void *p3);

/* Indicate whether we are currently running in user mode
 *
 * @return nonzero if the CPU is currently running with user permissions
 */
extern int _arch_is_user_context(void);

/* Interfaces for invoking system calls. Actual implementations should be
 * pulled in by arch/cpu.h
 */
static inline u32_t _arch_syscall_invoke5(u32_t call_id, u32_t arg1, u32_t arg2,
                      u32_t arg3, u32_t arg4, u32_t arg5);

static inline u32_t _arch_syscall_invoke4(u32_t call_id, u32_t arg1, u32_t arg2,
                      u32_t arg3, u32_t arg4);

static inline u32_t _arch_syscall_invoke3(u32_t call_id, u32_t arg1, u32_t arg2,
                      u32_t arg3);

static inline u32_t _arch_syscall_invoke2(u32_t call_id, u32_t arg1,
                      u32_t arg2);

static inline u32_t _arch_syscall_invoke1(u32_t call_id, u32_t arg1);

static inline u32_t _arch_syscall_invoke0(u32_t call_id);
nashif commented 7 years ago

by Andrew Boie:

one more:

/**
 * @brief Check memory region permissions
 *
 * Given a memory region, return whether the current memory management hardware
 * configuration would allow a user thread to read/write that region. Used by
 * system calls to validate buffers coming in from userspace.
 *
 * @param addr start address of the buffer
 * @param size the size of the buffer
 * @param write If nonzero, additionally check if the area is writable.
 *    Otherwise, just check if the memory can be read.
 *
 * @return nonzero if the permissions of the entire buffer match what is
 * expected.
 */
extern void _arch_buffer_validate(void *addr, size_t size, int write);
nashif commented 7 years ago

by Andrew Boie:

the interfaces I discussed earlier are all merged now.

I think all that remains to be specified are the kernel <-> arch interfaces to support protection domains.

nashif commented 7 years ago

by Andrew Boie:

I've sent a patch with the arch interfaces needed for memory domains:

https://github.com/zephyrproject-rtos/zephyr/pull/1471