nes-proj/drivers/cpu/x86/mm/multi-segment.c

249 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);
}
/*---------------------------------------------------------------------------*/