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.
This commit is contained in:
Michael LeMay 2015-08-10 08:34:02 -07:00
parent b0de416682
commit 3908253038
48 changed files with 3558 additions and 295 deletions

View file

@ -29,45 +29,15 @@
*/
#include <stdint.h>
#include "gdt.h"
#include "gdt-layout.h"
#include "helpers.h"
#include "prot-domains.h"
#include "segmentation.h"
#define NUM_DESC 3
#define GDT_IDX_NULL 0
#define GDT_IDX_CODE 1
#define GDT_IDX_DATA 2
/* All code in the x86 port of Contiki runs at ring (privilege) level 0 */
#define PRIV_LVL 0
/* Compute GDT selector from descriptor index and requested privilege level */
#define GDT_SEL(IDX, RPL) (((IDX) << 3) | (RPL))
#define GDT_SEL_NULL GDT_SEL(GDT_IDX_NULL, 0)
#define GDT_SEL_CODE GDT_SEL(GDT_IDX_CODE, PRIV_LVL)
#define GDT_SEL_DATA GDT_SEL(GDT_IDX_DATA, PRIV_LVL)
/* Each define here is for a specific flag in the descriptor. Refer to Intel
* Combined Manual (Intel 64 and IA-32 Architectures Software Developer's
* Manual), Vol. 3, Section 3.4.5 for a description of each flag.
*/
#define SEG_DESCTYPE(x) ((x) << 0x04) /* Descriptor type (0 for system, 1 for code/data) */
#define SEG_PRES(x) ((x) << 0x07) /* Present */
#define SEG_SAVL(x) ((x) << 0x0C) /* Available for system use */
#define SEG_LONG(x) ((x) << 0x0D) /* Long mode */
#define SEG_SIZE(x) ((x) << 0x0E) /* Size (0 for 16-bit, 1 for 32) */
#define SEG_GRAN(x) ((x) << 0x0F) /* Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB) */
#define SEG_PRIV(x) (((x) & 0x03) << 0x05) /* Set privilege level (0 - 3) */
#define SEG_DATA_RDWR 0x02 /* Read/Write */
#define SEG_CODE_EXRD 0x0A /* Execute/Read */
#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_CODE_EXRD
#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \
SEG_PRIV(0) | SEG_DATA_RDWR
#define GDT_MEM_PL0 (SEG_DESCTYPE_NSYS | SEG_GRAN_PAGE)
#define GDT_CODE_PL0 (GDT_MEM_PL0 | SEG_TYPE_CODE_EXRD)
#define GDT_DATA_PL0 (GDT_MEM_PL0 | SEG_TYPE_DATA_RDWR)
typedef struct gdtr
{
@ -75,41 +45,53 @@ typedef struct gdtr
uint32_t base;
} __attribute__((packed)) gdtr_t;
typedef uint64_t segment_desc_t;
/* From Intel Combined Manual, Vol. 3 , Section 3.5.1: The base addresses of
* the GDT should be aligned on an eight-byte boundary to yield the best
* processor performance.
*/
static segment_desc_t gdt[NUM_DESC] __attribute__ ((aligned (8)));
segment_desc_t __attribute__ ((aligned(8))) ATTR_BSS_GDT_START
gdt[GDT_NUM_FIXED_DESC];
static void
set_descriptor(unsigned int index, uint32_t base, uint32_t limit, uint16_t flag)
#define GDT_LEN \
((((uintptr_t)&_ebss_gdt_addr) - \
(uintptr_t)gdt)/sizeof(segment_desc_t))
/*---------------------------------------------------------------------------*/
static void ATTR_CODE_BOOT
set_descriptor(unsigned int index,
uint32_t base,
uint32_t len,
uint16_t flag)
{
segment_desc_t descriptor;
if (index >= NUM_DESC)
return;
if(GDT_LEN <= index) {
halt();
}
/* Create the high 32 bit segment */
descriptor = limit & 0x000F0000; /* set limit bits 19:16 */
descriptor |= (flag << 8) & 0x00F0FF00; /* set type, p, dpl, s, g, d/b, l and avl fields */
descriptor |= (base >> 16) & 0x000000FF; /* set base bits 23:16 */
descriptor |= base & 0xFF000000; /* set base bits 31:24 */
/* Shift by 32 to allow for low part of segment */
descriptor <<= 32;
/* Create the low 32 bit segment */
descriptor |= base << 16; /* set base bits 15:0 */
descriptor |= limit & 0x0000FFFF; /* set limit bits 15:0 */
segment_desc_init(&descriptor, base, len, flag);
/* Save descriptor into gdt */
gdt[index] = descriptor;
}
/*---------------------------------------------------------------------------*/
void
gdt_copy_desc_change_dpl(unsigned int dest_idx,
unsigned int src_idx,
unsigned dpl)
{
segment_desc_t desc;
if((GDT_LEN <= dest_idx) || (GDT_LEN <= src_idx)) {
halt();
}
/* This function initializes the Global Offset Table. For simplicity, the
desc = gdt[src_idx];
SEG_SET_FLAG(desc, DPL, dpl);
gdt[dest_idx] = desc;
}
/*---------------------------------------------------------------------------*/
/* This function initializes the Global Descriptor Table. For simplicity, the
* memory is organized following the flat model. Thus, memory appears to
* Contiki as a single continuous address space. Code, data, and stack
* are all contained in this address space (so called linear address space).
@ -120,29 +102,35 @@ gdt_init(void)
gdtr_t gdtr;
/* Initialize gdtr structure */
gdtr.limit = sizeof(segment_desc_t) * NUM_DESC - 1;
gdtr.limit = sizeof(segment_desc_t) * GDT_LEN - 1;
gdtr.base = (uint32_t) &gdt;
/* Initialize descriptors */
set_descriptor(GDT_IDX_NULL, 0, 0, 0);
set_descriptor(GDT_IDX_CODE, 0, 0x0FFFFF, GDT_CODE_PL0);
set_descriptor(GDT_IDX_DATA, 0, 0x0FFFFF, GDT_DATA_PL0);
set_descriptor(GDT_IDX_CODE_FLAT, 0, 0x100000, GDT_CODE_PL0);
set_descriptor(GDT_IDX_DATA_FLAT, 0, 0x100000, GDT_DATA_PL0);
/* Load GDTR register and update segment registers.
*
* CS register cannot be changed directly. For that reason, we do a far jump.
*/
__asm__ ("lgdt %[_gdtr_]\n\t"
"jmp %[_cs_], $1f\n\t"
"1:\n\t"
"mov %[_ds_], %%ds\n\t"
"mov %[_ds_], %%ss\n\t"
"mov %[_ds_], %%es\n\t"
"mov %[_ds_], %%fs\n\t"
"mov %[_ds_], %%gs\n\t"
:
: [_gdtr_] "m" (gdtr),
[_cs_] "i" (GDT_SEL_CODE),
[_ds_] "r" (GDT_SEL_DATA)
);
/* Load GDTR */
__asm__ __volatile__ ("lgdt %0" :: "m" (gdtr));
}
/*---------------------------------------------------------------------------*/
void
gdt_insert(unsigned int idx, segment_desc_t desc)
{
if(GDT_LEN <= idx) {
halt();
}
gdt[idx] = desc;
}
/*---------------------------------------------------------------------------*/
void
gdt_lookup(unsigned int idx, segment_desc_t *desc)
{
if((GDT_LEN <= idx) || (desc == NULL)) {
halt();
}
*desc = gdt[idx];
}
/*---------------------------------------------------------------------------*/