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

196 lines
9.6 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_MULTI_SEGMENT_H_
#define CPU_X86_MM_MULTI_SEGMENT_H_
#include <stdint.h>
#include <stdlib.h>
#include "helpers.h"
#include "ldt-layout.h"
#ifdef __clang__
#define __SEG_FS
#define __seg_fs __attribute__((address_space(257)))
#define __SEG_GS
#define __seg_gs __attribute__((address_space(256)))
#endif
#ifdef __SEG_FS
#define ATTR_MMIO_ADDR_SPACE __seg_fs
#define ATTR_KERN_ADDR_SPACE __seg_fs
#else
#define ATTR_KERN_ADDR_SPACE
#endif
#ifdef __SEG_GS
#define ATTR_META_ADDR_SPACE __seg_gs
#endif
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);
void multi_segment_launch_kernel(void);
#define MULTI_SEGMENT_ENTER_ISR(exc) \
"mov $" EXP_STRINGIFY(GDT_SEL_DATA) ", %%eax\n\t" \
/* Refresh DS and ES in case the userspace code corrupted them. */ \
"mov %%eax, %%ds\n\t" \
"mov %%eax, %%es\n\t" \
/* Refresh SEG_KERN. */ \
"mov $" EXP_STRINGIFY(LDT_SEL_KERN) ", %%eax\n\t" \
"mov %%eax, %%" SEG_KERN "s\n\t" \
".if " #exc "\n\t" \
/* It is possible that a routine performing MMIO is being interrupted. */ \
/* Thus, it is necessary to save and restore the MMIO segment register */ \
/* (in a callee-saved register). */ \
"mov %%" SEG_MMIO "s, %%ebp\n\t" \
"mov $" EXP_STRINGIFY(GDT_SEL_DATA_KERN_EXC) ", %%eax\n\t" \
"mov %%eax, %%" SEG_KERN "s\n\t" \
".endif\n\t"
#define MULTI_SEGMENT_LEAVE_ISR(exc) \
".if " #exc "\n\t" \
"mov %%ebp, %%" SEG_MMIO "s\n\t" \
".endif\n\t"
/**
* The MMIO region is tightly bounded within a segment, so its base offset is
* always 0.
*/
#define PROT_DOMAINS_MMIO(dcd) 0
/**
* The metadata region is tightly bounded within a segment, so its base offset
* is always 0.
*/
#define PROT_DOMAINS_META(dcd) 0
#define SEG_MMIO "f" /**< For MMIO accesses, when enabled. */
#define SEG_KERN "f" /**< For kernel data accesses */
#define SEG_META "g" /**< For metadata accesses */
#define _SEG_READL(seg, dst, src) \
__asm__ __volatile__ ( \
"movl %%" seg "s:%[src_], %[dst_]" : [dst_]"=r"(dst) : [src_]"m"(src))
#define _SEG_READW(seg, dst, src) \
__asm__ __volatile__ ( \
"movw %%" seg "s:%[src_], %[dst_]" : [dst_]"=r"(dst) : [src_]"m"(src))
#define _SEG_READB(seg, dst, src) \
__asm__ __volatile__ ( \
"movb %%" seg "s:%[src_], %[dst_]" : [dst_]"=q"(dst) : [src_]"m"(src))
#define _SEG_WRITEL(seg, dst, src) \
__asm__ __volatile__ ( \
"movl %[src_], %%" seg "s:%[dst_]" \
: [dst_]"=m"(dst) : [src_]"r"((uint32_t)(src)))
#define _SEG_WRITEW(seg, dst, src) \
__asm__ __volatile__ ( \
"movw %[src_], %%" seg "s:%[dst_]" \
: [dst_]"=m"(dst) : [src_]"r"((uint16_t)(src)))
#define _SEG_WRITEB(seg, dst, src) \
__asm__ __volatile__ ( \
"movb %[src_], %%" seg "s:%[dst_]" \
: [dst_]"=m"(dst) : [src_]"q"((uint8_t)(src)))
#ifndef __SEG_FS
#define MMIO_READL(dst, src) _SEG_READL(SEG_MMIO, dst, src)
#define MMIO_READW(dst, src) _SEG_READW(SEG_MMIO, dst, src)
#define MMIO_READB(dst, src) _SEG_READB(SEG_MMIO, dst, src)
#define MMIO_WRITEL(dst, src) _SEG_WRITEL(SEG_MMIO, dst, src)
#define MMIO_WRITEW(dst, src) _SEG_WRITEW(SEG_MMIO, dst, src)
#define MMIO_WRITEB(dst, src) _SEG_WRITEB(SEG_MMIO, dst, src)
#define KERN_READL(dst, src) _SEG_READL(SEG_KERN, dst, src)
#define KERN_READW(dst, src) _SEG_READW(SEG_KERN, dst, src)
#define KERN_READB(dst, src) _SEG_READB(SEG_KERN, dst, src)
#define KERN_WRITEL(dst, src) _SEG_WRITEL(SEG_KERN, dst, src)
#define KERN_WRITEW(dst, src) _SEG_WRITEW(SEG_KERN, dst, src)
#define KERN_WRITEB(dst, src) _SEG_WRITEB(SEG_KERN, dst, src)
#endif
#ifndef __SEG_GS
#define META_READL(dst, src) _SEG_READL(SEG_META, dst, src)
#define META_READW(dst, src) _SEG_READW(SEG_META, dst, src)
#define META_READB(dst, src) _SEG_READB(SEG_META, dst, src)
#define META_WRITEL(dst, src) _SEG_WRITEL(SEG_META, dst, src)
#define META_WRITEW(dst, src) _SEG_WRITEW(SEG_META, dst, src)
#define META_WRITEB(dst, src) _SEG_WRITEB(SEG_META, dst, src)
#endif
#define MEMCPY_FROM_META(dst, src, sz) \
{ \
uintptr_t __dst = (uintptr_t)(dst); \
uintptr_t __src = (uintptr_t)(src); \
size_t __sz = (size_t)(sz); \
__asm__ __volatile__ ( \
"rep movsb %%" SEG_META "s:(%%esi), %%es:(%%edi)\n\t" \
: "+D"(__dst), "+S"(__src), "+c"(__sz)); \
}
#define MEMCPY_TO_META(dst, src, sz) \
{ \
uintptr_t __dst = (uintptr_t)(dst); \
uintptr_t __src = (uintptr_t)(src); \
size_t __sz = (size_t)(sz); \
__asm__ __volatile__ ( \
"push %%es\n\t" \
"push %%" SEG_META "s\n\t" \
"pop %%es\n\t" \
"rep movsb\n\t" \
"pop %%es\n\t" \
: "+D"(__dst), "+S"(__src), "+c"(__sz)); \
}
/** Compute physical address from offset into kernel data space */
#define KERN_DATA_OFF_TO_PHYS_ADDR(x) \
(((uintptr_t)&_sbss_kern_addr) + (uintptr_t)(x))
/** Compute physical address from offset into default data space */
#define DATA_OFF_TO_PHYS_ADDR(x) \
(((uintptr_t)&_sdata_addr) + (uintptr_t)(x))
/** Compute kernel data offset from physical address in kernel data space */
#define PHYS_ADDR_TO_KERN_DATA_OFF(x) \
(((uintptr_t)(x)) - (uintptr_t)&_sbss_kern_addr)
/**
* In multi-segment protection domain implementations, it is sufficient to just
* compare incoming pointers against the frame pointer. All incoming pointers
* are dereferenced in the main data segment, which only maps the stacks and
* the shared data section. Since the shared data section is at a higher
* address range than the stacks, the frame pointer check is sufficient.
*/
#define PROT_DOMAINS_CHECK_INCOMING_PTR PROT_DOMAINS_CHECK_INCOMING_PTR_EBP
void prot_domains_enable_mmio(void);
void prot_domains_disable_mmio(void);
#endif /* CPU_X86_MM_MULTI_SEGMENT_H_ */