osd-contiki/cpu/x86/mm/prot-domains.h
Michael LeMay 3908253038 x86: Add support for (paging-based) protection domains
This patch implements a simple, lightweight form of protection domains
using a pluggable framework.  Currently, the following plugin is
available:

 - Flat memory model with paging.

The overall goal of a protection domain implementation within this
framework is to define a set of resources that should be accessible to
each protection domain and to prevent that protection domain from
accessing other resources.  The details of each implementation of
protection domains may differ substantially, but they should all be
guided by the principle of least privilege.  However, that idealized
principle is balanced against the practical objectives of limiting the
number of relatively time-consuming context switches and minimizing
changes to existing code.

For additional information, please refer to cpu/x86/mm/README.md.

This patch also causes the C compiler to be used as the default linker
and assembler.
2016-03-21 17:18:06 -07:00

276 lines
9.9 KiB
C

/*
* Copyright (C) 2015, Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CPU_X86_MM_PROT_DOMAINS_H_
#define CPU_X86_MM_PROT_DOMAINS_H_
#if !__ASSEMBLER__
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "helpers.h"
#endif
#define X86_CONF_PROT_DOMAINS__NONE 0
#define X86_CONF_PROT_DOMAINS__PAGING 1
/** Privilege level (ring) for exception handlers and other supervisory code */
#define PRIV_LVL_EXC 0
#if X86_CONF_PROT_DOMAINS != X86_CONF_PROT_DOMAINS__NONE
/** Privilege level for interrupt handlers */
#define PRIV_LVL_INT 2
/** Default privilege level */
#define PRIV_LVL_USER 3
#else
#define PRIV_LVL_INT PRIV_LVL_EXC
#define PRIV_LVL_USER PRIV_LVL_EXC
#endif
#define DOM_ID_kern 0
#define DOM_ID_app 1
/** I/O Privilege Level */
#define EFLAGS_IOPL(pl) ((pl) << 12)
/** Interrupt Enable Flag */
#define EFLAGS_IF (1u << 9)
#if !__ASSEMBLER__
/** Protection domain ID */
typedef uint32_t dom_id_t;
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__PAGING
#include "paging-prot-domains.h"
#endif
/* The following symbols are defined in the linker script */
/** Bounds for .text section */
extern uint32_t _stext_addr, _etext_addr;
#if X86_CONF_PROT_DOMAINS != X86_CONF_PROT_DOMAINS__NONE
/** Metadata that should not be DMA-accessible */
#define ATTR_BSS_META __attribute__((section(".meta_bss")))
/** Kernel-owned data */
#define ATTR_BSS_KERN __attribute__((section(".kern_bss")))
/** Code that should only be executable during bootup */
#define ATTR_CODE_BOOT __attribute__((section(".boot_text")))
/**
* Domain-defined metadata must be page-aligned, which is implemented by the
* linker script for variables with this attribute.
*/
#define ATTR_BSS_KERN_PAGE_ALIGNED \
__attribute__((section(".page_aligned_kern_bss")))
/** Bounds for .kern_data, .syscall_data, and .prot_dom_data sections */
extern uint32_t _sbss_kern_addr, _ebss_kern_addr;
/** End of .syscall_data section */
extern uint32_t _ebss_syscall_addr;
/** Bounds for other data sections */
extern uint32_t _sdata_addr, _edata_addr;
/**
* If set, this protection domain is already in the call stack and is not
* available for nested invocations.
*/
#define PROT_DOMAINS_FLAG_BUSY BIT(0)
/** If set, this protection domain requires port I/O access. */
#define PROT_DOMAINS_FLAG_PIO BIT(1)
/** If set, this protection domain is initialized. */
#define PROT_DOMAINS_FLAG_INITED BIT(2)
/**
* Data associated with each protection domain that should be fully accessible
* only to the kernel, with limited accesses and modifications permitted from
* other domains. Includes storage for system data structures.
*/
typedef struct dom_kern_data dom_kern_data_t;
extern volatile dom_kern_data_t prot_domains_kern_data[];
extern volatile dom_kern_data_t prot_domains_kern_data_end[];
#define PROT_DOMAINS_ACTUAL_CNT \
(prot_domains_kern_data_end - prot_domains_kern_data)
#define PROT_DOMAINS_GET_DOM_ID(dkd) \
((dom_id_t)((dkd) - prot_domains_kern_data))
void prot_domains_syscall_dispatcher(void);
/**
* Data associated with each protection domain that is owned by clients of that
* domain and used to identify the domain.
*/
struct dom_client_data {
dom_id_t dom_id;
} __attribute__((packed));
/** Allocate the client-owned protection domain data structure. */
#define PROT_DOMAINS_PDCS_NM(nm) _pdcs_##nm
#define PROT_DOMAINS_ALLOC(typ, nm) \
static dom_kern_data_t __attribute__((section(".prot_dom_bss"))) \
PROT_DOMAINS_PDCS_NM(nm); \
static typ ATTR_BSS_KERN nm
#define PROT_DOMAINS_INIT_ID(nm) \
(nm).dom_id = PROT_DOMAINS_GET_DOM_ID(&PROT_DOMAINS_PDCS_NM(nm))
/**
* Perform early initialization during boot stage 0 to prepare for boot stage 1
*/
void prot_domains_gdt_init() ATTR_CODE_BOOT;
/**
* Perform initialization during boot stage 1 to prepare for kernel launch
*/
void prot_domains_init();
void prot_domains_impl_init();
/* Return from cpu_boot_stage1 will invoke prot_domains_launch_kernel due to
* that return address being pushed on the stack by cpu_boot_stage0.
*/
#define prot_domains_leave_boot_stage1()
/* Return from main will invoke prot_domains_launch_app due to that return
* address being pushed on the stack by cpu_boot_stage0.
*/
#define prot_domains_leave_main()
void prot_domains_launch_kernel(void);
/* Whenever changing this, update syscalls-int-asm.S:prot_domains_launch_kernel
* to match:
*/
#define PROT_DOMAINS_INIT_RET_ADDR_CNT 2
void app_main(void);
#define prot_domains_launch_app app_main
#else
#define ATTR_BSS_META
#define ATTR_BSS_KERN
#define ATTR_CODE_BOOT
struct dom_client_data {
uintptr_t mmio; /**< MMIO range base address */
uintptr_t meta; /**< Domain-defined metadata base address */
};
/** Retrieve the MMIO base address for the specified protection domain. */
#define PROT_DOMAINS_MMIO(dcd) ((dcd).mmio)
/** Retrieve the metadata base address for the specified protection domain. */
#define PROT_DOMAINS_META(dcd) ((dcd).meta)
#define PROT_DOMAINS_ALLOC(typ, nm) static typ nm
#define PROT_DOMAINS_INIT_ID(nm)
#define prot_domains_gdt_init()
#define prot_domains_init()
int main(void);
#define prot_domains_leave_boot_stage1 main
#define prot_domains_leave_main ENABLE_IRQ(); app_main
#define PROT_DOMAINS_INIT_RET_ADDR_CNT 0
#endif
/**
* Protection domain data readable by the client. It is used to control
* execution, so it should be protected from modifications by clients.
* Otherwise, there is a risk that one client could modify one of these
* structures used by another client to issue a system call, which could then
* cause the latter client to perform an unintended system call.
*/
typedef struct dom_client_data dom_client_data_t;
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__NONE
#define prot_domains_reg(dcd, mmio_, mmio_sz, meta_, meta_sz, pio) \
(dcd)->mmio = (mmio_); \
(dcd)->meta = (meta_)
#else
/**
* \brief Register a protection domain, which involves creating the
* necessary system data structures for it.
*
* \param dcd Client-accessible domain information
* \param mmio Optional base address for per-domain memory-mapped IO region
* \param mmio_sz Size of MMIO region
* \param meta Optional base address for per-domain metadata
* \param meta_sz Size of metadata
* \param pio Set to true if protection domain requires port IO access
*/
void prot_domains_reg(dom_client_data_t *dcd,
uintptr_t mmio,
size_t mmio_sz,
uintptr_t meta,
size_t meta_sz,
bool pio);
#endif
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__NONE
#define prot_domains_lookup_meta_phys_base(drv) 0
#else
/** Lookup base physical address of metadata region for specified domain */
uintptr_t prot_domains_lookup_meta_phys_base(dom_client_data_t *drv);
#endif
#if X86_CONF_PROT_DOMAINS != X86_CONF_PROT_DOMAINS__PAGING
#define PROT_DOMAINS_META_OFF_TO_PHYS(off, meta_phys_base) \
((meta_phys_base) + (off))
#endif
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__NONE
#define PROT_DOMAINS_ENTER_ISR(...)
#define PROT_DOMAINS_LEAVE_ISR(...)
#else
#define PROT_DOMAINS_ENTER_ISR_COMMON(exc) \
".if !" #exc "\n\t" \
/* Save the current stack pointer into a callee-saved register. */ \
"mov %%esp, %%ebx\n\t" \
/* Pivot to the main stack of the interrupted context. */ \
/* Interrupts never have an error code, so the offset is always 44. */ \
/* No interrupt handlers use anything from the original interrupt stack, */ \
/* so there is no need to copy anything from it to the main stack. */ \
"mov 44(%%esp), %%esp\n\t" \
".endif\n\t"
#define PROT_DOMAINS_LEAVE_ISR_COMMON(exc) \
/* Restore the interrupt/exception stack pointer. */ \
".if !" #exc "\n\t" \
"mov %%ebx, %%esp\n\t" \
".endif\n\t"
#endif
#endif /* !__ASSEMBLER__ */
#endif /* CPU_X86_MM_PROT_DOMAINS_H_ */