e0aefd11d9
This patch extends the protection domain framework with a third plugin that is a hybrid of the previous two. The hardware task switching mechanism has a strictly-defined format for TSS data structures that causes more space to be consumed than would otherwise be required. This patch defines a smaller data structure that is allocated for each protection domain, only requiring 32 bytes instead of 128 bytes. It uses the same multi-segment memory layout as the TSS-based plugin and leaves paging disabled. However, it uses a similar mechanism as the paging plugin to perform system call dispatches and returns. For additional information, please refer to cpu/x86/mm/README.md.
248 lines
8.9 KiB
C
248 lines
8.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.
|
|
*/
|
|
|
|
#include "gdt.h"
|
|
#include "helpers.h"
|
|
#include "prot-domains.h"
|
|
#include "segmentation.h"
|
|
#include "stacks.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static uint32_t
|
|
segment_desc_compute_base(segment_desc_t desc)
|
|
{
|
|
return (desc.base_hi << 24) | (desc.base_mid << 16) | desc.base_lo;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
prot_domains_reg_multi_seg(volatile struct dom_kern_data ATTR_KERN_ADDR_SPACE *dkd,
|
|
uintptr_t mmio, size_t mmio_sz,
|
|
uintptr_t meta, size_t meta_sz)
|
|
{
|
|
segment_desc_t desc;
|
|
dom_id_t dom_id = PROT_DOMAINS_GET_DOM_ID(dkd);
|
|
uint32_t kern_data_len;
|
|
uint32_t tmp;
|
|
|
|
if((dkd < prot_domains_kern_data) ||
|
|
(prot_domains_kern_data_end <= dkd) ||
|
|
(((((uintptr_t)dkd) - (uintptr_t)prot_domains_kern_data) %
|
|
sizeof(dom_kern_data_t)) != 0)) {
|
|
halt();
|
|
}
|
|
|
|
KERN_READL(tmp, dkd->ldt[DT_SEL_GET_IDX(LDT_SEL_KERN)].raw_hi);
|
|
if(tmp != 0) {
|
|
/* This PDCS was previously initialized, which is disallowed. */
|
|
halt();
|
|
}
|
|
|
|
/* Initialize descriptors */
|
|
|
|
if(dom_id == DOM_ID_kern) {
|
|
kern_data_len = (uint32_t)&_ebss_kern_addr;
|
|
} else {
|
|
/* Non-kernel protection domains do not need to access the protection
|
|
* domain control structures, and they may contain saved register values
|
|
* that are private to each domain.
|
|
*/
|
|
kern_data_len = (uint32_t)&_ebss_syscall_addr;
|
|
}
|
|
kern_data_len -= (uint32_t)&_sbss_kern_addr;
|
|
|
|
segment_desc_init(&desc, (uint32_t)&_sbss_kern_addr, kern_data_len,
|
|
/* Every protection domain requires at least read-only access to kernel
|
|
data to read dom_client_data structures and to support the system call
|
|
dispatcher, if applicable. Only the kernel protection domain is granted
|
|
read/write access to the kernel data. */
|
|
((dom_id == DOM_ID_kern) ?
|
|
SEG_TYPE_DATA_RDWR :
|
|
SEG_TYPE_DATA_RDONLY) |
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) |
|
|
SEG_GRAN_BYTE | SEG_DESCTYPE_NSYS);
|
|
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_KERN].raw_lo, desc.raw_lo);
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_KERN].raw_hi, desc.raw_hi);
|
|
|
|
if(mmio_sz != 0) {
|
|
if(SEG_MAX_BYTE_GRAN_LEN < mmio_sz) {
|
|
halt();
|
|
}
|
|
|
|
segment_desc_init(&desc, mmio, mmio_sz,
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS | SEG_TYPE_DATA_RDWR);
|
|
} else {
|
|
desc.raw = SEG_DESC_NOT_PRESENT;
|
|
}
|
|
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_MMIO].raw_lo, desc.raw_lo);
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_MMIO].raw_hi, desc.raw_hi);
|
|
|
|
if(meta_sz != 0) {
|
|
if(SEG_MAX_BYTE_GRAN_LEN < meta_sz) {
|
|
halt();
|
|
}
|
|
|
|
segment_desc_init(&desc, meta, meta_sz,
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS | SEG_TYPE_DATA_RDWR);
|
|
} else {
|
|
desc.raw = SEG_DESC_NOT_PRESENT;
|
|
}
|
|
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_META].raw_lo, desc.raw_lo);
|
|
KERN_WRITEL(dkd->ldt[LDT_IDX_META].raw_hi, desc.raw_hi);
|
|
|
|
segment_desc_init(&desc,
|
|
KERN_DATA_OFF_TO_PHYS_ADDR(dkd->ldt),
|
|
sizeof(dkd->ldt),
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_SYS | SEG_TYPE_LDT);
|
|
gdt_insert(GDT_IDX_LDT(dom_id), desc);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
prot_domains_gdt_init()
|
|
{
|
|
int i;
|
|
segment_desc_t desc;
|
|
|
|
segment_desc_init(&desc,
|
|
(uint32_t)&_stext_addr,
|
|
((uint32_t)&_etext_addr) - (uint32_t)&_stext_addr,
|
|
SEG_FLAG(DPL, PRIV_LVL_EXC) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS |
|
|
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__SWSEG
|
|
/* The general protection fault handler requires read access to CS */
|
|
SEG_TYPE_CODE_EXRD
|
|
#else
|
|
SEG_TYPE_CODE_EX
|
|
#endif
|
|
);
|
|
gdt_insert_boot(GDT_IDX_CODE_EXC, desc);
|
|
|
|
segment_desc_init(&desc,
|
|
(uint32_t)&_sdata_addr,
|
|
((uint32_t)&_edata_addr) - (uint32_t)&_sdata_addr,
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS | SEG_TYPE_DATA_RDWR);
|
|
gdt_insert_boot(GDT_IDX_DATA, desc);
|
|
|
|
segment_desc_init(&desc,
|
|
(uint32_t)&_sbss_kern_addr,
|
|
((uint32_t)&_ebss_kern_addr) -
|
|
(uint32_t)&_sbss_kern_addr,
|
|
SEG_FLAG(DPL, PRIV_LVL_EXC) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS | SEG_TYPE_DATA_RDWR);
|
|
gdt_insert_boot(GDT_IDX_DATA_KERN_EXC, desc);
|
|
|
|
segment_desc_init(&desc,
|
|
(uint32_t)DATA_OFF_TO_PHYS_ADDR(stacks_main),
|
|
STACKS_SIZE_MAIN,
|
|
SEG_FLAG(DPL, PRIV_LVL_USER) | SEG_GRAN_BYTE |
|
|
SEG_DESCTYPE_NSYS | SEG_TYPE_DATA_RDWR);
|
|
gdt_insert_boot(GDT_IDX_STK, desc);
|
|
|
|
segment_desc_set_limit(&desc, STACKS_SIZE_MAIN + STACKS_SIZE_INT);
|
|
SEG_SET_FLAG(desc, DPL, PRIV_LVL_INT);
|
|
gdt_insert_boot(GDT_IDX_STK_INT, desc);
|
|
|
|
segment_desc_set_limit(&desc,
|
|
STACKS_SIZE_MAIN +
|
|
STACKS_SIZE_INT +
|
|
STACKS_SIZE_EXC);
|
|
SEG_SET_FLAG(desc, DPL, PRIV_LVL_EXC);
|
|
gdt_insert_boot(GDT_IDX_STK_EXC, desc);
|
|
|
|
/* Not all domains will necessarily be initialized, so this initially marks
|
|
* all per-domain descriptors not-present.
|
|
*/
|
|
desc.raw = SEG_DESC_NOT_PRESENT;
|
|
for(i = 0; i < PROT_DOMAINS_ACTUAL_CNT; i++) {
|
|
#if X86_CONF_PROT_DOMAINS == X86_CONF_PROT_DOMAINS__TSS
|
|
gdt_insert_boot(GDT_IDX_TSS(i), desc);
|
|
#endif
|
|
gdt_insert_boot(GDT_IDX_LDT(i), desc);
|
|
}
|
|
|
|
__asm__ __volatile__ (
|
|
"mov %[_default_data_], %%ds\n\t"
|
|
"mov %[_default_data_], %%es\n\t"
|
|
"mov %[_kern_data_], %%" SEG_KERN "s\n\t"
|
|
:
|
|
: [_default_data_] "r"(GDT_SEL_DATA),
|
|
[_kern_data_] "r"(GDT_SEL_DATA_KERN_EXC));
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
multi_segment_launch_kernel(void)
|
|
{
|
|
/* Update segment registers. */
|
|
__asm__ __volatile__ (
|
|
"mov %[_data_seg_], %%ds\n\t"
|
|
"mov %[_data_seg_], %%es\n\t"
|
|
"mov %[_kern_seg_], %%" SEG_KERN "s\n\t"
|
|
"mov %[_data_seg_], %%" SEG_META "s\n\t"
|
|
:
|
|
: [_data_seg_] "r" (GDT_SEL_DATA),
|
|
[_kern_seg_] "r" (LDT_SEL_KERN)
|
|
);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
prot_domains_enable_mmio(void)
|
|
{
|
|
__asm__ __volatile__ ("mov %0, %%" SEG_MMIO "s" :: "r" (LDT_SEL_MMIO));
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
prot_domains_disable_mmio(void)
|
|
{
|
|
__asm__ __volatile__ ("mov %0, %%" SEG_KERN "s" :: "r" (LDT_SEL_KERN));
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
uintptr_t
|
|
prot_domains_lookup_meta_phys_base(dom_client_data_t ATTR_KERN_ADDR_SPACE *drv)
|
|
{
|
|
dom_id_t dom_id;
|
|
segment_desc_t desc;
|
|
volatile dom_kern_data_t ATTR_KERN_ADDR_SPACE *dkd;
|
|
|
|
KERN_READL(dom_id, drv->dom_id);
|
|
|
|
dkd = prot_domains_kern_data + dom_id;
|
|
|
|
KERN_READL(desc.raw_lo, dkd->ldt[DT_SEL_GET_IDX(LDT_SEL_META)].raw_lo);
|
|
KERN_READL(desc.raw_hi, dkd->ldt[DT_SEL_GET_IDX(LDT_SEL_META)].raw_hi);
|
|
|
|
return segment_desc_compute_base(desc);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|