initial import

internal commit: 14ac2bc2299032fa6714d1fefa7cf0987b3e3085

Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Eddie Dong
2018-03-07 20:57:14 +08:00
committed by lijinxia
parent bd31b1c53e
commit 7a3a539b17
156 changed files with 41265 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
Trusty on ACRN
Overview
Trusty Architecture
Trusty specific Hypercalls
Trusty Boot flow
EPT Hierarchy
********
Overview
********
Trusty is a set of software components supporting a Trusted Execution Environment (TEE).
Trusty consists of:
1. An operating system (the Trusty OS) that runs on a processor intended to provide a TEE
2. Drivers for the Android kernel (Linux) to facilitate communication with applications
running under the Trusty OS
3. A set of libraries for Android/Linux systems software to facilitate communication with
trusted applications executed within the Trusty OS using the kernel drivers
LK (Little Kernel) is a tiny operating system suited for small embedded devices, bootloaders,
and other environments where OS primitives like threads, mutexes, and timers are needed, but
there's a desire to keep things small and lightweight. LK has been chosen as the Trusty OS kernel.
*******************
Trusty Architecture
*******************
+---------------------------+
|VMn |
| ...... |
+------------+ +---------------------------+ |
|VM0 | |VM1 | |
| | | +--------+ +--------+ | |
| | | | | | | | |
| SOS | | | Normal | | Secure | | |
| | | | World | | World | |-+
| | | | | | | |
| | | +--------+ +--------+ |
+------------+ +---------------------------+
+-------------------------------------------+
| ACRN Hypervisor |
+-------------------------------------------+
+-------------------------------------------+
| HW |
+-------------------------------------------+
Note: Trusty OS is running in Secure World in the architecture above.
**************************
Trusty specific Hypercalls
**************************
1. HC_LAUNCH_TRUSTY
->This Hypercall is used by UOSloader to request ACRN to launch Trusty.
->The Trusty memory region range, entry point must be specified.
->Hypervisor needs to save current vCPU contexts (Normal World).
2. HC_WORLD_SWITCH
->Simulate ARM SMC (Secure Monitor Call, or SMC) instruction to do world switch.
->Hypervisor needs to save current world vCPU contexts, and load next world vCPU contexts.
->Update rdi, rsi, rdx, rbx to next world vCPU contexts.
API
---
1. hcall_launch_trusty(vm_t *vm);
2. hcall_world_switch(vm_t *vm);
****************
Trusty Boot flow
****************
Per design, AOSloader will trigger boot of Trusty. So the boot flow will be:
AOSloader --> ACRN --> Trusty --> ACRN --> AOSloader
Detail:
1. UOSloader
1.1 load and verify trusty image from virtual disk.
1.2 allocate runtime memory for trusty.
1.3 do ELF relocation of trusty image and get entry address.
1.4 call HC_LAUNCH_TRUSTY with trusty memory base and entry address.
2. ACRN(HC_LAUNCH_TRUSTY)
2.1 save World context for Normal World.
2.2 init World context for Secure World(RIP, RSP, EPT, etc.).
2.3 resume to Secure World.
3. Trusty
3.1 booting up
3.2 call HC_WORLD_SWITCH to switch back to Normal World if boot completed.
4. ACRN(HC_WORLD_SWITCH)
4.1 save World context for the World which caused this vmexit(Secure World)
4.2 restore World context for next World(Normal World(UOSloader))
4.3 resume to next World(UOSloader)
5. UOSloader
5.1 continue to boot.
*************
EPT Hierarchy
*************
Per Trusty design, Trusty can access Normal World's memory, but Normal World cannot
access Secure World's memory. Hence it means Secure World EPTP page table hierarchy
must contain normal world GPA address space, while Trusty world's GPA address space
must be removed from the Normal world EPTP page table hierarchy.
Design:
Put Secure World's GPA to very high position: 511G-512G. The PML4/PDPT for Trusty
World are separated from Normal World. PD/PT for low memory (<511G) are shared in
both Trusty World's EPT and Normal World's EPT. PD/PT for high memory (>=511G) are
valid for Trusty World's EPT only.
Benefit:
This design will benefit the EPT changes of Normal World. There are requirement to
modify Normal World's EPT during runtime such as memory increasing, attribute
change, etc. If such behavior happened, only PD and PT for Normal World need to
be updated.
ABSTRACT EPT hierarchy for 2 Worlds:
==================================================================== ==================================================
: Normal World : : Secure World :
: PML4 : : PML4 :
: +--------+ : : +--------+ :
: | | : : | | :
: | | : : PD | | :
: | | : : +-------+ | | :
: | | : : | | | | :
: | 0-512G |--+ : : | | +--| 0-512G | :
:EPTP -->+--------+ | : : | | | +--------+<-- EPTP :
: | PDPT : : | | PDPT | :
: | +--------+ : : | | +--------+ | :
: | | >=511G |---> Not present : : +-------+<--| >=511G | | :
: | |________| : : |________| | :
: | | | : : | | | :
: | | <511G |->+<----------------------------:--------:--------------| <511G | | :
: | | | | : : | | | :
: +-->+--------+ | PD PT : : +--------+<-+ :
: | ... ... : ==================================================
: | +-------+ +-------+ :
: | +-------+| +-------+| :
: | | || | || :
: | | || | || :
: | | PDE |--+ | || :
: | | || | | || :
: | | |+ | | |+ :
: +-->+-------+ +-->+-------+ :
: :
====================================================================
API
----
/*
Create Secure World EPT hierarchy, construct new PML4/PDPT, reuse PD/PT parse from
vm->arch_vm->ept
Parameters:
vm: VM with 2 Worlds
gpa: original gpa allocated from vSBL
size: LK size(16M by default)
rebase_offset: rebase the gpa to offset xxx(511G_OFFSET)
*/
int create_secure_world_ept(vm_t *vm, uint64_t gpa, uint64_t size, uint64_t rebase_offset)

35
hypervisor/MAINTAINERS Normal file
View File

@@ -0,0 +1,35 @@
ACRN Hypervisor Maintainers
===========================
This file provides information about the primary maintainers for
ACRN Hypervisor Maintainers.
In general, you should not privately email the maintainer. You should
email the acrn-dev list, but you can also Cc the maintainer.
Descriptions of section entries:
L: Mailing list that is relevant to this area (default is acrn-dev)
Patches and questions should be sent to the email list.
M: Cc address for patches and questions (ie, the package maintainer)
W: Web-page with status/info
T: SCM tree type and location. Type is one of: git, svn.
S: Status, one of the following:
Supported: Someone is actually paid to look after this.
Maintained: Someone actually looks after it.
Odd Fixes: It has a maintainer but they don't have time to do
much other than throw the odd patch in. See below.
Orphan: No current maintainer [but maybe you could take the
role as you write your new code].
Obsolete: Old code. Something tagged obsolete generally means
it has been replaced by a better system and you
should be using that.
Maintainers List
----------------
W: N/A
S: Supported
L: https://lists.projectacrn.org/g/acrn-dev
T: git - https://github.com/projectacrn/acrn-hypervisor.git
M: Eddie Dong <eddie.dong@intel.com>
M: Jason Chen <jason.cj.chen@intel.com>

223
hypervisor/Makefile Normal file
View File

@@ -0,0 +1,223 @@
#
# ACRN Hypervisor
#
MAJOR_VERSION=0
MINOR_VERSION=1
RELEASE ?= 0
GCC_MAJOR=$(shell echo __GNUC__ | $(CC) -E -x c - | tail -n 1)
GCC_MINOR=$(shell echo __GNUC_MINOR__ | $(CC) -E -x c - | tail -n 1)
#enable stack overflow check
STACK_PROTECTOR := 1
BASEDIR := $(shell pwd)
PLATFORM ?= sbl
HV_OBJDIR ?= $(CURDIR)/build
HV_FILE := acrn
CFLAGS += -Wall -W
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -fshort-wchar -ffreestanding
CFLAGS += -m64
CFLAGS += -mno-red-zone
CFLAGS += -static -nostdinc -nostdlib -fno-common
ifdef STACK_PROTECTOR
ifeq (true, $(shell [ $(GCC_MAJOR) -gt 4 ] && echo true))
CFLAGS += -fstack-protector-strong
else
ifeq (true, $(shell [ $(GCC_MAJOR) -eq 4 ] && [ $(GCC_MINOR) -ge 9 ] && echo true))
CFLAGS += -fstack-protector-strong
else
CFLAGS += -fstack-protector
endif
endif
CFLAGS += -DSTACK_PROTECTOR
endif
ASFLAGS += -m64 -nostdinc -nostdlib
LDFLAGS += -Wl,--gc-sections -static -nostartfiles -nostdlib
LDFLAGS += -Wl,-n,-z,max-page-size=0x1000
LDFLAGS += -Wl,-z,noexecstack
ARCH_CFLAGS += -gdwarf-2 -O0
ARCH_ASFLAGS += -gdwarf-2 -DASSEMBLER=1
ARCH_ARFLAGS +=
ARCH_LDFLAGS +=
ARCH_LDSCRIPT = $(HV_OBJDIR)/link_ram.ld
ARCH_LDSCRIPT_IN = bsp/ld/link_ram.ld.in
INCLUDE_PATH += include
INCLUDE_PATH += include/lib
INCLUDE_PATH += include/common
INCLUDE_PATH += include/arch/x86
INCLUDE_PATH += include/arch/x86/guest
INCLUDE_PATH += include/debug
INCLUDE_PATH += include/public
INCLUDE_PATH += include/common
INCLUDE_PATH += bsp/include
INCLUDE_PATH += bsp/$(PLATFORM)/include/bsp
INCLUDE_PATH += boot/include
CC = gcc
AS = as
AR = ar
LD = gcc
POSTLD = objcopy
D_SRCS += debug/dump.c
D_SRCS += debug/logmsg.c
D_SRCS += debug/shell_internal.c
D_SRCS += debug/shell_public.c
D_SRCS += debug/vuart.c
D_SRCS += debug/serial.c
D_SRCS += debug/uart16550.c
D_SRCS += debug/console.c
D_SRCS += debug/sbuf.c
C_SRCS += debug/printf.c
D_SRCS += boot/acpi.c
C_SRCS += boot/dmar_parse.c
C_SRCS += arch/x86/ioapic.c
C_SRCS += arch/x86/intr_lapic.c
S_SRCS += arch/x86/cpu_secondary.S
C_SRCS += arch/x86/cpu.c
C_SRCS += arch/x86/softirq.c
C_SRCS += arch/x86/cpuid.c
C_SRCS += arch/x86/mmu.c
C_SRCS += arch/x86/notify.c
C_SRCS += arch/x86/intr_main.c
C_SRCS += arch/x86/vtd.c
C_SRCS += arch/x86/gdt.c
S_SRCS += arch/x86/cpu_primary.S
S_SRCS += arch/x86/idt.S
C_SRCS += arch/x86/irq.c
C_SRCS += arch/x86/timer.c
C_SRCS += arch/x86/ept.c
S_SRCS += arch/x86/vmx_asm.S
C_SRCS += arch/x86/io.c
C_SRCS += arch/x86/interrupt.c
C_SRCS += arch/x86/vmexit.c
C_SRCS += arch/x86/vmx.c
C_SRCS += arch/x86/assign.c
C_SRCS += arch/x86/guest/vcpu.c
C_SRCS += arch/x86/guest/vm.c
C_SRCS += arch/x86/guest/instr_emul_wrapper.c
C_SRCS += arch/x86/guest/vlapic.c
C_SRCS += arch/x86/guest/guest.c
C_SRCS += arch/x86/guest/vmcall.c
C_SRCS += arch/x86/guest/vpic.c
C_SRCS += arch/x86/guest/vmsr.c
C_SRCS += arch/x86/guest/vioapic.c
C_SRCS += arch/x86/guest/instr_emul.c
C_SRCS += lib/spinlock.c
C_SRCS += lib/udelay.c
C_SRCS += lib/strnlen.c
C_SRCS += lib/memchr.c
C_SRCS += lib/stdlib.c
C_SRCS += lib/memcpy.c
C_SRCS += lib/strtol.c
C_SRCS += lib/mdelay.c
C_SRCS += lib/div.c
C_SRCS += lib/strchr.c
C_SRCS += lib/strcpy.c
C_SRCS += lib/memset.c
C_SRCS += lib/mem_mgt.c
C_SRCS += lib/strncpy.c
C_SRCS += lib/crypto/tinycrypt/hmac.c
C_SRCS += lib/crypto/tinycrypt/sha256.c
C_SRCS += lib/crypto/hkdf.c
C_SRCS += common/hv_main.c
C_SRCS += common/hypercall.c
C_SRCS += common/schedule.c
C_SRCS += common/vm_load.c
ifdef STACK_PROTECTOR
C_SRCS += common/stack_protector.c
endif
C_SRCS += bsp/$(PLATFORM)/vm_description.c
C_SRCS += bsp/$(PLATFORM)/$(PLATFORM).c
ifeq ($(PLATFORM),uefi)
C_SRCS += bsp/$(PLATFORM)/cmdline.c
endif
C_OBJS := $(patsubst %.c,$(HV_OBJDIR)/%.o,$(C_SRCS))
ifeq ($(RELEASE),0)
C_OBJS += $(patsubst %.c,$(HV_OBJDIR)/%.o,$(D_SRCS))
CFLAGS += -DHV_DEBUG
endif
S_OBJS := $(patsubst %.S,$(HV_OBJDIR)/%.o,$(S_SRCS))
DISTCLEAN_OBJS := $(shell find $(BASEDIR) -name '*.o')
VERSION := bsp/$(PLATFORM)/include/bsp/version.h
.PHONY: all
all: $(VERSION) $(HV_OBJDIR)/$(HV_FILE).32.out $(HV_OBJDIR)/$(HV_FILE).bin
rm -f $(VERSION)
ifeq ($(PLATFORM), uefi)
all: efi
.PHONY: efi
efi: $(HV_OBJDIR)/$(HV_FILE).bin
echo "building hypervisor as EFI executable..."
make -C bsp/uefi/efi HV_OBJDIR=$(HV_OBJDIR) RELEASE=$(RELEASE)
install: efi
make -C bsp/uefi/efi HV_OBJDIR=$(HV_OBJDIR) RELEASE=$(RELEASE) install
endif
$(HV_OBJDIR)/$(HV_FILE).32.out: $(HV_OBJDIR)/$(HV_FILE).out
$(POSTLD) -S --section-alignment=0x1000 -O elf32-i386 $< $@
$(HV_OBJDIR)/$(HV_FILE).bin: $(HV_OBJDIR)/$(HV_FILE).out
$(POSTLD) -O binary $< $(HV_OBJDIR)/$(HV_FILE).bin
$(HV_OBJDIR)/$(HV_FILE).out: $(C_OBJS) $(S_OBJS)
$(CC) -E -x c $(patsubst %, -I%, $(INCLUDE_PATH)) $(ARCH_LDSCRIPT_IN) | grep -v '^#' > $(ARCH_LDSCRIPT)
$(LD) -Wl,-Map=$(HV_OBJDIR)/$(HV_FILE).map -o $@ $(LDFLAGS) $(ARCH_LDFLAGS) -T$(ARCH_LDSCRIPT) $^
.PHONY: clean
clean:
rm -f $(C_OBJS)
rm -f $(S_OBJS)
rm -f $(VERSION)
rm -rf $(HV_OBJDIR)
.PHONY: distclean
distclean:
rm -f $(DISTCLEAN_OBJS)
rm -f $(C_OBJS)
rm -f $(S_OBJS)
rm -f $(VERSION)
rm -rf $(HV_OBJDIR)
rm -f tags TAGS cscope.files cscope.in.out cscope.out cscope.po.out GTAGS GPATH GRTAGS GSYMS
PHONY: (VERSION)
$(VERSION):
touch $(VERSION)
@COMMIT=`git rev-parse --verify --short HEAD 2>/dev/null`;\
DIRTY=`git diff-index --name-only HEAD`;\
if [ -n "$$DIRTY" ];then PATCH="$$COMMIT-dirty";else PATCH="$$COMMIT";fi;\
TIME=`date "+%Y%m%d"`;\
cat license_header > $(VERSION);\
echo "#define HV_MAJOR_VERSION $(MAJOR_VERSION)" >> $(VERSION);\
echo "#define HV_MINOR_VERSION $(MINOR_VERSION)" >> $(VERSION);\
echo "#define HV_BUILD_VERSION "\""$$PATCH"\""" >> $(VERSION);\
echo "#define HV_BUILD_TIME "\""$$TIME"\""" >> $(VERSION);\
echo "#define HV_BUILD_USER "\""$(USER)"\""" >> $(VERSION)
$(HV_OBJDIR)/%.o: %.c
[ ! -e $@ ] && mkdir -p $(dir $@); \
$(CC) $(patsubst %, -I%, $(INCLUDE_PATH)) -I. -c $(CFLAGS) $(ARCH_CFLAGS) $< -o $@
$(HV_OBJDIR)/%.o: %.S
[ ! -e $@ ] && mkdir -p $(dir $@); \
$(CC) $(patsubst %, -I%, $(INCLUDE_PATH)) -I. $(ASFLAGS) $(ARCH_ASFLAGS) -c $< -o $@

25
hypervisor/README.rst Normal file
View File

@@ -0,0 +1,25 @@
Embedded-Hypervisor
###################
This open source embedded hypervisor defines a software architecture for
running multiple software subsystems managed securely on a consolidated
system (by means of a virtual machine manager), and defines a reference
framework Device Model implementation for devices emulation
This embedded hypervisor is type-1 reference hypervisor, running
directly on the system hardware. It can be used for building software
defined cockpit (SDC) or In-Vehicle Experience (IVE) solutions running
on Intel Architecture Apollo Lake platforms. As a reference
implementation, it provides the basis for embedded hypervisor vendors to
build solutions with an open source reference I/O mediation solution,
and provides auto makers a reference software stack for SDC usage.
This embedded hypervisor is able to support both Linux* and Android* as
a Guest OS, managed by the hypervisor, where applications can run.
This embedded hypervisor is a partitioning hypervisor reference stack,
also suitable for non-automotive IoT & embedded device solutions. It
will be addressing the gap that currently exists between datacenter
hypervisors, hard partitioning hypervisors, and select industrial
applications. Extending the scope of this open source embedded
hypervisor relies on the involvement of community developers like you!

1015
hypervisor/arch/x86/assign.c Normal file

File diff suppressed because it is too large Load Diff

650
hypervisor/arch/x86/cpu.c Normal file
View File

@@ -0,0 +1,650 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <bsp_extern.h>
#include <hv_arch.h>
#include <schedule.h>
#include <version.h>
#include <hv_debug.h>
#ifdef CONFIG_EFI_STUB
extern uint32_t efi_physical_available_ap_bitmap;
#endif
uint64_t tsc_clock_freq = 1000000000;
spinlock_t cpu_secondary_spinlock = {
.head = 0,
.tail = 0
};
spinlock_t up_count_spinlock = {
.head = 0,
.tail = 0
};
void *per_cpu_data_base_ptr;
int phy_cpu_num;
unsigned long pcpu_sync = 0;
uint32_t up_count = 0;
DEFINE_CPU_DATA(uint8_t[STACK_SIZE], stack) __aligned(16);
DEFINE_CPU_DATA(uint8_t, lapic_id);
DEFINE_CPU_DATA(void *, vcpu);
DEFINE_CPU_DATA(int, state);
/* TODO: add more capability per requirement */
struct cpu_capability {
bool tsc_adjust_supported;
bool ibrs_ibpb_supported;
bool stibp_supported;
bool apicv_supported;
bool monitor_supported;
};
static struct cpu_capability cpu_caps;
static void apicv_cap_detect(void);
static void cpu_set_logical_id(uint32_t logical_id);
static void print_hv_banner(void);
bool check_monitor_support(void);
int cpu_find_logical_id(uint32_t lapic_id);
#ifndef CONFIG_EFI_STUB
static void start_cpus();
#endif
static void pcpu_sync_sleep(unsigned long *sync, int mask_bit);
int ibrs_type;
static void check_cpu_capability(void)
{
uint32_t eax, ebx, ecx, edx;
memset(&cpu_caps, 0, sizeof(struct cpu_capability));
cpuid(CPUID_EXTEND_FEATURE, &eax, &ebx, &ecx, &edx);
cpu_caps.tsc_adjust_supported = (ebx & CPUID_EBX_TSC_ADJ) ?
(true) : (false);
cpu_caps.ibrs_ibpb_supported = (edx & CPUID_EDX_IBRS_IBPB) ?
(true) : (false);
cpu_caps.stibp_supported = (edx & CPUID_EDX_STIBP) ?
(true) : (false);
/* For speculation defence.
* The default way is to set IBRS at vmexit and then do IBPB at vcpu
* context switch(ibrs_type == IBRS_RAW).
* Now provide an optimized way (ibrs_type == IBRS_OPT) which set
* STIBP and do IBPB at vmexit,since having STIBP always set has less
* impact than having IBRS always set. Also since IBPB is already done
* at vmexit, it is no necessary to do so at vcpu context switch then.
*/
ibrs_type = IBRS_NONE;
/* Currently for APL, if we enabled retpoline, then IBRS should not
* take effect
* TODO: add IA32_ARCH_CAPABILITIES[1] check, if this bit is set, IBRS
* should be set all the time instead of relying on retpoline
*/
#ifndef CONFIG_RETPOLINE
if (cpu_caps.ibrs_ibpb_supported) {
ibrs_type = IBRS_RAW;
if (cpu_caps.stibp_supported)
ibrs_type = IBRS_OPT;
}
#endif
}
bool check_tsc_adjust_support(void)
{
return cpu_caps.tsc_adjust_supported;
}
bool check_ibrs_ibpb_support(void)
{
return cpu_caps.ibrs_ibpb_supported;
}
bool check_stibp_support(void)
{
return cpu_caps.stibp_supported;
}
static void alloc_phy_cpu_data(int pcpu_num)
{
phy_cpu_num = pcpu_num;
per_cpu_data_base_ptr = calloc(1, PER_CPU_DATA_SIZE * pcpu_num);
ASSERT(per_cpu_data_base_ptr != NULL, "");
}
int __attribute__((weak)) parse_madt(uint8_t *lapic_id_base)
{
static const uint32_t lapic_id[] = {0, 2, 4, 6};
uint32_t i;
for (i = 0; i < ARRAY_SIZE(lapic_id); i++)
*lapic_id_base++ = lapic_id[i];
return ARRAY_SIZE(lapic_id);
}
static int init_phy_cpu_storage(void)
{
int i, pcpu_num = 0;
int bsp_cpu_id;
uint8_t bsp_lapic_id = 0;
uint8_t *lapic_id_base;
/*
* allocate memory to save all lapic_id detected in parse_mdt.
* We allocate 4K size which could save 4K CPUs lapic_id info.
*/
lapic_id_base = alloc_page(CPU_PAGE_SIZE);
ASSERT(lapic_id_base != NULL, "fail to alloc page");
pcpu_num = parse_madt(lapic_id_base);
alloc_phy_cpu_data(pcpu_num);
for (i = 0; i < pcpu_num; i++) {
per_cpu(lapic_id, i) = *lapic_id_base++;
#ifdef CONFIG_EFI_STUB
efi_physical_available_ap_bitmap |= 1 << per_cpu(lapic_id, i);
#endif
}
/* free memory after lapic_id are saved in per_cpu data */
free(lapic_id_base);
bsp_lapic_id = get_cur_lapic_id();
#ifdef CONFIG_EFI_STUB
efi_physical_available_ap_bitmap &= ~(1 << bsp_lapic_id);
#endif
bsp_cpu_id = cpu_find_logical_id(bsp_lapic_id);
ASSERT(bsp_cpu_id >= 0, "fail to get phy cpu id");
return bsp_cpu_id;
}
static void cpu_set_current_state(uint32_t logical_id, int state)
{
spinlock_obtain(&up_count_spinlock);
/* Check if state is initializing */
if (state == CPU_STATE_INITIALIZING) {
/* Increment CPU up count */
up_count++;
/* Save this CPU's logical ID to the TSC AUX MSR */
cpu_set_logical_id(logical_id);
}
/* Set state for the specified CPU */
per_cpu(state, logical_id) = state;
spinlock_release(&up_count_spinlock);
}
#ifdef STACK_PROTECTOR
struct stack_canary {
/* Gcc generates extra code, using [fs:40] to access canary */
uint8_t reserved[40];
uint64_t canary;
};
static DEFINE_CPU_DATA(struct stack_canary, stack_canary);
static uint64_t get_random_value(void)
{
uint64_t random = 0;
asm volatile ("1: rdrand %%rax\n"
"jnc 1b\n"
"mov %%rax, %0\n"
: "=r"(random) :: );
return random;
}
static void set_fs_base(void)
{
struct stack_canary *psc = &get_cpu_var(stack_canary);
psc->canary = get_random_value();
msr_write(MSR_IA32_FS_BASE, (uint64_t)psc);
}
#endif
void bsp_boot_init(void)
{
#ifdef HV_DEBUG
uint64_t start_tsc = rdtsc();
#endif
/* Clear BSS */
memset(_ld_bss_start, 0, _ld_bss_end - _ld_bss_start);
/* Build time sanity checks to make sure hard-coded offset
* is matching the actual offset!
*/
STATIC_ASSERT(offsetof(struct cpu_regs, rax) ==
VMX_MACHINE_T_GUEST_RAX_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rbx) ==
VMX_MACHINE_T_GUEST_RBX_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rcx) ==
VMX_MACHINE_T_GUEST_RCX_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rdx) ==
VMX_MACHINE_T_GUEST_RDX_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rbp) ==
VMX_MACHINE_T_GUEST_RBP_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rsi) ==
VMX_MACHINE_T_GUEST_RSI_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, rdi) ==
VMX_MACHINE_T_GUEST_RDI_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r8) ==
VMX_MACHINE_T_GUEST_R8_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r9) ==
VMX_MACHINE_T_GUEST_R9_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r10) ==
VMX_MACHINE_T_GUEST_R10_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r11) ==
VMX_MACHINE_T_GUEST_R11_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r12) ==
VMX_MACHINE_T_GUEST_R12_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r13) ==
VMX_MACHINE_T_GUEST_R13_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r14) ==
VMX_MACHINE_T_GUEST_R14_OFFSET);
STATIC_ASSERT(offsetof(struct cpu_regs, r15) ==
VMX_MACHINE_T_GUEST_R15_OFFSET);
STATIC_ASSERT(offsetof(struct run_context, cr2) ==
VMX_MACHINE_T_GUEST_CR2_OFFSET);
STATIC_ASSERT(offsetof(struct run_context, ia32_spec_ctrl) ==
VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET);
/* Initialize the hypervisor paging */
init_paging();
early_init_lapic();
init_phy_cpu_storage();
load_gdtr_and_tr();
/* Switch to run-time stack */
CPU_SP_WRITE(&get_cpu_var(stack)[STACK_SIZE - 1]);
#ifdef STACK_PROTECTOR
set_fs_base();
#endif
check_cpu_capability();
apicv_cap_detect();
/* Set state for this CPU to initializing */
cpu_set_current_state(CPU_BOOT_ID, CPU_STATE_INITIALIZING);
/* Perform any necessary BSP initialization */
init_bsp();
/* Initialize Serial */
serial_init();
/* Initialize console */
console_init();
/* Print Hypervisor Banner */
print_hv_banner();
/* Make sure rdtsc is enabled */
check_tsc();
/* Calculate TSC Frequency */
tsc_clock_freq = tsc_cycles_in_period(1000) / 1000 * 1000000;
/* Enable logging */
init_logmsg(LOG_BUF_SIZE,
LOG_DESTINATION);
#ifdef HV_DEBUG
/* Log first messages */
printf("HV version %d.%d-%s-%s build by %s, start time %lluus\r\n",
HV_MAJOR_VERSION, HV_MINOR_VERSION, HV_BUILD_TIME,
HV_BUILD_VERSION, HV_BUILD_USER,
TICKS_TO_US(start_tsc));
#endif
pr_dbg("Core %d is up", CPU_BOOT_ID);
/* Warn for security feature not ready */
if (!check_ibrs_ibpb_support() && !check_stibp_support()) {
pr_fatal("SECURITY WARNING!!!!!!");
pr_fatal("Please apply the latest CPU uCode patch!");
}
/* Initialize the shell */
shell_init();
/* Initialize interrupts */
interrupt_init(CPU_BOOT_ID);
timer_init();
setup_notification();
ptdev_init();
init_scheduler();
#ifndef CONFIG_EFI_STUB
/* Start all secondary cores */
start_cpus();
/* Trigger event to allow secondary CPUs to continue */
bitmap_set(0, &pcpu_sync);
#else
memcpy_s(_ld_cpu_secondary_reset_start,
(unsigned long)&_ld_cpu_secondary_reset_size,
_ld_cpu_secondary_reset_load,
(unsigned long)&_ld_cpu_secondary_reset_size);
#endif
ASSERT(get_cpu_id() == CPU_BOOT_ID, "");
init_iommu();
console_setup_timer();
/* Start initializing the VM for this CPU */
hv_main(CPU_BOOT_ID);
/* Control should not come here */
cpu_halt(CPU_BOOT_ID);
}
void cpu_secondary_init(void)
{
/* NOTE: Use of local / stack variables in this function is problematic
* since the stack is switched in the middle of the function. For this
* reason, the logical id is only temporarily stored in a static
* variable, but this will be over-written once subsequent CPUs
* start-up. Once the spin-lock is released, the cpu_logical_id_get()
* API is used to obtain the logical ID
*/
/* Switch this CPU to use the same page tables set-up by the
* primary/boot CPU
*/
enable_paging(get_paging_pml4());
early_init_lapic();
/* Find the logical ID of this CPU given the LAPIC ID
* temp_logical_id =
* cpu_find_logical_id(get_cur_lapic_id());
*/
cpu_find_logical_id(get_cur_lapic_id());
/* Set state for this CPU to initializing */
cpu_set_current_state(cpu_find_logical_id
(get_cur_lapic_id()),
CPU_STATE_INITIALIZING);
/* Switch to run-time stack */
CPU_SP_WRITE(&get_cpu_var(stack)[STACK_SIZE - 1]);
#ifdef STACK_PROTECTOR
set_fs_base();
#endif
load_gdtr_and_tr();
/* Make sure rdtsc is enabled */
check_tsc();
pr_dbg("Core %d is up", get_cpu_id());
/* Release secondary boot spin-lock to allow one of the next CPU(s) to
* perform this common initialization
*/
spinlock_release(&cpu_secondary_spinlock);
/* Initialize secondary processor interrupts. */
interrupt_init(get_cpu_id());
timer_init();
/* Wait for boot processor to signal all secondary cores to continue */
pcpu_sync_sleep(&pcpu_sync, 0);
#ifdef CONFIG_EFI_STUB
bitmap_clr(0, &pcpu_sync);
#endif
hv_main(get_cpu_id());
/* Control will only come here for secondary CPUs not configured for
* use or if an error occurs in hv_main
*/
cpu_halt(get_cpu_id());
}
int cpu_find_logical_id(uint32_t lapic_id)
{
int i;
for (i = 0; i < phy_cpu_num; i++) {
if (per_cpu(lapic_id, i) == lapic_id)
return i;
}
return -1;
}
#ifndef CONFIG_EFI_STUB
/*
* Start all secondary CPUs.
*/
static void start_cpus()
{
uint32_t timeout;
uint32_t expected_up;
/*Copy segment for AP initialization code below 1MB */
memcpy_s(_ld_cpu_secondary_reset_start,
(unsigned long)&_ld_cpu_secondary_reset_size,
_ld_cpu_secondary_reset_load,
(unsigned long)&_ld_cpu_secondary_reset_size);
/* Set flag showing number of CPUs expected to be up to all
* cpus
*/
expected_up = phy_cpu_num;
/* Broadcast IPIs to all other CPUs */
send_startup_ipi(INTR_CPU_STARTUP_ALL_EX_SELF,
-1U, ((paddr_t) cpu_secondary_reset));
/* Wait until global count is equal to expected CPU up count or
* configured time-out has expired
*/
timeout = CPU_UP_TIMEOUT * 1000;
while ((up_count != expected_up) && (timeout != 0)) {
/* Delay 10us */
udelay(10);
/* Decrement timeout value */
timeout -= 10;
}
/* Check to see if all expected CPUs are actually up */
if (up_count != expected_up) {
/* Print error */
pr_fatal("Secondary CPUs failed to come up");
/* Error condition - loop endlessly for now */
do {
} while (1);
}
}
#endif
void cpu_halt(uint32_t logical_id)
{
/* For debug purposes, using a stack variable in the while loop enables
* us to modify the value using a JTAG probe and resume if needed.
*/
int halt = 1;
/* Set state to show CPU is halted */
cpu_set_current_state(logical_id, CPU_STATE_HALTED);
/* Halt the CPU */
do {
asm volatile ("hlt");
} while (halt);
}
static void cpu_set_logical_id(uint32_t logical_id)
{
/* Write TSC AUX register */
msr_write(MSR_IA32_TSC_AUX, (uint64_t) logical_id);
}
static void print_hv_banner(void)
{
char *boot_msg = "ACRN Hypervisor\n\r";
/* Print the boot message */
printf(boot_msg);
}
static void pcpu_sync_sleep(unsigned long *sync, int mask_bit)
{
int wake_sync = (1 << mask_bit);
if (check_monitor_support()) {
/* Wait for the event to be set using monitor/mwait */
asm volatile ("1: cmpl %%ebx,(%%eax)\n"
" je 2f\n"
" monitor\n"
" mwait\n"
" jmp 1b\n"
"2:\n"
:
: "a" (sync), "d"(0), "c"(0),
"b"(wake_sync)
: "cc");
} else {
/* Wait for the event to be set using pause */
asm volatile ("1: cmpl %%ebx,(%%eax)\n"
" je 2f\n"
" pause\n"
" jmp 1b\n"
"2:\n"
:
: "a" (sync), "d"(0), "c"(0),
"b"(wake_sync)
: "cc");
}
}
/*check allowed ONEs setting in vmx control*/
static bool is_ctrl_setting_allowed(uint64_t msr_val, uint32_t ctrl)
{
/*
* Intel SDM Appendix A.3
* - bitX in ctrl can be set 1
* only if bit 32+X in msr_val is 1
*/
return ((((uint32_t)(msr_val >> 32)) & ctrl) == ctrl);
}
static void apicv_cap_detect(void)
{
uint64_t val64;
uint32_t ctrl;
bool result;
ctrl = VMX_PROCBASED_CTLS_TPR_SHADOW;
val64 = msr_read(MSR_IA32_VMX_PROCBASED_CTLS);
result = is_ctrl_setting_allowed(val64, ctrl);
if (result) {
ctrl = VMX_PROCBASED_CTLS2_VAPIC |
VMX_PROCBASED_CTLS2_VAPIC_REGS |
VMX_PROCBASED_CTLS2_VIRQ;
val64 = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2);
result = is_ctrl_setting_allowed(val64, ctrl);
}
cpu_caps.apicv_supported = result;
}
bool is_apicv_enabled(void)
{
return cpu_caps.apicv_supported;
}
static void monitor_cap_detect(void)
{
uint32_t eax, ebx, ecx, edx;
uint32_t family;
uint32_t model;
/* Run CPUID to determine if MONITOR support available */
cpuid(CPUID_FEATURES, &eax, &ebx, &ecx, &edx);
/* See if MONITOR feature bit is set in ECX */
if (ecx & CPUID_ECX_MONITOR)
cpu_caps.monitor_supported = true;
/* don't use monitor for CPU (family: 0x6 model: 0x5c)
* in hypervisor, but still expose it to the guests and
* let them handle it correctly
*/
family = (eax >> 8) & 0xff;
if (family == 0xF)
family += (eax >> 20) & 0xff;
model = (eax >> 4) & 0xf;
if (family >= 0x06)
model += ((eax >> 16) & 0xf) << 4;
if (cpu_caps.monitor_supported &&
(family == 0x06) &&
(model == 0x5c)) {
cpu_caps.monitor_supported = false;
}
}
bool check_monitor_support(void)
{
return cpu_caps.monitor_supported;
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <cpu.h>
#include <mmu.h>
#include <gdt.h>
#include <idt.h>
#include <msr.h>
/* MULTIBOOT HEADER */
#define MULTIBOOT_HEADER_MAGIC 0x1badb002
#define MULTIBOOT_HEADER_FLAGS 0x00000002 /*flags bit 1 : enable mem_*, mmap_**/
.section multiboot_header, "a"
.align 4
/* header magic */
.long MULTIBOOT_HEADER_MAGIC
/* header flags - flags bit 6 : enable mmap_* */
.long MULTIBOOT_HEADER_FLAGS
/* header checksum = -(magic + flags) */
.long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
.section entry, "ax"
.align 8
.code32
.global cpu_primary_start_32
cpu_primary_start_32:
/* Disable interrupts */
cli
/* Clear direction flag */
cld
/* save eax and ebx */
movl %eax, %esp
movl %ebx, %ebp
/* detect whether it is in long mode */
movl $MSR_IA32_EFER, %ecx
rdmsr
test $MSR_IA32_EFER_LMA_BIT, %eax
/* jump to 64bit entry if it is already in long mode */
jne cpu_primary_start_64
/* save the MULTBOOT magic number & MBI */
movl %esp, (boot_regs)
movl %ebp, (boot_regs+4)
/* Disable paging */
mov %cr0, %ebx
andl $~CR0_PG, %ebx
mov %ebx, %cr0
/* Set DE, PAE, MCE and OS support bits in CR4 */
movl $(CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT), %eax
mov %eax, %cr4
/* Set CR3 to PML4 table address */
movl $cpu_boot32_page_tables_start, %edi
mov %edi, %cr3
/* Set LME bit in EFER */
movl $MSR_IA32_EFER, %ecx
rdmsr
orl $MSR_IA32_EFER_LME_BIT, %eax
wrmsr
/* Enable paging, protection, numeric error and co-processor
monitoring in CR0 to enter long mode */
mov %cr0, %ebx
orl $(CR0_PG | CR0_PE | CR0_MP | CR0_NE), %ebx
mov %ebx, %cr0
/* Load temportary GDT pointer value */
mov $cpu_primary32_gdt_ptr, %ebx
lgdt (%ebx)
/* Perform a long jump based to start executing in 64-bit mode */
ljmp $HOST_GDT_RING0_CODE_SEL, $primary_start_long_mode
.code64
.org 0x200
.global cpu_primary_start_64
cpu_primary_start_64:
/* save the MULTBOOT magic number & MBI */
movl %edi, (boot_regs)
movl %esi, (boot_regs+4)
#ifdef CONFIG_EFI_STUB
movl %edx, (boot_regs+8)
#endif
primary_start_long_mode:
/* Fix up the IDT desciptors */
movl $HOST_IDT, %edx
movl $HOST_IDT_ENTRIES, %ecx
.LFixUpIDT_Entries:
xorl %eax, %eax
xchgl %eax, 12(%edx) /* Set rsvd bits to 0; eax now has
high 32 of entry point */
xchgl %eax, 8(%edx) /* Set bits 63..32 of entry point;
eax now has low 32 of entry point */
movw %ax, (%edx) /* Set bits 0-15 of procedure entry
point */
shr $16, %eax
movw %ax, 6(%edx) /* Set bits 16-31 of entry point */
addl $X64_IDT_DESC_SIZE,%edx
loop .LFixUpIDT_Entries
/* Load IDT */
mov $HOST_IDTR, %rcx
lidtq (%rcx)
/* Load temportary GDT pointer value */
mov $cpu_primary32_gdt_ptr, %ebx
lgdt (%ebx)
/* Replace CS with the correct value should we need it */
mov $HOST_GDT_RING0_CODE_SEL, %bx
mov %bx, jcs
movabsq $jmpbuf, %rax
rex.w ljmp *(%rax)
.data
jmpbuf: .quad after
jcs: .word 0
.text
after:
/* Initialize temporary stack pointer */
movq $_ld_bss_end, %rsp
add $CPU_PAGE_SIZE,%rsp
and $(~(CPU_STACK_ALIGN - 1)),%rsp
// load all selector registers with appropriate values
xor %edx, %edx
lldt %dx
movl $HOST_GDT_RING0_DATA_SEL,%eax
mov %eax,%ss // Was 32bit POC Stack
mov %eax,%ds // Was 32bit POC Data
mov %eax,%es // Was 32bit POC Data
mov %edx,%fs // Was 32bit POC Data
mov %edx,%gs // Was 32bit POC CLS
/* Push sp magic to top of stack for call trace */
pushq $SP_BOTTOM_MAGIC
/* continue with chipset level initialization */
call bsp_boot_init
loop:
jmp loop
.align 4
.global boot_regs
boot_regs:
.long 0x00000000
.long 0x00000000
#ifdef CONFIG_EFI_STUB
.long 0x00000000
#endif
/* GDT table */
.align 4
cpu_primary32_gdt:
.quad 0x0000000000000000
.quad 0x00af9b000000ffff
.quad 0x00cf93000000ffff
cpu_primary32_gdt_end:
/* GDT pointer */
.align 2
cpu_primary32_gdt_ptr:
.short (cpu_primary32_gdt_end - cpu_primary32_gdt) - 1
.quad cpu_primary32_gdt
/* PML4, PDPT, and PD tables initialized to map first 4 GBytes of memory */
.align CPU_PAGE_SIZE
.global cpu_boot32_page_tables_start
cpu_boot32_page_tables_start:
.quad cpu_primary32_pdpt_addr + (IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
.align CPU_PAGE_SIZE
cpu_primary32_pdpt_addr:
address = 0
.rept 4
.quad cpu_primary32_pdt_addr + address + \
(IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + CPU_PAGE_SIZE
.endr
.align CPU_PAGE_SIZE
cpu_primary32_pdt_addr:
address = 0
.rept 2048
.quad address + (IA32E_PDPTE_PS_BIT | IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + 0x200000
.endr

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <spinlock.h>
#include <gdt.h>
#include <cpu.h>
#include <mmu.h>
#include <msr.h>
.extern cpu_secondary_init
.extern cpu_logical_id
.extern _ld_bss_end
.extern HOST_GDTR
.section .cpu_secondary_reset,"ax"
.align 4
.code16
.global cpu_secondary_reset
cpu_secondary_reset:
/* Disable local interrupts */
cli
/* Set DE, PAE, MCE and OS support bits in CR4 */
movl $(CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT), %eax
mov %eax, %cr4
/* Set CR3 to PML4 table address */
movl $CPU_Boot_Page_Tables_Start, %edi
mov %edi, %cr3
/* Set LME bit in EFER */
movl $MSR_IA32_EFER, %ecx
rdmsr
orl $MSR_IA32_EFER_LME_BIT, %eax
wrmsr
/* Enable paging, protection, numeric error and co-processor
monitoring in CR0 to enter long mode */
mov %cr0, %ebx
orl $(CR0_PG | CR0_PE | CR0_MP | CR0_NE), %ebx
mov %ebx, %cr0
/* Load temportary GDT pointer value */
mov $cpu_secondary_gdt_ptr, %ebx
lgdt (%ebx)
/* Perform a long jump based to start executing in 64-bit mode */
data32 ljmp $HOST_GDT_RING0_CODE_SEL, $cpu_secondary_long_mode
.code64
cpu_secondary_long_mode:
/* Set up all other data segment registers */
movl $HOST_GDT_RING0_DATA_SEL, %eax
mov %eax, %ss
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
/* Obtain secondary CPU spin-lock to serialize
booting of secondary cores for a bit */
spinlock_obtain(cpu_secondary_spinlock)
/* Initialize temporary stack pointer
NOTE: Using the PML4 memory (PDPT address is top of memory
for the PML4 page) for the temporary stack
as we are only using the very first entry in
this page and the stack is growing down from
the top of this page. This stack is only
used for a VERY short period of time, so
this reuse of PML4 memory should be acceptable. */
movq $cpu_secondary_pdpt_addr, %rsp
/* Push sp magic to top of stack for call trace */
pushq $SP_BOTTOM_MAGIC
/* Jump to C entry for the AP */
call cpu_secondary_init
cpu_secondary_error:
/* Error condition trap */
jmp cpu_secondary_error
/* GDT table */
.align 4
cpu_secondary_gdt:
.quad 0x0000000000000000
.quad 0x00af9b000000ffff
.quad 0x00cf93000000ffff
cpu_secondary_gdt_end:
/* GDT pointer */
.align 2
cpu_secondary_gdt_ptr:
.short (cpu_secondary_gdt_end - cpu_secondary_gdt) - 1
.quad cpu_secondary_gdt
/* PML4, PDPT, and PD tables initialized to map first 4 GBytes of memory */
.align CPU_PAGE_SIZE
.global CPU_Boot_Page_Tables_Start
CPU_Boot_Page_Tables_Start:
.quad cpu_secondary_pdpt_addr + (IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
.align CPU_PAGE_SIZE
cpu_secondary_pdpt_addr:
address = 0
.rept 4
.quad cpu_secondary_pdt_addr + address + \
(IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + CPU_PAGE_SIZE
.endr
.align CPU_PAGE_SIZE
cpu_secondary_pdt_addr:
address = 0
.rept 2048
.quad address + (IA32E_PDPTE_PS_BIT | IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + 0x200000
.endr
/*******************************************************************
* GUEST initial 4G page table
*
* guest starts with long mode, HV needs to prepare Guest identity
* mapped page table.
*
* guest page tables covers 4G size, with 2M page size.
*
* HV copy this page table (6 pages) to guest address
* CPU_Boot_Page_Tables_Start_VM before executing guest instruction.
*
******************************************************************/
.align CPU_PAGE_SIZE
.global CPU_Boot_Page_Tables_Start_VM
CPU_Boot_Page_Tables_Start_VM:
.quad vm_cpu_pdpt_addr + (IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
.align CPU_PAGE_SIZE
vm_cpu_pdpt_addr:
address = 0
.rept 4
.quad vm_cpu_pdt_addr + address + (IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + CPU_PAGE_SIZE
.endr
.align CPU_PAGE_SIZE
vm_cpu_pdt_addr:
address = 0
.rept 2048
.quad address + (IA32E_PDPTE_PS_BIT | IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT)
address = address + 0x200000
.endr
.end

195
hypervisor/arch/x86/cpuid.c Normal file
View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <cpu.h>
void emulate_cpuid(struct vcpu *vcpu, uint32_t src_op, uint32_t *eax_ptr,
uint32_t *ebx_ptr, uint32_t *ecx_ptr, uint32_t *edx_ptr)
{
uint32_t apicid = vlapic_get_id(vcpu->arch_vcpu.vlapic);
static const char sig[12] = "ACRNACRNACRN";
const uint32_t *sigptr = (const uint32_t *)sig;
uint32_t count = *ecx_ptr;
if ((src_op != 0x40000000) && (src_op != 0x40000010))
cpuid_count(src_op, count, eax_ptr, ebx_ptr, ecx_ptr, edx_ptr);
switch (src_op) {
/* Virtualize cpuid 0x01 */
case 0x01:
/* Patching initial APIC ID */
*ebx_ptr &= ~APIC_ID_MASK;
*ebx_ptr |= (apicid & APIC_ID_MASK);
/* mask mtrr */
*edx_ptr &= ~CPUID_EDX_MTRR;
/* Patching X2APIC, X2APIC mode is disabled by default. */
if (x2apic_enabled)
*ecx_ptr |= CPUID_ECX_x2APIC;
else
*ecx_ptr &= ~CPUID_ECX_x2APIC;
/* mask pcid */
*ecx_ptr &= ~CPUID_ECX_PCID;
/*mask vmx to guest os */
*ecx_ptr &= ~CPUID_ECX_VMX;
break;
/* Virtualize cpuid 0x07 */
case 0x07:
/* mask invpcid */
*ebx_ptr &= ~CPUID_EBX_INVPCID;
break;
case 0x0a:
/* not support pmu */
*eax_ptr &= ~0xff;
break;
/* Virtualize cpuid 0x0b */
case 0x0b:
/* Patching X2APIC */
if (!x2apic_enabled) {
*eax_ptr = 0;
*ebx_ptr = 0;
*ecx_ptr = 0;
*edx_ptr = 0;
}
break;
/*
* Leaf 0x40000000
* This leaf returns the CPUID leaf range supported by the
* hypervisor and the hypervisor vendor signature.
*
* EAX: The maximum input value for CPUID supported by the
* hypervisor.
* EBX, ECX, EDX: Hypervisor vendor ID signature.
*/
case 0x40000000:
*eax_ptr = 0x40000010;
*ebx_ptr = sigptr[0];
*ecx_ptr = sigptr[1];
*edx_ptr = sigptr[2];
break;
/*
* Leaf 0x40000010 - Timing Information.
* This leaf returns the current TSC frequency and
* current Bus frequency in kHz.
*
* EAX: (Virtual) TSC frequency in kHz.
* TSC frequency is calculated from PIT in ACRN
* EBX: (Virtual) Bus (local apic timer) frequency in kHz.
* Bus (local apic timer) frequency is hardcoded as
* (128 * 1024 * 1024) in ACRN
* ECX, EDX: RESERVED (reserved fields are set to zero).
*/
case 0x40000010:
*eax_ptr = (uint32_t)(tsc_clock_freq / 1000);
*ebx_ptr = (128 * 1024 * 1024) / 1000;
*ecx_ptr = 0;
*edx_ptr = 0;
break;
default:
break;
}
}
static DEFINE_CPU_DATA(struct cpuid_cache_entry[CPUID_EXTEND_FEATURE_CACHE_MAX],
cpuid_cache);
static inline struct cpuid_cache_entry *find_cpuid_cache_entry(uint32_t op,
uint32_t count)
{
int pcpu_id = get_cpu_id();
enum cpuid_cache_idx idx = CPUID_EXTEND_FEATURE_CACHE_MAX;
if ((count != 0))
return NULL;
switch (op) {
case CPUID_VENDORSTRING:
idx = CPUID_VENDORSTRING_CACHE_IDX;
break;
case CPUID_FEATURES:
idx = CPUID_FEATURES_CACHE_IDX;
break;
case CPUID_EXTEND_FEATURE:
idx = CPUID_EXTEND_FEATURE_CACHE_IDX;
break;
default:
break;
}
if (idx == CPUID_EXTEND_FEATURE_CACHE_MAX)
return NULL;
return &per_cpu(cpuid_cache, pcpu_id)[idx];
}
inline void cpuid_count(uint32_t op, uint32_t count,
uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
{
struct cpuid_cache_entry *entry;
entry = find_cpuid_cache_entry(op, count);
if (entry == NULL) {
native_cpuid_count(op, count, a, b, c, d);
} else if (entry->inited) {
*a = entry->a;
*b = entry->b;
*c = entry->c;
*d = entry->d;
} else {
native_cpuid_count(op, count, a, b, c, d);
entry->a = *a;
entry->b = *b;
entry->c = *c;
entry->d = *d;
entry->inited = 1;
}
}

569
hypervisor/arch/x86/ept.c Normal file
View File

@@ -0,0 +1,569 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <acrn_hv_defs.h>
#include <hv_arch.h>
#include <hypercall.h>
#include <hv_debug.h>
#include "guest/instr_emul_wrapper.h"
#include "guest/instr_emul.h"
#define ACRN_DBG_EPT 6
void *create_guest_paging(struct vm *vm)
{
void *hva_dest;
void *hva_src;
/* copy guest identity mapped 4G page table to guest */
hva_dest = GPA2HVA(vm,
(uint64_t)CPU_Boot_Page_Tables_Start_VM);
hva_src = (void *)(_ld_cpu_secondary_reset_load
+ (CPU_Boot_Page_Tables_Start_VM
- _ld_cpu_secondary_reset_start));
/* 2MB page size, need to copy 6 pages */
memcpy_s(hva_dest, 6 * CPU_PAGE_SIZE, hva_src, 6 * CPU_PAGE_SIZE);
return (void *)CPU_Boot_Page_Tables_Start_VM;
}
static void *find_next_table(uint32_t table_offset,
void *table_base)
{
uint64_t table_entry;
uint64_t table_present;
void *sub_table_addr = 0;
/* Read the table entry */
table_entry = MEM_READ64(table_base
+ (table_offset * IA32E_COMM_ENTRY_SIZE));
/* If bit 7 is set, entry is not a subtable. */
if ((table_entry & IA32E_PDPTE_PS_BIT)
|| (table_entry & IA32E_PDE_PS_BIT))
return sub_table_addr;
/* Set table present bits to any of the read/write/execute bits */
table_present = (IA32E_EPT_R_BIT | IA32E_EPT_W_BIT | IA32E_EPT_X_BIT);
/* Determine if a valid entry exists */
if ((table_entry & table_present) == 0) {
/* No entry present */
return sub_table_addr;
}
/* Get address of the sub-table */
sub_table_addr = (void *)(table_entry & IA32E_REF_MASK);
/* Return the next table in the walk */
return sub_table_addr;
}
void free_ept_mem(void *pml4_addr)
{
void *pdpt_addr;
void *pde_addr;
void *pte_addr;
uint32_t pml4_index;
uint32_t pdpt_index;
uint32_t pde_index;
for (pml4_index = 0; pml4_index < IA32E_NUM_ENTRIES; pml4_index++) {
/* Walk from the PML4 table to the PDPT table */
pdpt_addr = find_next_table(pml4_index, pml4_addr);
if (pdpt_addr == NULL)
continue;
for (pdpt_index = 0; pdpt_index < IA32E_NUM_ENTRIES;
pdpt_index++) {
/* Walk from the PDPT table to the PD table */
pde_addr = find_next_table(pdpt_index, pdpt_addr);
if (pde_addr == NULL)
continue;
for (pde_index = 0; pde_index < IA32E_NUM_ENTRIES;
pde_index++) {
/* Walk from the PD table to the page table */
pte_addr = find_next_table(pde_index,
pde_addr);
/* Free page table entry table */
if (pte_addr)
free(pte_addr);
}
/* Free page directory entry table */
if (pde_addr)
free(pde_addr);
}
free(pdpt_addr);
}
free(pml4_addr);
}
void destroy_ept(struct vm *vm)
{
free_ept_mem(vm->arch_vm.ept);
free_ept_mem(vm->arch_vm.m2p);
}
uint64_t gpa2hpa_check(struct vm *vm, uint64_t gpa,
uint64_t size, int *found, bool assert)
{
uint64_t hpa = 0;
int _found = 0;
struct entry_params entry;
struct map_params map_params;
map_params.page_table_type = PT_EPT;
map_params.pml4_base = vm->arch_vm.ept;
map_params.pml4_inverted = vm->arch_vm.m2p;
obtain_last_page_table_entry(&map_params, &entry,
(void *)gpa, true);
if (entry.entry_present == PT_PRESENT
/* if cross several pages, now not handle it,
* only print error info
*/
&& ((gpa % entry.page_size) + size) <= entry.page_size) {
_found = 1;
hpa = ((entry.entry_val & (~(entry.page_size - 1)))
| (gpa & (entry.page_size - 1)));
}
if (found != NULL)
*found = _found;
if (_found == 0 && assert) {
pr_err("VM %d GPA2HPA: failed for gpa 0x%llx",
vm->attr.boot_idx, gpa);
ASSERT(_found != 0, "GPA2HPA not found");
}
pr_dbg("GPA2HPA: 0x%llx->0x%llx", gpa, hpa);
return hpa;
}
uint64_t gpa2hpa(struct vm *vm, uint64_t gpa)
{
return gpa2hpa_check(vm, gpa, 0, NULL, true);
}
uint64_t hpa2gpa(struct vm *vm, uint64_t hpa)
{
struct entry_params entry;
struct map_params map_params;
map_params.page_table_type = PT_EPT;
map_params.pml4_base = vm->arch_vm.ept;
map_params.pml4_inverted = vm->arch_vm.m2p;
obtain_last_page_table_entry(&map_params, &entry,
(void *)hpa, false);
if (entry.entry_present == PT_NOT_PRESENT) {
pr_err("VM %d hpa2gpa: failed for hpa 0x%llx",
vm->attr.boot_idx, hpa);
ASSERT(false, "hpa2gpa not found");
}
return ((entry.entry_val & (~(entry.page_size - 1)))
| (hpa & (entry.page_size - 1)));
}
int is_ept_supported(void)
{
uint16_t status;
uint64_t tmp64;
/* Read primary processor based VM control. */
tmp64 = msr_read(MSR_IA32_VMX_PROCBASED_CTLS);
/* Check if secondary processor based VM control is available. */
if (tmp64 & MMU_MEM_ATTR_BIT_EXECUTE_DISABLE) {
/* Read primary processor based VM control. */
tmp64 = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2);
/* Check if EPT is supported. */
if (tmp64 & (((uint64_t)VMX_PROCBASED_CTLS2_EPT) << 32)) {
/* EPT is present. */
status = 1;
} else {
status = 0;
}
} else {
/* Secondary processor based VM control is not present */
status = 0;
}
return status;
}
static int check_hv_mmio_range(struct vm *vm, struct mem_io *mmio)
{
int status = false;
struct list_head *pos;
struct mem_io_node *mmio_node;
list_for_each(pos, &vm->mmio_list) {
mmio_node = list_entry(pos, struct mem_io_node, list);
/* Check if this handler's range covers this memory access */
if ((mmio->paddr >= mmio_node->range_start) &&
(mmio->paddr + mmio->access_size <=
mmio_node->range_end)) {
status = true;
/* Break from loop - only 1 handler allowed to support
* a given memory range
*/
break;
}
}
/* Return success for now */
return status;
}
static int hv_emulate_mmio(struct vcpu *vcpu, struct mem_io *mmio)
{
int status = -EINVAL;
struct list_head *pos;
struct mem_io_node *mmio_node;
struct vm *vm = vcpu->vm;
list_for_each(pos, &vm->mmio_list) {
mmio_node = list_entry(pos, struct mem_io_node, list);
/* Check if this handler's range covers this memory access */
if ((mmio->paddr >= mmio_node->range_start) &&
(mmio->paddr + mmio->access_size
<= mmio_node->range_end)) {
ASSERT((mmio->paddr % mmio->access_size) == 0,
"access size not align with paddr");
/* Handle this MMIO operation */
status = mmio_node->read_write(vcpu, mmio,
mmio_node->handler_private_data);
/* Break from loop - only 1 handler allowed to support
* given memory range
*/
break;
}
}
/* Return success for now */
return status;
}
int register_mmio_emulation_handler(struct vm *vm,
hv_mem_io_handler_t read_write, uint64_t start,
uint64_t end, void *handler_private_data)
{
int status = -EINVAL;
struct mem_io_node *mmio_node;
if (vm->hw.created_vcpus > 0 && vm->hw.vcpu_array[0]->launched) {
ASSERT(0, "register mmio handler after vm launched");
return status;
}
/* Ensure both a read/write handler and range check function exist */
if ((read_write != HV_NULL) && (end > start)) {
/* Allocate memory for node */
mmio_node =
(struct mem_io_node *)calloc(1, sizeof(struct mem_io_node));
/* Ensure memory successfully allocated */
if (mmio_node) {
/* Fill in information for this node */
mmio_node->read_write = read_write;
mmio_node->handler_private_data = handler_private_data;
INIT_LIST_HEAD(&mmio_node->list);
list_add(&mmio_node->list, &vm->mmio_list);
mmio_node->range_start = start;
mmio_node->range_end = end;
ept_mmap(vm, start, start, end - start,
MAP_UNMAP, 0);
/* Return success */
status = 0;
}
}
/* Return status to caller */
return status;
}
void unregister_mmio_emulation_handler(struct vm *vm, uint64_t start,
uint64_t end)
{
struct list_head *pos, *tmp;
struct mem_io_node *mmio_node;
list_for_each_safe(pos, tmp, &vm->mmio_list) {
mmio_node = list_entry(pos, struct mem_io_node, list);
if ((mmio_node->range_start == start) &&
(mmio_node->range_end == end)) {
/* assume only one entry found in mmio_list */
list_del_init(&mmio_node->list);
free(mmio_node);
break;
}
}
}
int dm_emulate_mmio_post(struct vcpu *vcpu)
{
int ret = 0;
int cur = vcpu->vcpu_id;
struct vhm_request_buffer *req_buf =
(void *)HPA2HVA(vcpu->vm->sw.req_buf);
vcpu->req.reqs.mmio_request.value =
req_buf->req_queue[cur].reqs.mmio_request.value;
/* VHM emulation data already copy to req, mark to free slot now */
req_buf->req_queue[cur].valid = false;
if (req_buf->req_queue[cur].processed == REQ_STATE_SUCCESS)
vcpu->mmio.mmio_status = MMIO_TRANS_VALID;
else {
vcpu->mmio.mmio_status = MMIO_TRANS_INVALID;
goto out;
}
if (vcpu->mmio.read_write == HV_MEM_IO_READ) {
vcpu->mmio.value = vcpu->req.reqs.mmio_request.value;
/* Emulate instruction and update vcpu register set */
ret = emulate_instruction(vcpu, &vcpu->mmio);
if (ret != 0)
goto out;
}
out:
return ret;
}
static int dm_emulate_mmio_pre(struct vcpu *vcpu, uint64_t exit_qual)
{
int status;
status = analyze_instruction(vcpu, &vcpu->mmio);
if (status != 0)
return status;
if (vcpu->mmio.read_write == HV_MEM_IO_WRITE) {
status = emulate_instruction(vcpu, &vcpu->mmio);
if (status != 0)
return status;
vcpu->req.reqs.mmio_request.value = vcpu->mmio.value;
/* XXX: write access while EPT perm RX -> WP */
if ((exit_qual & 0x38) == 0x28)
vcpu->req.type = REQ_WP;
}
if (vcpu->req.type == 0)
vcpu->req.type = REQ_MMIO;
vcpu->req.reqs.mmio_request.direction = vcpu->mmio.read_write;
vcpu->req.reqs.mmio_request.address = (long)vcpu->mmio.paddr;
vcpu->req.reqs.mmio_request.size = vcpu->mmio.access_size;
return 0;
}
int ept_violation_handler(struct vcpu *vcpu)
{
int status;
uint64_t exit_qual;
uint64_t gpa;
/* Handle page fault from guest */
exit_qual = exec_vmread(VMX_EXIT_QUALIFICATION);
memset(&vcpu->req, 0, sizeof(struct vhm_request));
/* Specify if read or write operation */
if (exit_qual & 0x2) {
/* Write operation */
vcpu->mmio.read_write = HV_MEM_IO_WRITE;
/* Get write value from appropriate register in context */
/* TODO: Need to figure out how to determine value being
* written
*/
vcpu->mmio.value = 0;
} else {
/* Read operation */
vcpu->mmio.read_write = HV_MEM_IO_READ;
/* Get sign extension requirements for read */
/* TODO: Need to determine how sign extension is determined for
* reads
*/
vcpu->mmio.sign_extend_read = 0;
}
/* Get the guest physical address */
gpa = exec_vmread64(VMX_GUEST_PHYSICAL_ADDR_FULL);
TRACE_2L(TRC_VMEXIT_EPT_VIOLATION, exit_qual, gpa);
/* Adjust IPA appropriately and OR page offset to get full IPA of abort
*/
vcpu->mmio.paddr = gpa;
/* Check if the MMIO access has a HV registered handler */
status = check_hv_mmio_range((struct vm *) vcpu->vm, &vcpu->mmio);
if (status == true) {
/* Fetch and decode current vcpu instruction */
status = analyze_instruction(vcpu, &vcpu->mmio);
if (status != 0)
goto out;
if (vcpu->mmio.read_write == HV_MEM_IO_WRITE) {
status = emulate_instruction(vcpu, &vcpu->mmio);
if (status != 0)
goto out;
}
/* Call generic memory emulation handler
* For MMIO write, call hv_emulate_mmio after
* instruction emulation. For MMIO read,
* call hv_emulate_mmio at first.
*/
status = hv_emulate_mmio(vcpu, &vcpu->mmio);
if (vcpu->mmio.read_write == HV_MEM_IO_READ) {
/* Emulate instruction and update vcpu register set */
status = emulate_instruction(vcpu, &vcpu->mmio);
if (status != 0)
goto out;
}
} else {
/*
* No mmio handler from HV side, search from VHM in Dom0
*
* ACRN insert request to VHM and inject upcall
* For MMIO write, ask DM to run MMIO emulation after
* instruction emulation. For MMIO read, ask DM to run MMIO
* emulation at first.
*/
status = dm_emulate_mmio_pre(vcpu, exit_qual);
if (status != 0)
goto out;
status = acrn_insert_request_wait(vcpu, &vcpu->req);
}
return status;
out:
pr_fatal("Guest Linear Address: 0x%016llx",
exec_vmread(VMX_GUEST_LINEAR_ADDR));
pr_fatal("Guest Physical Address address: 0x%016llx",
gpa);
ASSERT(status == true, "EPT violation");
return status;
}
int ept_misconfig_handler(__unused struct vcpu *vcpu)
{
int status;
status = -EINVAL;
/* TODO - EPT Violation handler */
pr_info("%s, Guest linear address: 0x%016llx ",
__func__, exec_vmread64(VMX_GUEST_LINEAR_ADDR));
pr_info("%s, Guest physical address: 0x%016llx ",
__func__, exec_vmread64(VMX_GUEST_PHYSICAL_ADDR_FULL));
ASSERT(status == 0, "EPT Misconfiguration is not handled.\n");
TRACE_2L(TRC_VMEXIT_EPT_MISCONFIGURATION, 0, 0);
return status;
}
int ept_mmap(struct vm *vm, uint64_t hpa,
uint64_t gpa, uint64_t size, uint32_t type, uint32_t prot)
{
struct map_params map_params;
int i;
struct vcpu *vcpu;
/* Setup memory map parameters */
map_params.page_table_type = PT_EPT;
if (vm->arch_vm.ept) {
map_params.pml4_base = vm->arch_vm.ept;
map_params.pml4_inverted = vm->arch_vm.m2p;
} else {
map_params.pml4_base =
alloc_paging_struct();
vm->arch_vm.ept = map_params.pml4_base;
map_params.pml4_inverted = alloc_paging_struct();
vm->arch_vm.m2p = map_params.pml4_inverted;
}
if (type == MAP_MEM || type == MAP_MMIO) {
map_mem(&map_params, (void *)hpa,
(void *)gpa, size, prot);
} else if (type == MAP_UNMAP) {
unmap_mem(&map_params, (void *)hpa, (void *)gpa,
size, prot);
} else
ASSERT(0, "unknown map type");
foreach_vcpu(i, vm, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_TLB_FLUSH);
}
dev_dbg(ACRN_DBG_EPT, "ept map: %s hpa: 0x%016llx gpa: 0x%016llx ",
type == MAP_UNMAP ? "unmap" : "map", hpa, gpa);
dev_dbg(ACRN_DBG_EPT, "size: 0x%016llx prot: 0x%x\n", size, prot);
return 0;
}

84
hypervisor/arch/x86/gdt.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <cpu.h>
#include <gdt.h>
DEFINE_CPU_DATA(struct tss_64, tss);
DEFINE_CPU_DATA(struct host_gdt, gdt);
DEFINE_CPU_DATA(uint8_t[STACK_SIZE], mc_stack) __aligned(16);
DEFINE_CPU_DATA(uint8_t[STACK_SIZE], df_stack) __aligned(16);
DEFINE_CPU_DATA(uint8_t[STACK_SIZE], sf_stack) __aligned(16);
static void set_tss_desc(union tss_64_descriptor *desc,
void *tss, int tss_limit, int type)
{
uint32_t u1, u2, u3;
u1 = ((uint64_t)tss << 16) & 0xFFFFFFFF;
u2 = (uint64_t)tss & 0xFF000000;
u3 = ((uint64_t)tss & 0x00FF0000) >> 16;
desc->low32.value = u1 | (tss_limit & 0xFFFF);
desc->base_addr_63_32 = (uint32_t)((uint64_t)tss >> 32);
desc->high32.value = (u2 | ((uint32_t)type << 8) | 0x8000 | u3);
}
void load_gdtr_and_tr(void)
{
struct host_gdt *gdt = &get_cpu_var(gdt);
struct host_gdt_descriptor gdtr;
struct tss_64 *tss = &get_cpu_var(tss);
/* first entry is not used */
gdt->rsvd = 0xAAAAAAAAAAAAAAAA;
/* ring 0 code sel descriptor */
gdt->host_gdt_code_descriptor.value = 0x00Af9b000000ffff;
/* ring 0 data sel descriptor */
gdt->host_gdt_data_descriptor.value = 0x00cf93000000ffff;
tss->ist1 = (uint64_t)get_cpu_var(mc_stack) + STACK_SIZE;
tss->ist2 = (uint64_t)get_cpu_var(df_stack) + STACK_SIZE;
tss->ist3 = (uint64_t)get_cpu_var(sf_stack) + STACK_SIZE;
tss->ist4 = 0L;
/* tss descriptor */
set_tss_desc(&gdt->host_gdt_tss_descriptors,
(void *)tss, sizeof(struct tss_64), TSS_AVAIL);
gdtr.len = sizeof(struct host_gdt) - 1;
gdtr.gdt = gdt;
asm volatile ("lgdt %0" ::"m"(gdtr));
CPU_LTR_EXECUTE(HOST_GDT_RING0_CPU_TSS_SEL);
}

View File

@@ -0,0 +1,389 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_cfg.h>
#include <bsp_extern.h>
#include <acrn_hv_defs.h>
#include <hv_debug.h>
#include <multiboot.h>
#define BOOT_ARGS_LOAD_ADDR 0x24EFC000
#define ACRN_DBG_GUEST 6
/* for VM0 e820 */
uint32_t e820_entries;
struct e820_entry e820[E820_MAX_ENTRIES];
struct e820_mem_params e820_mem;
inline bool
is_vm0(struct vm *vm)
{
return (vm->attr.boot_idx & 0x7F) == 0;
}
inline struct vcpu *vcpu_from_vid(struct vm *vm, int vcpu_id)
{
int i;
struct vcpu *vcpu;
foreach_vcpu(i, vm, vcpu) {
if (vcpu->vcpu_id == vcpu_id)
return vcpu;
}
return NULL;
}
inline struct vcpu *vcpu_from_pid(struct vm *vm, int pcpu_id)
{
int i;
struct vcpu *vcpu;
foreach_vcpu(i, vm, vcpu) {
if (vcpu->pcpu_id == pcpu_id)
return vcpu;
}
return NULL;
}
inline struct vcpu *get_primary_vcpu(struct vm *vm)
{
int i;
struct vcpu *vcpu;
foreach_vcpu(i, vm, vcpu) {
if (is_vcpu_bsp(vcpu))
return vcpu;
}
return NULL;
}
inline uint64_t vcpumask2pcpumask(struct vm *vm, uint64_t vdmask)
{
int vcpu_id;
uint64_t dmask = 0;
struct vcpu *vcpu;
while ((vcpu_id = bitmap_ffs(&vdmask)) >= 0) {
bitmap_clr(vcpu_id, &vdmask);
vcpu = vcpu_from_vid(vm, vcpu_id);
ASSERT(vcpu, "vcpu_from_vid failed");
bitmap_set(vcpu->pcpu_id, &dmask);
}
return dmask;
}
inline bool vm_lapic_disabled(struct vm *vm)
{
int i;
struct vcpu *vcpu;
foreach_vcpu(i, vm, vcpu) {
if (vlapic_enabled(vcpu->arch_vcpu.vlapic))
return false;
}
return true;
}
int init_vm0_boot_info(struct vm *vm)
{
struct multiboot_module *mods = NULL;
struct multiboot_info *mbi = NULL;
if (!is_vm0(vm)) {
pr_err("just for vm0 to get info!");
return -EINVAL;
}
if (boot_regs[0] != MULTIBOOT_INFO_MAGIC) {
ASSERT(0, "no multiboot info found");
return -EINVAL;
}
mbi = (struct multiboot_info *)((uint64_t)boot_regs[1]);
dev_dbg(ACRN_DBG_GUEST, "Multiboot detected, flag=0x%x", mbi->mi_flags);
if (!(mbi->mi_flags & MULTIBOOT_INFO_HAS_MODS)) {
ASSERT(0, "no sos kernel info found");
return -EINVAL;
}
dev_dbg(ACRN_DBG_GUEST, "mod counts=%d\n", mbi->mi_mods_count);
/* mod[0] is for kernel&cmdline, other mod for ramdisk/firmware info*/
mods = (struct multiboot_module *)(uint64_t)mbi->mi_mods_addr;
dev_dbg(ACRN_DBG_GUEST, "mod0 start=0x%x, end=0x%x",
mods[0].mm_mod_start, mods[0].mm_mod_end);
dev_dbg(ACRN_DBG_GUEST, "cmd addr=0x%x, str=%s", mods[0].mm_string,
(char *) (uint64_t)mods[0].mm_string);
vm->sw.kernel_type = VM_LINUX_GUEST;
vm->sw.kernel_info.kernel_src_addr =
(void *)(uint64_t)mods[0].mm_mod_start;
vm->sw.kernel_info.kernel_size =
mods[0].mm_mod_end - mods[0].mm_mod_start;
vm->sw.kernel_info.kernel_load_addr =
(void *)(uint64_t)mods[0].mm_mod_start;
vm->sw.linux_info.bootargs_src_addr =
(void *)(uint64_t)mods[0].mm_string;
vm->sw.linux_info.bootargs_load_addr =
(void *)BOOT_ARGS_LOAD_ADDR;
vm->sw.linux_info.bootargs_size =
strnlen_s((char *)(uint64_t) mods[0].mm_string, MEM_2K);
return 0;
}
uint64_t gva2gpa(struct vm *vm, uint64_t cr3, uint64_t gva)
{
int level, index, shift;
uint64_t *base, addr, entry, page_size;
uint64_t gpa = 0;
addr = cr3;
for (level = 3; level >= 0; level--) {
addr = addr & IA32E_REF_MASK;
base = GPA2HVA(vm, addr);
ASSERT(base != NULL, "invalid ptp base.");
shift = level * 9 + 12;
index = (gva >> shift) & 0x1FF;
page_size = 1UL << shift;
entry = base[index];
if (level > 0 && (entry & MMU_32BIT_PDE_PS) != 0)
break;
addr = entry;
}
entry >>= shift; entry <<= (shift + 12); entry >>= 12;
gpa = entry | (gva & (page_size - 1));
return gpa;
}
void init_e820(void)
{
unsigned int i;
if (boot_regs[0] == MULTIBOOT_INFO_MAGIC) {
struct multiboot_info *mbi =
(struct multiboot_info *)((uint64_t)boot_regs[1]);
pr_info("Multiboot info detected\n");
if (mbi->mi_flags & 0x40) {
struct multiboot_mmap *mmap =
(struct multiboot_mmap *)
((uint64_t)mbi->mi_mmap_addr);
e820_entries = mbi->mi_mmap_length/
sizeof(struct multiboot_mmap);
if (e820_entries > E820_MAX_ENTRIES) {
pr_err("Too many E820 entries %d\n",
e820_entries);
e820_entries = E820_MAX_ENTRIES;
}
dev_dbg(ACRN_DBG_GUEST,
"mmap length 0x%x addr 0x%x entries %d\n",
mbi->mi_mmap_length, mbi->mi_mmap_addr,
e820_entries);
for (i = 0; i < e820_entries; i++) {
e820[i].baseaddr = mmap[i].baseaddr;
e820[i].length = mmap[i].length;
e820[i].type = mmap[i].type;
dev_dbg(ACRN_DBG_GUEST,
"mmap table: %d type: 0x%x\n",
i, mmap[i].type);
dev_dbg(ACRN_DBG_GUEST,
"Base: 0x%016llx length: 0x%016llx",
mmap[i].baseaddr, mmap[i].length);
}
}
} else
ASSERT(0, "no multiboot info found");
}
void obtain_e820_mem_info(void)
{
unsigned int i;
struct e820_entry *entry;
e820_mem.mem_bottom = UINT64_MAX;
e820_mem.mem_top = 0x00;
e820_mem.max_ram_blk_base = 0;
e820_mem.max_ram_blk_size = 0;
for (i = 0; i < e820_entries; i++) {
entry = &e820[i];
if (e820_mem.mem_bottom > entry->baseaddr)
e820_mem.mem_bottom = entry->baseaddr;
if (entry->baseaddr + entry->length
> e820_mem.mem_top) {
e820_mem.mem_top = entry->baseaddr
+ entry->length;
}
if (entry->baseaddr == UOS_DEFAULT_START_ADDR
&& entry->type == E820_TYPE_RAM) {
e820_mem.max_ram_blk_base =
entry->baseaddr;
e820_mem.max_ram_blk_size = entry->length;
}
}
}
static void rebuild_vm0_e820(void)
{
unsigned int i;
uint64_t entry_start;
uint64_t entry_end;
uint64_t hv_start = CONFIG_RAM_START;
uint64_t hv_end = hv_start + CONFIG_RAM_SIZE;
struct e820_entry *entry, new_entry = {0};
/* hypervisor mem need be filter out from e820 table
* it's hv itself + other hv reserved mem like vgt etc
*/
for (i = 0; i < e820_entries; i++) {
entry = &e820[i];
entry_start = entry->baseaddr;
entry_end = entry->baseaddr + entry->length;
/* No need handle in these cases*/
if (entry->type != E820_TYPE_RAM || entry_end <= hv_start
|| entry_start >= hv_end) {
continue;
}
/* filter out hv mem and adjust length of this entry*/
if (entry_start < hv_start && entry_end <= hv_end) {
entry->length = hv_start - entry_start;
continue;
}
/* filter out hv mem and need to create a new entry*/
if (entry_start < hv_start && entry_end > hv_end) {
entry->length = hv_start - entry_start;
new_entry.baseaddr = hv_end;
new_entry.length = entry_end - hv_end;
new_entry.type = E820_TYPE_RAM;
continue;
}
/* This entry is within the range of hv mem
* change to E820_TYPE_RESERVED
*/
if (entry_start >= hv_start && entry_end <= hv_end) {
entry->type = E820_TYPE_RESERVED;
continue;
}
if (entry_start >= hv_start && entry_start < hv_end
&& entry_end > hv_end) {
entry->baseaddr = hv_end;
entry->length = entry_end - hv_end;
continue;
}
}
if (new_entry.length > 0) {
e820_entries++;
ASSERT(e820_entries <= E820_MAX_ENTRIES,
"e820 entry overflow");
entry = &e820[e820_entries - 1];
entry->baseaddr = new_entry.baseaddr;
entry->length = new_entry.length;
entry->type = new_entry.type;
}
}
int prepare_vm0_memmap_and_e820(struct vm *vm)
{
unsigned int i;
uint32_t attr_wb = (MMU_MEM_ATTR_READ |
MMU_MEM_ATTR_WRITE |
MMU_MEM_ATTR_EXECUTE |
MMU_MEM_ATTR_WB_CACHE);
uint32_t attr_uc = (MMU_MEM_ATTR_READ |
MMU_MEM_ATTR_WRITE |
MMU_MEM_ATTR_EXECUTE |
MMU_MEM_ATTR_UNCACHED);
struct e820_entry *entry;
ASSERT(is_vm0(vm), "This func only for vm0");
rebuild_vm0_e820();
dev_dbg(ACRN_DBG_GUEST,
"vm0: bottom memory - 0x%llx, top memory - 0x%llx\n",
e820_mem.mem_bottom, e820_mem.mem_top);
/* create real ept map for all ranges with UC */
ept_mmap(vm, e820_mem.mem_bottom, e820_mem.mem_bottom,
(e820_mem.mem_top - e820_mem.mem_bottom),
MAP_MMIO, attr_uc);
/* update ram entries to WB attr */
for (i = 0; i < e820_entries; i++) {
entry = &e820[i];
if (entry->type == E820_TYPE_RAM)
ept_mmap(vm, entry->baseaddr, entry->baseaddr,
entry->length, MAP_MEM, attr_wb);
}
dev_dbg(ACRN_DBG_GUEST, "VM0 e820 layout:\n");
for (i = 0; i < e820_entries; i++) {
entry = &e820[i];
dev_dbg(ACRN_DBG_GUEST,
"e820 table: %d type: 0x%x", i, entry->type);
dev_dbg(ACRN_DBG_GUEST,
"BaseAddress: 0x%016llx length: 0x%016llx\n",
entry->baseaddr, entry->length);
}
/* unmap hypervisor itself for safety
* will cause EPT violation if sos accesses hv memory
*/
ept_mmap(vm, CONFIG_RAM_START, CONFIG_RAM_START,
CONFIG_RAM_SIZE, MAP_UNMAP, 0);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#ifndef _VMM_INSTRUCTION_EMUL_H_
#define _VMM_INSTRUCTION_EMUL_H_
/*
* Callback functions to read and write memory regions.
*/
typedef int (*mem_region_read_t)(struct vcpu *vcpu, uint64_t gpa,
uint64_t *rval, int rsize, void *arg);
typedef int (*mem_region_write_t)(struct vcpu *vcpu, uint64_t gpa,
uint64_t wval, int wsize, void *arg);
/*
* Emulate the decoded 'vie' instruction.
*
* The callbacks 'mrr' and 'mrw' emulate reads and writes to the memory region
* containing 'gpa'. 'mrarg' is an opaque argument that is passed into the
* callback functions.
*
* 'void *vm' should be 'struct vm *' when called from kernel context and
* 'struct vmctx *' when called from user context.
* s
*/
int vmm_emulate_instruction(struct vcpu *vcpu, uint64_t gpa, struct vie *vie,
struct vm_guest_paging *paging, mem_region_read_t mrr,
mem_region_write_t mrw, void *mrarg);
int vie_update_register(struct vcpu *vcpu, enum vm_reg_name reg,
uint64_t val, int size);
/*
* Returns 1 if an alignment check exception should be injected and 0 otherwise.
*/
int vie_alignment_check(int cpl, int operand_size, uint64_t cr0,
uint64_t rflags, uint64_t gla);
/* Returns 1 if the 'gla' is not canonical and 0 otherwise. */
int vie_canonical_check(enum vm_cpu_mode cpu_mode, uint64_t gla);
uint64_t vie_size2mask(int size);
int vie_calculate_gla(enum vm_cpu_mode cpu_mode, enum vm_reg_name seg,
struct seg_desc *desc, uint64_t off, int length, int addrsize, int prot,
uint64_t *gla);
void vie_init(struct vie *vie, const char *inst_bytes, int inst_length);
/*
* Decode the instruction fetched into 'vie' so it can be emulated.
*
* 'gla' is the guest linear address provided by the hardware assist
* that caused the nested page table fault. It is used to verify that
* the software instruction decoding is in agreement with the hardware.
*
* Some hardware assists do not provide the 'gla' to the hypervisor.
* To skip the 'gla' verification for this or any other reason pass
* in VIE_INVALID_GLA instead.
*/
#define VIE_INVALID_GLA (1UL << 63) /* a non-canonical address */
int vmm_decode_instruction(struct vcpu *vcpu, uint64_t gla,
enum vm_cpu_mode cpu_mode, int csd, struct vie *vie);
int emulate_instruction(struct vcpu *vcpu, struct mem_io *mmio);
int analyze_instruction(struct vcpu *vcpu, struct mem_io *mmio);
#endif /* _VMM_INSTRUCTION_EMUL_H_ */

View File

@@ -0,0 +1,466 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <hv_debug.h>
#include "instr_emul_wrapper.h"
#include "instr_emul.h"
struct emul_cnx {
struct vie vie;
struct vm_guest_paging paging;
struct vcpu *vcpu;
struct mem_io *mmio;
};
static DEFINE_CPU_DATA(struct emul_cnx, g_inst_ctxt);
static int
encode_vmcs_seg_desc(int seg, uint32_t *base, uint32_t *lim, uint32_t *acc);
static int32_t
get_vmcs_field(int ident);
static bool
is_segment_register(int reg);
static bool
is_descriptor_table(int reg);
int vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
{
struct run_context *cur_context;
if (!vcpu)
return -EINVAL;
if ((reg >= VM_REG_LAST) || (reg < VM_REG_GUEST_RAX))
return -EINVAL;
if ((reg >= VM_REG_GUEST_RAX) && (reg <= VM_REG_GUEST_RDI)) {
cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
*retval = cur_context->guest_cpu_regs.longs[reg];
} else if ((reg > VM_REG_GUEST_RDI) && (reg < VM_REG_LAST)) {
int32_t field = get_vmcs_field(reg);
if (field != -1)
*retval = exec_vmread(field);
else
return -EINVAL;
}
return 0;
}
int vm_set_register(struct vcpu *vcpu, int reg, uint64_t val)
{
struct run_context *cur_context;
if (!vcpu)
return -EINVAL;
if ((reg >= VM_REG_LAST) || (reg < VM_REG_GUEST_RAX))
return -EINVAL;
if ((reg >= VM_REG_GUEST_RAX) && (reg <= VM_REG_GUEST_RDI)) {
cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
cur_context->guest_cpu_regs.longs[reg] = val;
} else if ((reg > VM_REG_GUEST_RDI) && (reg < VM_REG_LAST)) {
int32_t field = get_vmcs_field(reg);
if (field != -1)
exec_vmwrite(field, val);
else
return -EINVAL;
}
return 0;
}
int vm_set_seg_desc(struct vcpu *vcpu, int seg, struct seg_desc *ret_desc)
{
int error;
uint32_t base, limit, access;
if ((!vcpu) || (!ret_desc))
return -EINVAL;
if (!is_segment_register(seg) && !is_descriptor_table(seg))
return -EINVAL;
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
if ((error != 0) || (access == 0xffffffff))
return -EINVAL;
exec_vmwrite(base, ret_desc->base);
exec_vmwrite(limit, ret_desc->limit);
exec_vmwrite(access, ret_desc->access);
return 0;
}
int vm_get_seg_desc(struct vcpu *vcpu, int seg, struct seg_desc *desc)
{
int error;
uint32_t base, limit, access;
if ((!vcpu) || (!desc))
return -EINVAL;
if (!is_segment_register(seg) && !is_descriptor_table(seg))
return -EINVAL;
error = encode_vmcs_seg_desc(seg, &base, &limit, &access);
if ((error != 0) || (access == 0xffffffff))
return -EINVAL;
desc->base = exec_vmread(base);
desc->limit = exec_vmread(limit);
desc->access = exec_vmread(access);
return 0;
}
int vm_restart_instruction(struct vcpu *vcpu)
{
if (!vcpu)
return -EINVAL;
VCPU_RETAIN_RIP(vcpu);
return 0;
}
static bool is_descriptor_table(int reg)
{
switch (reg) {
case VM_REG_GUEST_IDTR:
case VM_REG_GUEST_GDTR:
return true;
default:
return false;
}
}
static bool is_segment_register(int reg)
{
switch (reg) {
case VM_REG_GUEST_ES:
case VM_REG_GUEST_CS:
case VM_REG_GUEST_SS:
case VM_REG_GUEST_DS:
case VM_REG_GUEST_FS:
case VM_REG_GUEST_GS:
case VM_REG_GUEST_TR:
case VM_REG_GUEST_LDTR:
return true;
default:
return false;
}
}
static int encode_vmcs_seg_desc(int seg, uint32_t *base, uint32_t *lim,
uint32_t *acc)
{
switch (seg) {
case VM_REG_GUEST_ES:
*base = VMX_GUEST_ES_BASE;
*lim = VMX_GUEST_ES_LIMIT;
*acc = VMX_GUEST_ES_ATTR;
break;
case VM_REG_GUEST_CS:
*base = VMX_GUEST_CS_BASE;
*lim = VMX_GUEST_CS_LIMIT;
*acc = VMX_GUEST_CS_ATTR;
break;
case VM_REG_GUEST_SS:
*base = VMX_GUEST_SS_BASE;
*lim = VMX_GUEST_SS_LIMIT;
*acc = VMX_GUEST_SS_ATTR;
break;
case VM_REG_GUEST_DS:
*base = VMX_GUEST_DS_BASE;
*lim = VMX_GUEST_DS_LIMIT;
*acc = VMX_GUEST_DS_ATTR;
break;
case VM_REG_GUEST_FS:
*base = VMX_GUEST_FS_BASE;
*lim = VMX_GUEST_FS_LIMIT;
*acc = VMX_GUEST_FS_ATTR;
break;
case VM_REG_GUEST_GS:
*base = VMX_GUEST_GS_BASE;
*lim = VMX_GUEST_GS_LIMIT;
*acc = VMX_GUEST_GS_ATTR;
break;
case VM_REG_GUEST_TR:
*base = VMX_GUEST_TR_BASE;
*lim = VMX_GUEST_TR_LIMIT;
*acc = VMX_GUEST_TR_ATTR;
break;
case VM_REG_GUEST_LDTR:
*base = VMX_GUEST_LDTR_BASE;
*lim = VMX_GUEST_LDTR_LIMIT;
*acc = VMX_GUEST_LDTR_ATTR;
break;
case VM_REG_GUEST_IDTR:
*base = VMX_GUEST_IDTR_BASE;
*lim = VMX_GUEST_IDTR_LIMIT;
*acc = 0xffffffff;
break;
case VM_REG_GUEST_GDTR:
*base = VMX_GUEST_GDTR_BASE;
*lim = VMX_GUEST_GDTR_LIMIT;
*acc = 0xffffffff;
break;
default:
return -EINVAL;
}
return 0;
}
static int32_t get_vmcs_field(int ident)
{
switch (ident) {
case VM_REG_GUEST_CR0:
return VMX_GUEST_CR0;
case VM_REG_GUEST_CR3:
return VMX_GUEST_CR3;
case VM_REG_GUEST_CR4:
return VMX_GUEST_CR4;
case VM_REG_GUEST_DR7:
return VMX_GUEST_DR7;
case VM_REG_GUEST_RSP:
return VMX_GUEST_RSP;
case VM_REG_GUEST_RIP:
return VMX_GUEST_RIP;
case VM_REG_GUEST_RFLAGS:
return VMX_GUEST_RFLAGS;
case VM_REG_GUEST_ES:
return VMX_GUEST_ES_SEL;
case VM_REG_GUEST_CS:
return VMX_GUEST_CS_SEL;
case VM_REG_GUEST_SS:
return VMX_GUEST_SS_SEL;
case VM_REG_GUEST_DS:
return VMX_GUEST_DS_SEL;
case VM_REG_GUEST_FS:
return VMX_GUEST_FS_SEL;
case VM_REG_GUEST_GS:
return VMX_GUEST_GS_SEL;
case VM_REG_GUEST_TR:
return VMX_GUEST_TR_SEL;
case VM_REG_GUEST_LDTR:
return VMX_GUEST_LDTR_SEL;
case VM_REG_GUEST_EFER:
return VMX_GUEST_IA32_EFER_FULL;
case VM_REG_GUEST_PDPTE0:
return VMX_GUEST_PDPTE0_FULL;
case VM_REG_GUEST_PDPTE1:
return VMX_GUEST_PDPTE1_FULL;
case VM_REG_GUEST_PDPTE2:
return VMX_GUEST_PDPTE2_FULL;
case VM_REG_GUEST_PDPTE3:
return VMX_GUEST_PDPTE3_FULL;
default:
return -1;
}
}
static enum vm_cpu_mode get_vmx_cpu_mode(void)
{
uint32_t csar;
if (exec_vmread(VMX_GUEST_IA32_EFER_FULL) & EFER_LMA) {
csar = exec_vmread(VMX_GUEST_CS_ATTR);
if (csar & 0x2000)
return CPU_MODE_64BIT; /* CS.L = 1 */
else
return CPU_MODE_COMPATIBILITY;
} else if (exec_vmread(VMX_GUEST_CR0) & CR0_PE) {
return CPU_MODE_PROTECTED;
} else {
return CPU_MODE_REAL;
}
}
static void get_guest_paging_info(struct vcpu *vcpu, struct emul_cnx *emul_cnx)
{
uint32_t cpl, csar;
ASSERT(emul_cnx != NULL && vcpu != NULL, "Error in input arguments");
csar = exec_vmread(VMX_GUEST_CS_ATTR);
cpl = (csar >> 5) & 3;
emul_cnx->paging.cr3 =
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3;
emul_cnx->paging.cpl = cpl;
emul_cnx->paging.cpu_mode = get_vmx_cpu_mode();
emul_cnx->paging.paging_mode = PAGING_MODE_FLAT;/*maybe change later*/
}
static int mmio_read(struct vcpu *vcpu, __unused uint64_t gpa, uint64_t *rval,
__unused int size, __unused void *arg)
{
struct emul_cnx *emul_cnx;
struct mem_io *mmio;
if (!vcpu)
return -EINVAL;
emul_cnx = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
mmio = emul_cnx->mmio;
ASSERT(mmio != NULL, "invalid mmio when reading");
*rval = mmio->value;
return 0;
}
static int mmio_write(struct vcpu *vcpu, __unused uint64_t gpa, uint64_t wval,
__unused int size, __unused void *arg)
{
struct emul_cnx *emul_cnx;
struct mem_io *mmio;
if (!vcpu)
return -EINVAL;
emul_cnx = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
mmio = emul_cnx->mmio;
ASSERT(mmio != NULL, "invalid mmio when writing");
mmio->value = wval;
return 0;
}
void vm_gva2gpa(struct vcpu *vcpu, uint64_t gva, uint64_t *gpa)
{
ASSERT(gpa != NULL, "Error in input arguments");
ASSERT(vcpu != NULL,
"Invalid vcpu id when gva2gpa");
*gpa = gva2gpa(vcpu->vm,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3, gva);
}
int analyze_instruction(struct vcpu *vcpu, struct mem_io *mmio)
{
uint64_t guest_rip_gva, guest_rip_gpa;
char *guest_rip_hva;
struct emul_cnx *emul_cnx;
uint32_t csar;
int retval = 0;
enum vm_cpu_mode cpu_mode;
int i;
guest_rip_gva =
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].rip;
guest_rip_gpa = gva2gpa(vcpu->vm,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3,
guest_rip_gva);
guest_rip_hva = GPA2HVA(vcpu->vm, guest_rip_gpa);
emul_cnx = &per_cpu(g_inst_ctxt, vcpu->pcpu_id);
emul_cnx->mmio = mmio;
emul_cnx->vcpu = vcpu;
/* by now, HVA <-> HPA is 1:1 mapping, so use hpa is OK*/
vie_init(&emul_cnx->vie, guest_rip_hva,
vcpu->arch_vcpu.inst_len);
get_guest_paging_info(vcpu, emul_cnx);
csar = exec_vmread(VMX_GUEST_CS_ATTR);
cpu_mode = get_vmx_cpu_mode();
mmio->private_data = emul_cnx;
retval = vmm_decode_instruction(vcpu, guest_rip_gva,
cpu_mode, SEG_DESC_DEF32(csar), &emul_cnx->vie);
mmio->access_size = emul_cnx->vie.opsize;
if (retval != 0) {
/* dump to instruction when decoding failed */
pr_err("decode following instruction failed @ 0x%016llx:",
exec_vmread(VMX_GUEST_RIP));
for (i = 0; i < emul_cnx->vie.num_valid; i++) {
if (i >= VIE_INST_SIZE)
break;
if (i == 0)
pr_err("\n");
pr_err("%d=%02hhx ",
i, emul_cnx->vie.inst[i]);
}
}
return retval;
}
int emulate_instruction(struct vcpu *vcpu, struct mem_io *mmio)
{
struct emul_cnx *emul_cnx = (struct emul_cnx *)(mmio->private_data);
struct vm_guest_paging *paging = &emul_cnx->paging;
int i, retval = 0;
uint64_t gpa = mmio->paddr;
mem_region_read_t mread = mmio_read;
mem_region_write_t mwrite = mmio_write;
retval = vmm_emulate_instruction(vcpu, gpa,
&emul_cnx->vie, paging, mread, mwrite, &retval);
if (retval != 0) {
/* dump to instruction when emulation failed */
pr_err("emulate following instruction failed @ 0x%016llx:",
exec_vmread(VMX_GUEST_RIP));
for (i = 0; i < emul_cnx->vie.num_valid; i++) {
if (i >= VIE_INST_SIZE)
break;
if (i == 0)
pr_err("\n");
pr_err("%d=%02hhx ",
i, emul_cnx->vie.inst[i]);
}
}
return retval;
}

View File

@@ -0,0 +1,203 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#include <cpu.h>
struct vie_op {
uint8_t op_byte; /* actual opcode byte */
uint8_t op_type; /* type of operation (e.g. MOV) */
uint16_t op_flags;
};
#define VIE_INST_SIZE 15
struct vie {
uint8_t inst[VIE_INST_SIZE]; /* instruction bytes */
uint8_t num_valid; /* size of the instruction */
uint8_t num_processed;
uint8_t addrsize:4, opsize:4; /* address and operand sizes */
uint8_t rex_w:1, /* REX prefix */
rex_r:1,
rex_x:1,
rex_b:1,
rex_present:1,
repz_present:1, /* REP/REPE/REPZ prefix */
repnz_present:1, /* REPNE/REPNZ prefix */
opsize_override:1, /* Operand size override */
addrsize_override:1, /* Address size override */
segment_override:1; /* Segment override */
uint8_t mod:2, /* ModRM byte */
reg:4,
rm:4;
uint8_t ss:2, /* SIB byte */
index:4,
base:4;
uint8_t disp_bytes;
uint8_t imm_bytes;
uint8_t scale;
int base_register; /* VM_REG_GUEST_xyz */
int index_register; /* VM_REG_GUEST_xyz */
int segment_register; /* VM_REG_GUEST_xyz */
int64_t displacement; /* optional addr displacement */
int64_t immediate; /* optional immediate operand */
uint8_t decoded; /* set to 1 if successfully decoded */
struct vie_op op; /* opcode description */
};
#define PSL_C 0x00000001 /* carry bit */
#define PSL_PF 0x00000004 /* parity bit */
#define PSL_AF 0x00000010 /* bcd carry bit */
#define PSL_Z 0x00000040 /* zero bit */
#define PSL_N 0x00000080 /* negative bit */
#define PSL_T 0x00000100 /* trace enable bit */
#define PSL_I 0x00000200 /* interrupt enable bit */
#define PSL_D 0x00000400 /* string instruction direction bit */
#define PSL_V 0x00000800 /* overflow bit */
#define PSL_IOPL 0x00003000 /* i/o privilege level */
#define PSL_NT 0x00004000 /* nested task bit */
#define PSL_RF 0x00010000 /* resume flag bit */
#define PSL_VM 0x00020000 /* virtual 8086 mode bit */
#define PSL_AC 0x00040000 /* alignment checking */
#define PSL_VIF 0x00080000 /* virtual interrupt enable */
#define PSL_VIP 0x00100000 /* virtual interrupt pending */
#define PSL_ID 0x00200000 /* identification bit */
/*
* The 'access' field has the format specified in Table 21-2 of the Intel
* Architecture Manual vol 3b.
*
* XXX The contents of the 'access' field are architecturally defined except
* bit 16 - Segment Unusable.
*/
struct seg_desc {
uint64_t base;
uint32_t limit;
uint32_t access;
};
/*
* Protections are chosen from these bits, or-ed together
*/
#define PROT_NONE 0x00 /* no permissions */
#define PROT_READ 0x01 /* pages can be read */
#define PROT_WRITE 0x02 /* pages can be written */
#define PROT_EXEC 0x04 /* pages can be executed */
#define SEG_DESC_TYPE(access) ((access) & 0x001f)
#define SEG_DESC_DPL(access) (((access) >> 5) & 0x3)
#define SEG_DESC_PRESENT(access) (((access) & 0x0080) ? 1 : 0)
#define SEG_DESC_DEF32(access) (((access) & 0x4000) ? 1 : 0)
#define SEG_DESC_GRANULARITY(access) (((access) & 0x8000) ? 1 : 0)
#define SEG_DESC_UNUSABLE(access) (((access) & 0x10000) ? 1 : 0)
enum vm_cpu_mode {
CPU_MODE_REAL,
CPU_MODE_PROTECTED,
CPU_MODE_COMPATIBILITY, /* IA-32E mode (CS.L = 0) */
CPU_MODE_64BIT, /* IA-32E mode (CS.L = 1) */
};
enum vm_paging_mode {
PAGING_MODE_FLAT,
PAGING_MODE_32,
PAGING_MODE_PAE,
PAGING_MODE_64,
};
struct vm_guest_paging {
uint64_t cr3;
int cpl;
enum vm_cpu_mode cpu_mode;
enum vm_paging_mode paging_mode;
};
/*
* Identifiers for architecturally defined registers.
*/
enum vm_reg_name {
VM_REG_GUEST_RAX,
VM_REG_GUEST_RBX,
VM_REG_GUEST_RCX,
VM_REG_GUEST_RDX,
VM_REG_GUEST_RBP,
VM_REG_GUEST_RSI,
VM_REG_GUEST_R8,
VM_REG_GUEST_R9,
VM_REG_GUEST_R10,
VM_REG_GUEST_R11,
VM_REG_GUEST_R12,
VM_REG_GUEST_R13,
VM_REG_GUEST_R14,
VM_REG_GUEST_R15,
VM_REG_GUEST_RDI,
VM_REG_GUEST_CR0,
VM_REG_GUEST_CR3,
VM_REG_GUEST_CR4,
VM_REG_GUEST_DR7,
VM_REG_GUEST_RSP,
VM_REG_GUEST_RIP,
VM_REG_GUEST_RFLAGS,
VM_REG_GUEST_ES,
VM_REG_GUEST_CS,
VM_REG_GUEST_SS,
VM_REG_GUEST_DS,
VM_REG_GUEST_FS,
VM_REG_GUEST_GS,
VM_REG_GUEST_LDTR,
VM_REG_GUEST_TR,
VM_REG_GUEST_IDTR,
VM_REG_GUEST_GDTR,
VM_REG_GUEST_EFER,
VM_REG_GUEST_CR2,
VM_REG_GUEST_PDPTE0,
VM_REG_GUEST_PDPTE1,
VM_REG_GUEST_PDPTE2,
VM_REG_GUEST_PDPTE3,
VM_REG_GUEST_INTR_SHADOW,
VM_REG_LAST
};
typedef unsigned long u_long;
int vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval);
int vm_set_register(struct vcpu *vcpu, int reg, uint64_t val);
int vm_get_seg_desc(struct vcpu *vcpu, int reg,
struct seg_desc *ret_desc);
int vm_set_seg_desc(struct vcpu *vcpu, int reg,
struct seg_desc *desc);
int vm_restart_instruction(struct vcpu *vcpu);
void vm_gva2gpa(struct vcpu *vcpu, uint64_t gla, uint64_t *gpa);

View File

@@ -0,0 +1,118 @@
/*-
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
* Copyright (c) 2018 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 University 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 REGENTS 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 REGENTS 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.
*
* @(#)time.h 8.5 (Berkeley) 5/4/95
* $FreeBSD$
*/
#ifndef _TIME_H_
#define _TIME_H_
struct callout {
void *c_arg; /* function argument */
void (*c_func)(void *); /* function to call */
short c_flags; /* User State */
};
#define CALLOUT_ACTIVE 0x0002 /* callout is currently active */
#define CALLOUT_PENDING 0x0004 /* callout is waiting for timeout */
#define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE)
#define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE)
#define callout_pending(c) ((c)->c_flags & CALLOUT_PENDING)
typedef int64_t time_t;
typedef int64_t sbintime_t;
struct bintime {
time_t sec;
uint64_t frac;
};
static inline void
bintime_add(struct bintime *_bt, const struct bintime *_bt2)
{
uint64_t _u;
_u = _bt->frac;
_bt->frac += _bt2->frac;
if (_u > _bt->frac)
_bt->sec++;
_bt->sec += _bt2->sec;
}
static inline void
bintime_sub(struct bintime *_bt, const struct bintime *_bt2)
{
uint64_t _u;
_u = _bt->frac;
_bt->frac -= _bt2->frac;
if (_u < _bt->frac)
_bt->sec--;
_bt->sec -= _bt2->sec;
}
static inline void
bintime_mul(struct bintime *_bt, uint32_t _x)
{
uint64_t _p1, _p2;
_p1 = (_bt->frac & 0xffffffffull) * _x;
_p2 = (_bt->frac >> 32) * _x + (_p1 >> 32);
_bt->sec *= _x;
_bt->sec += (_p2 >> 32);
_bt->frac = (_p2 << 32) | (_p1 & 0xffffffffull);
}
#define bintime_cmp(a, b, cmp) \
(((a)->sec == (b)->sec) ? \
((a)->frac cmp(b)->frac) : \
((a)->sec cmp(b)->sec))
#define SBT_1S ((sbintime_t)1 << 32)
#define SBT_1US (SBT_1S / 1000000)
#define BT2FREQ(bt) \
(((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) / \
((bt)->frac >> 1))
#define FREQ2BT(freq, bt) \
{ \
(bt)->sec = 0; \
(bt)->frac = ((uint64_t)0x8000000000000000 / (freq)) << 1; \
}
static inline sbintime_t
bttosbt(const struct bintime _bt)
{
return (((sbintime_t)_bt.sec << 32) + (_bt.frac >> 32));
}
#endif /* !_TIME_H_ */

View File

@@ -0,0 +1,357 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <schedule.h>
#include <hv_debug.h>
vm_sw_loader_t vm_sw_loader;
/***********************************************************************
* vcpu_id/pcpu_id mapping table:
*
* if
* VM0_CPUS[2] = {0, 2} , VM1_CPUS[2] = {3, 1};
* then
* for physical CPU 0 : vcpu->pcpu_id = 0, vcpu->vcpu_id = 0, vmid = 0;
* for physical CPU 2 : vcpu->pcpu_id = 2, vcpu->vcpu_id = 1, vmid = 0;
* for physical CPU 3 : vcpu->pcpu_id = 3, vcpu->vcpu_id = 0, vmid = 1;
* for physical CPU 1 : vcpu->pcpu_id = 1, vcpu->vcpu_id = 1, vmid = 1;
*
***********************************************************************/
int create_vcpu(int cpu_id, struct vm *vm, struct vcpu **rtn_vcpu_handle)
{
struct vcpu *vcpu;
ASSERT(vm != NULL, "");
ASSERT(rtn_vcpu_handle != NULL, "");
pr_info("Creating VCPU %d", cpu_id);
/* Allocate memory for VCPU */
vcpu = calloc(1, sizeof(struct vcpu));
ASSERT(vcpu != NULL, "");
/* Initialize the physical CPU ID for this VCPU */
vcpu->pcpu_id = cpu_id;
/* Initialize the parent VM reference */
vcpu->vm = vm;
/* Initialize the virtual ID for this VCPU */
/* FIXME:
* We have assumption that we always destroys vcpus in one
* shot (like when vm is destroyed). If we need to support
* specific vcpu destroy on fly, this vcpu_id assignment
* needs revise.
*/
/*
* vcpu->vcpu_id = vm->hw.created_vcpus;
* vm->hw.created_vcpus++;
*/
vcpu->vcpu_id = atomic_xadd_int(&vm->hw.created_vcpus, 1);
/* vm->hw.vcpu_array[vcpu->vcpu_id] = vcpu; */
atomic_store_rel_64(
(unsigned long *)&vm->hw.vcpu_array[vcpu->vcpu_id],
(unsigned long)vcpu);
ASSERT(vcpu->vcpu_id < vm->hw.num_vcpus,
"Allocated vcpu_id is out of range!");
per_cpu(vcpu, cpu_id) = vcpu;
pr_info("PCPU%d is working as VM%d VCPU%d, Role: %s",
vcpu->pcpu_id, vcpu->vm->attr.id, vcpu->vcpu_id,
is_vcpu_bsp(vcpu) ? "PRIMARY" : "SECONDARY");
/* Is this VCPU a VM BSP, create page hierarchy for this VM */
if (is_vcpu_bsp(vcpu)) {
/* Set up temporary guest page tables */
vm->arch_vm.guest_pml4 = create_guest_paging(vm);
pr_info("VM *d VCPU %d CR3: 0x%016llx ",
vm->attr.id, vcpu->vcpu_id, vm->arch_vm.guest_pml4);
}
/* Allocate VMCS region for this VCPU */
vcpu->arch_vcpu.vmcs = alloc_page();
ASSERT(vcpu->arch_vcpu.vmcs != NULL, "");
/* Memset VMCS region for this VCPU */
memset(vcpu->arch_vcpu.vmcs, 0, CPU_PAGE_SIZE);
/* Initialize exception field in VCPU context */
vcpu->arch_vcpu.exception_info.exception = -1;
/* Initialize cur context */
vcpu->arch_vcpu.cur_context = NORMAL_WORLD;
/* Create per vcpu vlapic */
vlapic_create(vcpu);
/* Populate the return handle */
*rtn_vcpu_handle = vcpu;
vcpu->launched = false;
vcpu->paused_cnt = 0;
vcpu->running = 0;
vcpu->ioreq_pending = 0;
vcpu->arch_vcpu.nr_sipi = 0;
vcpu->pending_pre_work = 0;
vcpu->state = VCPU_INIT;
return 0;
}
int start_vcpu(struct vcpu *vcpu)
{
uint64_t rip, instlen;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
int64_t status = 0;
ASSERT(vcpu != NULL, "Incorrect arguments");
/* If this VCPU is not already launched, launch it */
if (!vcpu->launched) {
pr_info("VM %d Starting VCPU %d",
vcpu->vm->attr.id, vcpu->vcpu_id);
/* Set vcpu launched */
vcpu->launched = true;
/* avoid VMCS recycling RSB usage, set IBPB.
* NOTE: this should be done for any time vmcs got switch
* currently, there is no other place to do vmcs switch
* Please add IBPB set for future vmcs switch case(like trusty)
*/
if (ibrs_type == IBRS_RAW)
msr_write(MSR_IA32_PRED_CMD, PRED_SET_IBPB);
/* Launch the VM */
status = vmx_vmrun(cur_context, VM_LAUNCH, ibrs_type);
/* See if VM launched successfully */
if (status == 0) {
if (is_vcpu_bsp(vcpu)) {
pr_info("VM %d VCPU %d successfully launched",
vcpu->vm->attr.id, vcpu->vcpu_id);
}
}
} else {
/* This VCPU was already launched, check if the last guest
* instruction needs to be repeated and resume VCPU accordingly
*/
instlen = vcpu->arch_vcpu.inst_len;
rip = cur_context->rip;
exec_vmwrite(VMX_GUEST_RIP, ((rip + instlen) &
0xFFFFFFFFFFFFFFFF));
/* Resume the VM */
status = vmx_vmrun(cur_context, VM_RESUME, ibrs_type);
}
/* Save guest CR3 register */
cur_context->cr3 = exec_vmread(VMX_GUEST_CR3);
/* Obtain current VCPU instruction pointer and length */
cur_context->rip = exec_vmread(VMX_GUEST_RIP);
vcpu->arch_vcpu.inst_len = exec_vmread(VMX_EXIT_INSTR_LEN);
cur_context->rsp = exec_vmread(VMX_GUEST_RSP);
cur_context->rflags = exec_vmread(VMX_GUEST_RFLAGS);
/* Obtain VM exit reason */
vcpu->arch_vcpu.exit_reason = exec_vmread(VMX_EXIT_REASON);
if (status != 0) {
/* refer to 64-ia32 spec section 24.9.1 volume#3 */
if (vcpu->arch_vcpu.exit_reason & VMX_VMENTRY_FAIL)
pr_fatal("vmentry fail reason=%lx", vcpu->arch_vcpu.exit_reason);
else
pr_fatal("vmexit fail err_inst=%lx", exec_vmread(VMX_INSTR_ERROR));
ASSERT(status == 0, "vm fail");
}
return status;
}
int shutdown_vcpu(__unused struct vcpu *vcpu)
{
/* TODO : Implement VCPU shutdown sequence */
return 0;
}
int destroy_vcpu(struct vcpu *vcpu)
{
ASSERT(vcpu != NULL, "Incorrect arguments");
/* vcpu->vm->hw.vcpu_array[vcpu->vcpu_id] = NULL; */
atomic_store_rel_64(
(unsigned long *)&vcpu->vm->hw.vcpu_array[vcpu->vcpu_id],
(unsigned long)NULL);
atomic_subtract_int(&vcpu->vm->hw.created_vcpus, 1);
vlapic_free(vcpu);
free(vcpu->arch_vcpu.vmcs);
free(vcpu->guest_msrs);
free_pcpu(vcpu->pcpu_id);
free(vcpu);
return 0;
}
/* NOTE:
* vcpu should be paused before call this function.
*/
void reset_vcpu(struct vcpu *vcpu)
{
struct vlapic *vlapic;
pr_dbg("vcpu%d reset", vcpu->vcpu_id);
ASSERT(vcpu->state != VCPU_RUNNING,
"reset vcpu when it's running");
if (vcpu->state == VCPU_INIT)
return;
vcpu->state = VCPU_INIT;
vcpu->launched = false;
vcpu->paused_cnt = 0;
vcpu->running = 0;
vcpu->ioreq_pending = 0;
vcpu->arch_vcpu.nr_sipi = 0;
vcpu->pending_pre_work = 0;
vlapic = vcpu->arch_vcpu.vlapic;
vlapic_init(vlapic);
}
void init_vcpu(struct vcpu *vcpu)
{
if (is_vcpu_bsp(vcpu))
vcpu->arch_vcpu.cpu_mode = PAGE_PROTECTED_MODE;
else
vcpu->arch_vcpu.cpu_mode = REAL_MODE;
/* init_vmcs is delayed to vcpu vmcs launch first time */
}
void pause_vcpu(struct vcpu *vcpu, enum vcpu_state new_state)
{
int pcpu_id = get_cpu_id();
pr_dbg("vcpu%d paused, new state: %d",
vcpu->vcpu_id, new_state);
vcpu->prev_state = vcpu->state;
vcpu->state = new_state;
get_schedule_lock(pcpu_id);
if (atomic_load_acq_32(&vcpu->running) == 1) {
remove_vcpu_from_runqueue(vcpu);
make_reschedule_request(vcpu);
release_schedule_lock(pcpu_id);
if (vcpu->pcpu_id != pcpu_id) {
while (atomic_load_acq_32(&vcpu->running) == 1)
__asm__ __volatile("pause" ::: "memory");
}
} else {
remove_vcpu_from_runqueue(vcpu);
release_schedule_lock(pcpu_id);
}
}
void resume_vcpu(struct vcpu *vcpu)
{
pr_dbg("vcpu%d resumed", vcpu->vcpu_id);
vcpu->state = vcpu->prev_state;
get_schedule_lock(vcpu->pcpu_id);
if (vcpu->state == VCPU_RUNNING) {
add_vcpu_to_runqueue(vcpu);
make_reschedule_request(vcpu);
}
release_schedule_lock(vcpu->pcpu_id);
}
void schedule_vcpu(struct vcpu *vcpu)
{
vcpu->state = VCPU_RUNNING;
pr_dbg("vcpu%d scheduled", vcpu->vcpu_id);
get_schedule_lock(vcpu->pcpu_id);
add_vcpu_to_runqueue(vcpu);
make_reschedule_request(vcpu);
release_schedule_lock(vcpu->pcpu_id);
}
/* help function for vcpu create */
int prepare_vcpu(struct vm *vm, int pcpu_id)
{
int ret = 0;
struct vcpu *vcpu = NULL;
ret = create_vcpu(pcpu_id, vm, &vcpu);
ASSERT(ret == 0, "vcpu create failed");
if (is_vcpu_bsp(vcpu)) {
/* Load VM SW */
if (!vm_sw_loader)
vm_sw_loader = general_sw_loader;
vm_sw_loader(vm, vcpu);
vcpu->arch_vcpu.cpu_mode = PAGE_PROTECTED_MODE;
} else {
vcpu->arch_vcpu.cpu_mode = REAL_MODE;
}
/* init_vmcs is delayed to vcpu vmcs launch first time */
/* initialize the vcpu tsc aux */
vcpu->msr_tsc_aux_guest = vcpu->vcpu_id;
set_pcpu_used(pcpu_id);
INIT_LIST_HEAD(&vcpu->run_list);
return ret;
}
void request_vcpu_pre_work(struct vcpu *vcpu, int pre_work_id)
{
bitmap_set(pre_work_id, &vcpu->pending_pre_work);
}

View File

@@ -0,0 +1,662 @@
/*-
* Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#define pr_fmt(fmt) "vioapic: " fmt
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define IOREGSEL 0x00
#define IOWIN 0x10
#define IOEOI 0x40
#define REDIR_ENTRIES_HW 120 /* SOS align with native ioapic */
#define REDIR_ENTRIES_UOS 24 /* UOS pins*/
#define RTBL_RO_BITS ((uint64_t)(IOAPIC_RTE_REM_IRR | IOAPIC_RTE_DELIVS))
#define ACRN_DBG_IOAPIC 6
struct vioapic {
struct vm *vm;
spinlock_t mtx;
uint32_t id;
uint32_t ioregsel;
struct {
uint64_t reg;
int acnt; /* sum of pin asserts (+1) and deasserts (-1) */
} rtbl[REDIR_ENTRIES_HW];
};
#define VIOAPIC_LOCK(vioapic) spinlock_obtain(&((vioapic)->mtx))
#define VIOAPIC_UNLOCK(vioapic) spinlock_release(&((vioapic)->mtx))
static inline const char *pinstate_str(bool asserted)
{
return (asserted) ? "asserted" : "deasserted";
}
struct vioapic *
vm_ioapic(struct vm *vm)
{
return (struct vioapic *)vm->arch_vm.virt_ioapic;
}
static void
vioapic_send_intr(struct vioapic *vioapic, int pin)
{
int vector, delmode;
uint32_t low, high, dest;
bool level, phys;
if (pin < 0 || pin >= vioapic_pincount(vioapic->vm))
pr_err("vioapic_send_intr: invalid pin number %d", pin);
low = vioapic->rtbl[pin].reg;
high = vioapic->rtbl[pin].reg >> 32;
if ((low & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET) {
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%d: masked", pin);
return;
}
phys = ((low & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
delmode = low & IOAPIC_RTE_DELMOD;
level = low & IOAPIC_RTE_TRGRLVL ? true : false;
if (level)
vioapic->rtbl[pin].reg |= IOAPIC_RTE_REM_IRR;
vector = low & IOAPIC_RTE_INTVEC;
dest = high >> APIC_ID_SHIFT;
vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
}
static void
vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
{
int oldcnt, newcnt;
bool needintr;
if (pin < 0 || pin >= vioapic_pincount(vioapic->vm))
pr_err("vioapic_set_pinstate: invalid pin number %d", pin);
oldcnt = vioapic->rtbl[pin].acnt;
if (newstate)
vioapic->rtbl[pin].acnt++;
else
vioapic->rtbl[pin].acnt--;
newcnt = vioapic->rtbl[pin].acnt;
if (newcnt < 0) {
pr_err("ioapic pin%d: bad acnt %d", pin, newcnt);
}
needintr = false;
if (oldcnt == 0 && newcnt == 1) {
needintr = true;
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%d: asserted", pin);
} else if (oldcnt == 1 && newcnt == 0) {
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%d: deasserted", pin);
} else {
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%d: %s, ignored, acnt %d",
pin, pinstate_str(newstate), newcnt);
}
if (needintr)
vioapic_send_intr(vioapic, pin);
}
enum irqstate {
IRQSTATE_ASSERT,
IRQSTATE_DEASSERT,
IRQSTATE_PULSE
};
static int
vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
{
struct vioapic *vioapic;
if (irq < 0 || irq >= vioapic_pincount(vm))
return -EINVAL;
vioapic = vm_ioapic(vm);
VIOAPIC_LOCK(vioapic);
switch (irqstate) {
case IRQSTATE_ASSERT:
vioapic_set_pinstate(vioapic, irq, true);
break;
case IRQSTATE_DEASSERT:
vioapic_set_pinstate(vioapic, irq, false);
break;
case IRQSTATE_PULSE:
vioapic_set_pinstate(vioapic, irq, true);
vioapic_set_pinstate(vioapic, irq, false);
break;
default:
panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
}
VIOAPIC_UNLOCK(vioapic);
return 0;
}
int
vioapic_assert_irq(struct vm *vm, int irq)
{
return vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT);
}
int
vioapic_deassert_irq(struct vm *vm, int irq)
{
return vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT);
}
int
vioapic_pulse_irq(struct vm *vm, int irq)
{
return vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE);
}
/*
* Reset the vlapic's trigger-mode register to reflect the ioapic pin
* configuration.
*/
void
vioapic_update_tmr(struct vcpu *vcpu)
{
struct vioapic *vioapic;
struct vlapic *vlapic;
uint32_t low;
int delmode, pin, vector;
bool level;
vlapic = vcpu->arch_vcpu.vlapic;
vioapic = vm_ioapic(vcpu->vm);
VIOAPIC_LOCK(vioapic);
for (pin = 0; pin < vioapic_pincount(vioapic->vm); pin++) {
low = vioapic->rtbl[pin].reg;
level = low & IOAPIC_RTE_TRGRLVL ? true : false;
/*
* For a level-triggered 'pin' let the vlapic figure out if
* an assertion on this 'pin' would result in an interrupt
* being delivered to it. If yes, then it will modify the
* TMR bit associated with this vector to level-triggered.
*/
delmode = low & IOAPIC_RTE_DELMOD;
vector = low & IOAPIC_RTE_INTVEC;
vlapic_set_tmr_one_vec(vlapic, delmode, vector, level);
}
vlapic_apicv_batch_set_tmr(vlapic);
VIOAPIC_UNLOCK(vioapic);
}
static uint32_t
vioapic_read(struct vioapic *vioapic, uint32_t addr)
{
int regnum, pin, rshift;
regnum = addr & 0xff;
switch (regnum) {
case IOAPIC_ID:
return vioapic->id;
case IOAPIC_VER:
return ((vioapic_pincount(vioapic->vm) - 1) << MAX_RTE_SHIFT)
| 0x11;
case IOAPIC_ARB:
return vioapic->id;
default:
break;
}
/* redirection table entries */
if (regnum >= IOAPIC_REDTBL &&
regnum < IOAPIC_REDTBL + vioapic_pincount(vioapic->vm) * 2) {
pin = (regnum - IOAPIC_REDTBL) / 2;
if ((regnum - IOAPIC_REDTBL) % 2)
rshift = 32;
else
rshift = 0;
return vioapic->rtbl[pin].reg >> rshift;
}
return 0;
}
/*
* version 0x20+ ioapic has EOI register. And cpu could write vector to this
* register to clear related IRR.
*/
static void
vioapic_write_eoi(struct vioapic *vioapic, int32_t vector)
{
struct vm *vm = vioapic->vm;
int pin;
if (vector < VECTOR_FOR_INTR_START || vector > NR_MAX_VECTOR)
pr_err("vioapic_process_eoi: invalid vector %d", vector);
VIOAPIC_LOCK(vioapic);
for (pin = 0; pin < vioapic_pincount(vm); pin++) {
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_REM_IRR) == 0)
continue;
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_INTVEC) !=
(uint64_t)vector)
continue;
vioapic->rtbl[pin].reg &= ~IOAPIC_RTE_REM_IRR;
if (vioapic->rtbl[pin].acnt > 0) {
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%d: asserted at eoi, acnt %d",
pin, vioapic->rtbl[pin].acnt);
vioapic_send_intr(vioapic, pin);
}
}
VIOAPIC_UNLOCK(vioapic);
}
static void
vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data)
{
uint64_t data64, mask64;
uint64_t last, new, changed;
int regnum, pin, lshift;
regnum = addr & 0xff;
switch (regnum) {
case IOAPIC_ID:
vioapic->id = data & APIC_ID_MASK;
break;
case IOAPIC_VER:
case IOAPIC_ARB:
/* readonly */
break;
default:
break;
}
/* redirection table entries */
if (regnum >= IOAPIC_REDTBL &&
regnum < IOAPIC_REDTBL + vioapic_pincount(vioapic->vm) * 2) {
pin = (regnum - IOAPIC_REDTBL) / 2;
if ((regnum - IOAPIC_REDTBL) % 2)
lshift = 32;
else
lshift = 0;
last = new = vioapic->rtbl[pin].reg;
data64 = (uint64_t)data << lshift;
mask64 = (uint64_t)0xffffffff << lshift;
new &= ~mask64 | RTBL_RO_BITS;
new |= data64 & ~RTBL_RO_BITS;
changed = last ^ new;
/* pin0 from vpic mask/unmask */
if (pin == 0 && (changed & IOAPIC_RTE_INTMASK)) {
/* mask -> umask */
if ((last & IOAPIC_RTE_INTMASK) &&
((new & IOAPIC_RTE_INTMASK) == 0)) {
if ((vioapic->vm->vpic_wire_mode
== VPIC_WIRE_NULL) ||
(vioapic->vm->vpic_wire_mode
== VPIC_WIRE_INTR)) {
atomic_set_int(
&vioapic->vm->vpic_wire_mode,
VPIC_WIRE_IOAPIC);
dev_dbg(ACRN_DBG_IOAPIC,
"vpic wire mode -> IOAPIC");
} else {
pr_err("WARNING: invalid vpic wire mode change");
return;
}
/* unmask -> mask */
} else if (((last & IOAPIC_RTE_INTMASK) == 0) &&
(new & IOAPIC_RTE_INTMASK)) {
if (vioapic->vm->vpic_wire_mode
== VPIC_WIRE_IOAPIC) {
atomic_set_int(
&vioapic->vm->vpic_wire_mode,
VPIC_WIRE_INTR);
dev_dbg(ACRN_DBG_IOAPIC,
"vpic wire mode -> INTR");
}
}
}
vioapic->rtbl[pin].reg = new;
dev_dbg(ACRN_DBG_IOAPIC, "ioapic pin%d: redir table entry %#lx",
pin, vioapic->rtbl[pin].reg);
/*
* If any fields in the redirection table entry (except mask
* or polarity) have changed then rendezvous all the vcpus
* to update their vlapic trigger-mode registers.
*/
if (changed & ~(IOAPIC_RTE_INTMASK | IOAPIC_RTE_INTPOL)) {
int i;
struct vcpu *vcpu;
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%d: recalculate vlapic trigger-mode reg",
pin);
VIOAPIC_UNLOCK(vioapic);
foreach_vcpu(i, vioapic->vm, vcpu) {
vcpu_make_request(vcpu, ACRN_REQUEST_TMR_UPDATE);
}
VIOAPIC_LOCK(vioapic);
}
/*
* Generate an interrupt if the following conditions are met:
* - pin is not masked
* - previous interrupt has been EOIed
* - pin level is asserted
*/
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_INTMASK) ==
IOAPIC_RTE_INTMCLR &&
(vioapic->rtbl[pin].reg & IOAPIC_RTE_REM_IRR) == 0 &&
(vioapic->rtbl[pin].acnt > 0)) {
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%d: asserted at rtbl write, acnt %d",
pin, vioapic->rtbl[pin].acnt);
vioapic_send_intr(vioapic, pin);
}
/* remap for active: interrupt mask -> unmask
* remap for deactive: interrupt mask & vector set to 0
*/
data64 = vioapic->rtbl[pin].reg;
if ((((data64 & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMCLR)
&& ((last & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET))
|| (((data64 & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET)
&& ((vioapic->rtbl[pin].reg & IOAPIC_RTE_INTVEC) == 0))) {
/* VM enable intr */
struct ptdev_intx_info intx;
/* NOTE: only support max 256 pin */
intx.virt_pin = (uint8_t)pin;
intx.vpin_src = PTDEV_VPIN_IOAPIC;
ptdev_intx_pin_remap(vioapic->vm, &intx);
}
}
}
static int
vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa,
uint64_t *data, int size, bool doread)
{
uint64_t offset;
offset = gpa - VIOAPIC_BASE;
/*
* The IOAPIC specification allows 32-bit wide accesses to the
* IOREGSEL (offset 0) and IOWIN (offset 16) registers.
*/
if (size != 4 || (offset != IOREGSEL && offset != IOWIN &&
offset != IOEOI)) {
if (doread)
*data = 0;
return 0;
}
VIOAPIC_LOCK(vioapic);
if (offset == IOREGSEL) {
if (doread)
*data = vioapic->ioregsel;
else
vioapic->ioregsel = *data;
} else if (offset == IOEOI) {
/* only need to handle write operation */
if (!doread)
vioapic_write_eoi(vioapic, *data);
} else {
if (doread) {
*data = vioapic_read(vioapic, vioapic->ioregsel);
} else {
vioapic_write(vioapic, vioapic->ioregsel,
*data);
}
}
VIOAPIC_UNLOCK(vioapic);
return 0;
}
int
vioapic_mmio_read(void *vm, uint64_t gpa, uint64_t *rval,
int size)
{
int error;
struct vioapic *vioapic;
vioapic = vm_ioapic(vm);
error = vioapic_mmio_rw(vioapic, gpa, rval, size, true);
return error;
}
int
vioapic_mmio_write(void *vm, uint64_t gpa, uint64_t wval,
int size)
{
int error;
struct vioapic *vioapic;
vioapic = vm_ioapic(vm);
error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false);
return error;
}
void
vioapic_process_eoi(struct vm *vm, int vector)
{
struct vioapic *vioapic;
int pin;
if (vector < VECTOR_FOR_INTR_START || vector > NR_MAX_VECTOR)
pr_err("vioapic_process_eoi: invalid vector %d", vector);
vioapic = vm_ioapic(vm);
dev_dbg(ACRN_DBG_IOAPIC, "ioapic processing eoi for vector %d", vector);
/* notify device to ack if assigned pin */
for (pin = 0; pin < vioapic_pincount(vm); pin++) {
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_REM_IRR) == 0)
continue;
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_INTVEC) !=
(uint64_t)vector)
continue;
ptdev_intx_ack(vm, pin, PTDEV_VPIN_IOAPIC);
}
/*
* XXX keep track of the pins associated with this vector instead
* of iterating on every single pin each time.
*/
VIOAPIC_LOCK(vioapic);
for (pin = 0; pin < vioapic_pincount(vm); pin++) {
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_REM_IRR) == 0)
continue;
if ((vioapic->rtbl[pin].reg & IOAPIC_RTE_INTVEC) !=
(uint64_t)vector)
continue;
vioapic->rtbl[pin].reg &= ~IOAPIC_RTE_REM_IRR;
if (vioapic->rtbl[pin].acnt > 0) {
dev_dbg(ACRN_DBG_IOAPIC,
"ioapic pin%d: asserted at eoi, acnt %d",
pin, vioapic->rtbl[pin].acnt);
vioapic_send_intr(vioapic, pin);
}
}
VIOAPIC_UNLOCK(vioapic);
}
struct vioapic *
vioapic_init(struct vm *vm)
{
int i;
struct vioapic *vioapic;
vioapic = calloc(1, sizeof(struct vioapic));
ASSERT(vioapic != NULL, "");
vioapic->vm = vm;
spinlock_init(&vioapic->mtx);
/* Initialize all redirection entries to mask all interrupts */
for (i = 0; i < vioapic_pincount(vioapic->vm); i++)
vioapic->rtbl[i].reg = 0x0001000000010000UL;
register_mmio_emulation_handler(vm,
vioapic_mmio_access_handler,
(uint64_t)VIOAPIC_BASE,
(uint64_t)VIOAPIC_BASE + VIOAPIC_SIZE,
(void *) 0);
return vioapic;
}
void
vioapic_cleanup(struct vioapic *vioapic)
{
unregister_mmio_emulation_handler(vioapic->vm,
(uint64_t)VIOAPIC_BASE,
(uint64_t)VIOAPIC_BASE + VIOAPIC_SIZE);
free(vioapic);
}
int
vioapic_pincount(struct vm *vm)
{
if (is_vm0(vm))
return REDIR_ENTRIES_HW;
else
return REDIR_ENTRIES_UOS;
}
int vioapic_mmio_access_handler(struct vcpu *vcpu, struct mem_io *mmio,
void *handler_private_data)
{
struct vm *vm = vcpu->vm;
uint64_t gpa = mmio->paddr;
int ret = 0;
(void)handler_private_data;
/* Note all RW to IOAPIC are 32-Bit in size */
ASSERT(mmio->access_size == 4,
"All RW to LAPIC must be 32-bits in size");
if (mmio->read_write == HV_MEM_IO_READ) {
ret = vioapic_mmio_read(vm,
gpa,
&mmio->value,
mmio->access_size);
mmio->mmio_status = MMIO_TRANS_VALID;
} else if (mmio->read_write == HV_MEM_IO_WRITE) {
ret = vioapic_mmio_write(vm,
gpa,
mmio->value,
mmio->access_size);
mmio->mmio_status = MMIO_TRANS_VALID;
}
return ret;
}
bool vioapic_get_rte(struct vm *vm, int pin, void *rte)
{
struct vioapic *vioapic;
vioapic = vm_ioapic(vm);
if (vioapic && rte) {
*(uint64_t *)rte = vioapic->rtbl[pin].reg;
return true;
} else
return false;
}
int get_vioapic_info(char *str, int str_max, int vmid)
{
int pin, len, size = str_max, vector, delmode;
uint64_t rte;
uint32_t low, high, dest;
bool level, phys, remote_irr, mask;
struct vm *vm = get_vm_from_vmid(vmid);
if (!vm) {
len = snprintf(str, size,
"\r\nvm is not exist for vmid %d", vmid);
size -= len;
str += len;
goto END;
}
len = snprintf(str, size,
"\r\nPIN\tVEC\tDM\tDEST\tTM\tDELM\tIRR\tMASK");
size -= len;
str += len;
for (pin = 0 ; pin < vioapic_pincount(vm); pin++) {
vioapic_get_rte(vm, pin, (void *)&rte);
low = rte;
high = rte >> 32;
mask = ((low & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET);
remote_irr = ((low & IOAPIC_RTE_REM_IRR) == IOAPIC_RTE_REM_IRR);
phys = ((low & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
delmode = low & IOAPIC_RTE_DELMOD;
level = low & IOAPIC_RTE_TRGRLVL ? true : false;
vector = low & IOAPIC_RTE_INTVEC;
dest = high >> APIC_ID_SHIFT;
len = snprintf(str, size,
"\r\n%d\t0x%X\t%s\t0x%X\t%s\t%d\t%d\t%d",
pin, vector, phys ? "phys" : "logic",
dest, level ? "level" : "edge",
delmode >> 8, remote_irr, mask);
size -= len;
str += len;
}
END:
snprintf(str, size, "\r\n");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
/*-
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#ifndef _VLAPIC_PRIV_H_
#define _VLAPIC_PRIV_H_
/*
* APIC Register: Offset Description
*/
#define APIC_OFFSET_ID 0x20 /* Local APIC ID */
#define APIC_OFFSET_VER 0x30 /* Local APIC Version */
#define APIC_OFFSET_TPR 0x80 /* Task Priority Register */
#define APIC_OFFSET_APR 0x90 /* Arbitration Priority */
#define APIC_OFFSET_PPR 0xA0 /* Processor Priority Register */
#define APIC_OFFSET_EOI 0xB0 /* EOI Register */
#define APIC_OFFSET_RRR 0xC0 /* Remote read */
#define APIC_OFFSET_LDR 0xD0 /* Logical Destination */
#define APIC_OFFSET_DFR 0xE0 /* Destination Format Register */
#define APIC_OFFSET_SVR 0xF0 /* Spurious Vector Register */
#define APIC_OFFSET_ISR0 0x100 /* In Service Register */
#define APIC_OFFSET_ISR1 0x110
#define APIC_OFFSET_ISR2 0x120
#define APIC_OFFSET_ISR3 0x130
#define APIC_OFFSET_ISR4 0x140
#define APIC_OFFSET_ISR5 0x150
#define APIC_OFFSET_ISR6 0x160
#define APIC_OFFSET_ISR7 0x170
#define APIC_OFFSET_TMR0 0x180 /* Trigger Mode Register */
#define APIC_OFFSET_TMR1 0x190
#define APIC_OFFSET_TMR2 0x1A0
#define APIC_OFFSET_TMR3 0x1B0
#define APIC_OFFSET_TMR4 0x1C0
#define APIC_OFFSET_TMR5 0x1D0
#define APIC_OFFSET_TMR6 0x1E0
#define APIC_OFFSET_TMR7 0x1F0
#define APIC_OFFSET_IRR0 0x200 /* Interrupt Request Register */
#define APIC_OFFSET_IRR1 0x210
#define APIC_OFFSET_IRR2 0x220
#define APIC_OFFSET_IRR3 0x230
#define APIC_OFFSET_IRR4 0x240
#define APIC_OFFSET_IRR5 0x250
#define APIC_OFFSET_IRR6 0x260
#define APIC_OFFSET_IRR7 0x270
#define APIC_OFFSET_ESR 0x280 /* Error Status Register */
#define APIC_OFFSET_CMCI_LVT 0x2F0 /* Local Vector Table (CMCI) */
#define APIC_OFFSET_ICR_LOW 0x300 /* Interrupt Command Register */
#define APIC_OFFSET_ICR_HI 0x310
#define APIC_OFFSET_TIMER_LVT 0x320 /* Local Vector Table (Timer) */
#define APIC_OFFSET_THERM_LVT 0x330 /* Local Vector Table (Thermal) */
#define APIC_OFFSET_PERF_LVT 0x340 /* Local Vector Table (PMC) */
#define APIC_OFFSET_LINT0_LVT 0x350 /* Local Vector Table (LINT0) */
#define APIC_OFFSET_LINT1_LVT 0x360 /* Local Vector Table (LINT1) */
#define APIC_OFFSET_ERROR_LVT 0x370 /* Local Vector Table (ERROR) */
#define APIC_OFFSET_TIMER_ICR 0x380 /* Timer's Initial Count */
#define APIC_OFFSET_TIMER_CCR 0x390 /* Timer's Current Count */
#define APIC_OFFSET_TIMER_DCR 0x3E0 /* Timer's Divide Configuration */
#define APIC_OFFSET_SELF_IPI 0x3F0 /* Self IPI register */
/*
* 16 priority levels with at most one vector injected per level.
*/
#define ISRVEC_STK_SIZE (16 + 1)
#define VLAPIC_MAXLVT_INDEX APIC_LVT_CMCI
struct vlapic;
struct pir_desc {
uint64_t pir[4];
uint64_t pending;
uint64_t unused[3];
} __aligned(64);
struct vlapic_ops {
int (*apicv_set_intr_ready)
(struct vlapic *vlapic, int vector, bool level);
int (*apicv_pending_intr)(struct vlapic *vlapic, int *vecptr);
void (*apicv_intr_accepted)(struct vlapic *vlapic, int vector);
void (*apicv_post_intr)(struct vlapic *vlapic, int hostcpu);
void (*apicv_set_tmr)(struct vlapic *vlapic, int vector, bool level);
void (*apicv_batch_set_tmr)(struct vlapic *vlapic);
void (*enable_x2apic_mode)(struct vlapic *vlapic);
};
struct vlapic {
struct vm *vm;
struct vcpu *vcpu;
struct lapic *apic_page;
struct pir_desc *pir_desc;
struct vlapic_ops ops;
uint32_t esr_pending;
int esr_firing;
struct callout callout; /* vlapic timer */
struct bintime timer_fire_bt; /* callout expiry time */
struct bintime timer_freq_bt; /* timer frequency */
struct bintime timer_period_bt; /* timer period */
long last_timer; /* the last timer id */
spinlock_t timer_mtx;
/*
* The 'isrvec_stk' is a stack of vectors injected by the local apic.
* A vector is popped from the stack when the processor does an EOI.
* The vector on the top of the stack is used to compute the
* Processor Priority in conjunction with the TPR.
*/
uint8_t isrvec_stk[ISRVEC_STK_SIZE];
int isrvec_stk_top;
uint64_t msr_apicbase;
/*
* Copies of some registers in the virtual APIC page. We do this for
* a couple of different reasons:
* - to be able to detect what changed (e.g. svr_last)
* - to maintain a coherent snapshot of the register (e.g. lvt_last)
*/
uint32_t svr_last;
uint32_t lvt_last[VLAPIC_MAXLVT_INDEX + 1];
struct pir_desc pir;
};
void vlapic_cleanup(struct vlapic *vlapic);
#endif /* _VLAPIC_PRIV_H_ */

View File

@@ -0,0 +1,324 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <hv_debug.h>
/* Local variables */
/* VMs list */
struct list_head vm_list = {
.next = &vm_list,
.prev = &vm_list,
};
/* Lock for VMs list */
spinlock_t vm_list_lock = {
.head = 0,
.tail = 0
};
/* used for vmid allocation. And this means the max vm number is 64 */
static unsigned long vmid_bitmap;
static void init_vm(struct vm_description *vm_desc,
struct vm *vm_handle)
{
/* Populate VM attributes from VM description */
vm_handle->hw.num_vcpus = vm_desc->vm_hw_num_cores;
vm_handle->state_info.privilege = vm_desc->vm_state_info_privilege;
vm_handle->state_info.boot_count = 0;
}
/* return a pointer to the virtual machine structure associated with
* this VM ID
*/
struct vm *get_vm_from_vmid(int vm_id)
{
struct vm *vm = NULL;
struct list_head *pos;
spinlock_obtain(&vm_list_lock);
list_for_each(pos, &vm_list) {
vm = list_entry(pos, struct vm, list);
if (vm->attr.id == vm_id) {
spinlock_release(&vm_list_lock);
return vm;
}
}
spinlock_release(&vm_list_lock);
return NULL;
}
int create_vm(struct vm_description *vm_desc, struct vm **rtn_vm)
{
unsigned int id;
struct vm *vm;
int status = 0;
if ((vm_desc == NULL) || (rtn_vm == NULL))
status = -EINVAL;
if (status == 0) {
/* Allocate memory for virtual machine */
vm = calloc(1, sizeof(struct vm));
ASSERT(vm != NULL, "vm allocation failed");
/*
* Map Virtual Machine to its VM Description
*/
init_vm(vm_desc, vm);
/* Init mmio list */
INIT_LIST_HEAD(&vm->mmio_list);
if (vm->hw.num_vcpus == 0)
vm->hw.num_vcpus = phy_cpu_num;
vm->hw.vcpu_array =
calloc(1, sizeof(struct vcpu *) * vm->hw.num_vcpus);
ASSERT(vm->hw.vcpu_array != NULL,
"vcpu_array allocation failed");
for (id = 0; id < sizeof(long) * 8; id++)
if (bitmap_test_and_set(id, &vmid_bitmap) == 0)
break;
vm->attr.id = vm->attr.boot_idx = id;
snprintf(&vm->attr.name[0], MAX_VM_NAME_LEN, "vm_%d",
vm->attr.id);
atomic_store_rel_int(&vm->hw.created_vcpus, 0);
/* gpa_lowtop are used for system start up */
vm->hw.gpa_lowtop = 0;
/* Only for SOS: Configure VM software information */
/* For UOS: This VM software information is configure in DM */
if (is_vm0(vm)) {
prepare_vm0_memmap_and_e820(vm);
#ifndef CONFIG_EFI_STUB
status = init_vm0_boot_info(vm);
#endif
} else {
/* populate UOS vm fields according to vm_desc */
vm->secure_world_enabled =
vm_desc->secure_world_enabled;
memcpy_s(&vm->GUID[0], sizeof(vm->GUID),
&vm_desc->GUID[0],
sizeof(vm_desc->GUID));
}
INIT_LIST_HEAD(&vm->list);
spinlock_obtain(&vm_list_lock);
list_add(&vm->list, &vm_list);
spinlock_release(&vm_list_lock);
/* Ensure VM software information obtained */
if (status == 0) {
/* Set up IO bit-mask such that VM exit occurs on
* selected IO ranges
*/
setup_io_bitmap(vm);
/* Create virtual uart */
if (is_vm0(vm))
vm->vuart = vuart_init(vm);
vm->vpic = vpic_init(vm);
/* vpic wire_mode default is INTR */
vm->vpic_wire_mode = VPIC_WIRE_INTR;
/* Allocate full emulated vIOAPIC instance */
vm->arch_vm.virt_ioapic = vioapic_init(vm);
/* Populate return VM handle */
*rtn_vm = vm;
ptdev_vm_init(vm);
vm->sw.req_buf = 0;
vm->state = VM_CREATED;
}
}
/* Return status to caller */
return status;
}
int shutdown_vm(struct vm *vm)
{
int i, status = 0;
struct vcpu *vcpu = NULL;
if (vm == NULL)
return -EINVAL;
pause_vm(vm);
/* Only allow shutdown paused vm */
if (vm->state != VM_PAUSED)
return -EINVAL;
foreach_vcpu(i, vm, vcpu) {
reset_vcpu(vcpu);
destroy_vcpu(vcpu);
}
spinlock_obtain(&vm_list_lock);
list_del_init(&vm->list);
spinlock_release(&vm_list_lock);
ptdev_vm_deinit(vm);
/* cleanup and free vioapic */
vioapic_cleanup(vm->arch_vm.virt_ioapic);
/* Free EPT allocated resources assigned to VM */
destroy_ept(vm);
/* Free MSR bitmap */
free(vm->arch_vm.msr_bitmap);
/* TODO: De-initialize I/O Emulation */
free_io_emulation_resource(vm);
/* Free iommu_domain */
if (vm->iommu_domain)
destroy_iommu_domain(vm->iommu_domain);
bitmap_clr(vm->attr.id, &vmid_bitmap);
if (vm->vpic)
vpic_cleanup(vm);
free(vm->hw.vcpu_array);
/* TODO: De-Configure HV-SW */
/* Deallocate VM */
free(vm);
/* Return status to caller */
return status;
}
int start_vm(struct vm *vm)
{
struct vcpu *vcpu = NULL;
vm->state = VM_STARTED;
/* Only start BSP (vid = 0) and let BSP start other APs */
vcpu = vcpu_from_vid(vm, 0);
ASSERT(vcpu != NULL, "vm%d, vcpu0", vm->attr.id);
schedule_vcpu(vcpu);
return 0;
}
/*
* DM only pause vm for shutdown/reboot. If we need to
* extend the pause vm for DM, this API should be extended.
*/
int pause_vm(struct vm *vm)
{
int i;
struct vcpu *vcpu = NULL;
if (vm->state == VM_PAUSED)
return 0;
vm->state = VM_PAUSED;
foreach_vcpu(i, vm, vcpu)
pause_vcpu(vcpu, VCPU_ZOMBIE);
return 0;
}
int vm_resume(struct vm *vm)
{
int i;
struct vcpu *vcpu = NULL;
foreach_vcpu(i, vm, vcpu)
resume_vcpu(vcpu);
vm->state = VM_STARTED;
return 0;
}
/* Finally, we will remove the array and only maintain vm0 desc */
struct vm_description *get_vm_desc(int idx)
{
struct vm_description_array *vm_desc_array;
/* Obtain base of user defined VM description array data
* structure
*/
vm_desc_array = (struct vm_description_array *)get_vm_desc_base();
/* Obtain VM description array base */
if (idx >= vm_desc_array->num_vm_desc)
return NULL;
else
return &vm_desc_array->vm_desc_array[idx];
}
/* Create vm/vcpu for vm0 */
int prepare_vm0(void)
{
int i, ret;
struct vm *vm = NULL;
struct vm_description *vm_desc = NULL;
vm_desc = get_vm_desc(0);
ASSERT(vm_desc, "get vm desc failed");
ret = create_vm(vm_desc, &vm);
ASSERT(ret == 0, "VM creation failed!");
prepare_vcpu(vm, vm_desc->vm_hw_logical_core_ids[0]);
/* Prepare the AP for vm0 */
for (i = 1; i < vm_desc->vm_hw_num_cores; i++)
prepare_vcpu(vm, vm_desc->vm_hw_logical_core_ids[i]);
/* start vm0 BSP automatically */
start_vm(vm);
pr_fatal("Start VM0");
return 0;
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include <acrn_hv_defs.h>
#include <hypercall.h>
int vmcall_handler(struct vcpu *vcpu)
{
int64_t ret = 0;
struct vm *vm = vcpu->vm;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
/* hypercall ID from guest*/
uint64_t hypcall_id = cur_context->guest_cpu_regs.regs.r8;
/* hypercall param1 from guest*/
uint64_t param1 = cur_context->guest_cpu_regs.regs.rdi;
/* hypercall param2 from guest*/
uint64_t param2 = cur_context->guest_cpu_regs.regs.rsi;
/* hypercall param3 from guest, reserved*/
/* uint64_t param3 = cur_context->guest_cpu_regs.regs.rdx; */
/* hypercall param4 from guest, reserved*/
/* uint64_t param4 = cur_context->guest_cpu_regs.regs.rcx; */
/* Dispatch the hypercall handler */
switch (hypcall_id) {
case HC_GET_API_VERSION:
ret = hcall_get_api_version(vm, param1);
break;
case HC_CREATE_VM:
ret = hcall_create_vm(vm, param1);
break;
case HC_DESTROY_VM:
ret = hcall_destroy_vm(param1);
break;
case HC_START_VM:
ret = hcall_resume_vm(param1);
break;
case HC_PAUSE_VM:
ret = hcall_pause_vm(param1);
break;
case HC_CREATE_VCPU:
ret = hcall_create_vcpu(vm, param1, param2);
break;
case HC_ASSERT_IRQLINE:
ret = hcall_assert_irqline(vm, param1, param2);
break;
case HC_DEASSERT_IRQLINE:
ret = hcall_deassert_irqline(vm, param1, param2);
break;
case HC_PULSE_IRQLINE:
ret = hcall_pulse_irqline(vm, param1, param2);
break;
case HC_INJECT_MSI:
ret = hcall_inject_msi(vm, param1, param2);
break;
case HC_SET_IOREQ_BUFFER:
ret = hcall_set_ioreq_buffer(vm, param1, param2);
break;
case HC_NOTIFY_REQUEST_FINISH:
ret = hcall_notify_req_finish(param1, param2);
break;
case HC_VM_SET_MEMMAP:
ret = hcall_set_vm_memmap(vm, param1, param2);
break;
case HC_VM_PCI_MSIX_REMAP:
ret = hcall_remap_pci_msix(vm, param1, param2);
break;
case HC_VM_GPA2HPA:
ret = hcall_gpa_to_hpa(vm, param1, param2);
break;
case HC_ASSIGN_PTDEV:
ret = hcall_assign_ptdev(vm, param1, param2);
break;
case HC_DEASSIGN_PTDEV:
ret = hcall_deassign_ptdev(vm, param1, param2);
break;
case HC_SET_PTDEV_INTR_INFO:
ret = hcall_set_ptdev_intr_info(vm, param1, param2);
break;
case HC_RESET_PTDEV_INTR_INFO:
ret = hcall_reset_ptdev_intr_info(vm, param1, param2);
break;
case HC_SETUP_SBUF:
ret = hcall_setup_sbuf(vm, param1);
break;
default:
pr_err("op %d: Invalid hypercall\n", hypcall_id);
ret = -1;
break;
}
cur_context->guest_cpu_regs.regs.rax = ret;
TRACE_2L(TRC_VMEXIT_VMCALL, vm->attr.id, hypcall_id);
return 0;
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
/*MRS need to be emulated, the order in this array better as freq of ops*/
static const uint32_t emulated_msrs[] = {
MSR_IA32_TSC_DEADLINE, /* Enable TSC_DEADLINE VMEXIT */
/* following MSR not emulated now */
/*
* MSR_IA32_APIC_BASE,
* MSR_IA32_SYSENTER_CS,
* MSR_IA32_SYSENTER_ESP,
* MSR_IA32_SYSENTER_EIP,
* MSR_IA32_TSC_AUX,
* MSR_IA32_TIME_STAMP_COUNTER,
*/
};
/* the index is matched with emulated msrs array*/
enum {
IDX_TSC_DEADLINE,
IDX_MAX_MSR
};
static void enable_msr_interception(uint8_t *bitmap, uint32_t msr)
{
uint8_t *read_map;
uint8_t *write_map;
uint8_t value;
/* low MSR */
if (msr < 0x1FFF) {
read_map = bitmap;
write_map = bitmap + 2048;
} else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
read_map = bitmap + 1024;
write_map = bitmap + 3072;
} else {
pr_err("Invalid MSR");
return;
}
msr &= 0x1FFF;
value = read_map[(msr>>3)];
value |= 1<<(msr%8);
/* right now we trap for both r/w */
read_map[(msr>>3)] = value;
write_map[(msr>>3)] = value;
}
/* not used now just leave it for some cases it may be used as API*/
void disable_msr_interception(uint8_t *bitmap, uint32_t msr)
{
uint8_t *read_map;
uint8_t *write_map;
uint8_t value;
/* low MSR */
if (msr < 0x1FFF) {
read_map = bitmap;
write_map = bitmap + 2048;
} else if ((msr >= 0xc0000000) && (msr <= 0xc0001fff)) {
read_map = bitmap + 1024;
write_map = bitmap + 3072;
} else {
pr_err("Invalid MSR");
return;
}
msr &= 0x1FFF;
value = read_map[(msr>>3)];
value &= ~(1<<(msr%8));
/* right now we trap for both r/w */
read_map[(msr>>3)] = value;
write_map[(msr>>3)] = value;
}
void init_msr_emulation(struct vcpu *vcpu)
{
uint32_t i = 0;
uint32_t msrs_count = ARRAY_SIZE(emulated_msrs);
void *msr_bitmap;
uint64_t value64;
ASSERT(msrs_count == IDX_MAX_MSR,
"MSR ID should be matched with emulated_msrs");
/*msr bitmap, just allocated/init once, and used for all vm's vcpu*/
if (is_vcpu_bsp(vcpu)) {
/* Allocate and initialize memory for MSR bitmap region*/
vcpu->vm->arch_vm.msr_bitmap = alloc_page();
ASSERT(vcpu->vm->arch_vm.msr_bitmap, "");
memset(vcpu->vm->arch_vm.msr_bitmap, 0x0, CPU_PAGE_SIZE);
msr_bitmap = vcpu->vm->arch_vm.msr_bitmap;
for (i = 0; i < msrs_count; i++)
enable_msr_interception(msr_bitmap, emulated_msrs[i]);
/* below MSR protected from guest OS, if access to inject gp*/
enable_msr_interception(msr_bitmap, MSR_IA32_MTRR_CAP);
enable_msr_interception(msr_bitmap, MSR_IA32_MTRR_DEF_TYPE);
for (i = MSR_IA32_MTRR_PHYSBASE_0;
i <= MSR_IA32_MTRR_PHYSMASK_9; i++) {
enable_msr_interception(msr_bitmap, i);
}
enable_msr_interception(msr_bitmap, MSR_IA32_MTRR_FIX64K_00000);
enable_msr_interception(msr_bitmap, MSR_IA32_MTRR_FIX16K_80000);
enable_msr_interception(msr_bitmap, MSR_IA32_MTRR_FIX16K_A0000);
for (i = MSR_IA32_MTRR_FIX4K_C0000;
i <= MSR_IA32_MTRR_FIX4K_F8000; i++) {
enable_msr_interception(msr_bitmap, i);
}
}
/* Set up MSR bitmap - pg 2904 24.6.9 */
value64 = (int64_t) vcpu->vm->arch_vm.msr_bitmap;
exec_vmwrite64(VMX_MSR_BITMAP_FULL, value64);
pr_dbg("VMX_MSR_BITMAP: 0x%016llx ", value64);
vcpu->guest_msrs = (uint64_t *)calloc(msrs_count, sizeof(uint64_t));
ASSERT(vcpu->guest_msrs != NULL, "");
memset(vcpu->guest_msrs, 0, msrs_count * sizeof(uint64_t));
}
int rdmsr_handler(struct vcpu *vcpu)
{
uint32_t msr;
uint64_t v = 0;
uint32_t id;
int cur_context = vcpu->arch_vcpu.cur_context;
/* Read the msr value */
msr = vcpu->arch_vcpu.contexts[cur_context].guest_cpu_regs.regs.rcx;
/* Do the required processing for each msr case */
switch (msr) {
case MSR_IA32_TSC_DEADLINE:
{
v = vcpu->guest_msrs[IDX_TSC_DEADLINE];
break;
}
case MSR_IA32_MTRR_CAP:
case MSR_IA32_MTRR_DEF_TYPE:
case MSR_IA32_MTRR_PHYSBASE_0 ... MSR_IA32_MTRR_PHYSMASK_9:
case MSR_IA32_MTRR_FIX64K_00000 ... MSR_IA32_MTRR_FIX4K_F8000:
{
vcpu_inject_gp(vcpu);
break;
}
/* following MSR not emulated now just left for future */
case MSR_IA32_SYSENTER_CS:
{
v = exec_vmread(VMX_GUEST_IA32_SYSENTER_CS);
break;
}
case MSR_IA32_SYSENTER_ESP:
{
v = exec_vmread(VMX_GUEST_IA32_SYSENTER_ESP);
break;
}
case MSR_IA32_SYSENTER_EIP:
{
v = exec_vmread(VMX_GUEST_IA32_SYSENTER_EIP);
break;
}
case MSR_IA32_TSC_AUX:
{
v = vcpu->arch_vcpu.msr_tsc_aux;
break;
}
case MSR_IA32_TIME_STAMP_COUNTER:
{
/* Read the host TSC value */
CPU_RDTSCP_EXECUTE(&v, &id);
/* Add the TSC_offset to host TSC and return the value */
v += exec_vmread64(VMX_TSC_OFFSET_FULL);
break;
}
case MSR_IA32_APIC_BASE:
{
bool ret;
/* Read APIC base */
vlapic_rdmsr(vcpu, msr, &v, &ret);
break;
}
default:
{
pr_warn("rdmsr: %lx should not come here!", msr);
v = 0;
break;
}
}
/* Store the MSR contents in RAX and RDX */
vcpu->arch_vcpu.contexts[cur_context].guest_cpu_regs.regs.rax =
v & 0xffffffff;
vcpu->arch_vcpu.contexts[cur_context].guest_cpu_regs.regs.rdx = v >> 32;
TRACE_2L(TRC_VMEXIT_RDMSR, msr, v);
return 0;
}
int wrmsr_handler(struct vcpu *vcpu)
{
uint32_t msr;
uint64_t v;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
/* Read the MSR ID */
msr = cur_context->guest_cpu_regs.regs.rcx;
/* Get the MSR contents */
v = (((uint64_t) cur_context->guest_cpu_regs.regs.rdx) << 32) |
((uint64_t) cur_context->guest_cpu_regs.regs.rax);
/* Do the required processing for each msr case */
switch (msr) {
case MSR_IA32_TSC_DEADLINE:
{
bool ret;
/* Write APIC base */
vlapic_wrmsr(vcpu, msr, v, &ret);
vcpu->guest_msrs[IDX_TSC_DEADLINE] = v;
break;
}
case MSR_IA32_MTRR_CAP:
case MSR_IA32_MTRR_DEF_TYPE:
case MSR_IA32_MTRR_PHYSBASE_0 ... MSR_IA32_MTRR_PHYSMASK_9:
case MSR_IA32_MTRR_FIX64K_00000 ... MSR_IA32_MTRR_FIX4K_F8000:
{
vcpu_inject_gp(vcpu);
break;
}
/* following MSR not emulated now just left for future */
case MSR_IA32_SYSENTER_CS:
{
exec_vmwrite(VMX_GUEST_IA32_SYSENTER_CS, v);
break;
}
case MSR_IA32_SYSENTER_ESP:
{
exec_vmwrite(VMX_GUEST_IA32_SYSENTER_ESP, v);
break;
}
case MSR_IA32_SYSENTER_EIP:
{
exec_vmwrite(VMX_GUEST_IA32_SYSENTER_EIP, v);
break;
}
case MSR_IA32_GS_BASE:
{
exec_vmwrite(VMX_GUEST_GS_BASE, v);
break;
}
case MSR_IA32_TSC_AUX:
{
vcpu->arch_vcpu.msr_tsc_aux = v;
break;
}
case MSR_IA32_APIC_BASE:
{
bool ret;
/* Write APIC base */
vlapic_wrmsr(vcpu, msr, v, &ret);
break;
}
default:
{
ASSERT(0, "wrmsr: %lx should not come here!", msr);
msr_write(msr, v);
break;
}
}
TRACE_2L(TRC_VMEXIT_WRMSR, msr, v);
return 0;
}

View File

@@ -0,0 +1,950 @@
/*-
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
#define pr_fmt(fmt) "vpic: " fmt
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define VPIC_LOCK_INIT(vpic) spinlock_init(&((vpic)->lock))
#define VPIC_LOCK(vpic) spinlock_obtain(&((vpic)->lock))
#define VPIC_UNLOCK(vpic) spinlock_release(&((vpic)->lock))
/* TODO: add spinlock_locked support? */
/*#define VPIC_LOCKED(vpic) spinlock_locked(&((vpic)->lock))*/
#define vm_pic(vm) (vm->vpic)
#define true 1
#define false 0
#define ACRN_DBG_PIC 6
enum irqstate {
IRQSTATE_ASSERT,
IRQSTATE_DEASSERT,
IRQSTATE_PULSE
};
struct pic {
bool ready;
int icw_num;
int rd_cmd_reg;
bool aeoi;
bool poll;
bool rotate;
bool sfn; /* special fully-nested mode */
int irq_base;
uint8_t request; /* Interrupt Request Register (IIR) */
uint8_t service; /* Interrupt Service (ISR) */
uint8_t mask; /* Interrupt Mask Register (IMR) */
uint8_t smm; /* special mask mode */
int acnt[8]; /* sum of pin asserts and deasserts */
int lowprio; /* lowest priority irq */
bool intr_raised;
uint8_t elc;
};
struct vpic {
struct vm *vm;
spinlock_t lock;
struct pic pic[2];
};
/*
* Loop over all the pins in priority order from highest to lowest.
*/
#define PIC_PIN_FOREACH(pinvar, pic, tmpvar) \
for (tmpvar = 0, pinvar = (pic->lowprio + 1) & 0x7; \
tmpvar < 8; \
tmpvar++, pinvar = (pinvar + 1) & 0x7)
static void vpic_set_pinstate(struct vpic *vpic, int pin, bool newstate);
static inline bool master_pic(struct vpic *vpic, struct pic *pic)
{
if (pic == &vpic->pic[0])
return true;
else
return false;
}
static inline int vpic_get_highest_isrpin(struct pic *pic)
{
int bit, pin;
int i;
PIC_PIN_FOREACH(pin, pic, i) {
bit = (1 << pin);
if (pic->service & bit) {
/*
* An IS bit that is masked by an IMR bit will not be
* cleared by a non-specific EOI in Special Mask Mode.
*/
if (pic->smm && (pic->mask & bit) != 0)
continue;
else
return pin;
}
}
return -1;
}
static inline int vpic_get_highest_irrpin(struct pic *pic)
{
int serviced;
int bit, pin, tmp;
/*
* In 'Special Fully-Nested Mode' when an interrupt request from
* a slave is in service, the slave is not locked out from the
* master's priority logic.
*/
serviced = pic->service;
if (pic->sfn)
serviced &= ~(1 << 2);
/*
* In 'Special Mask Mode', when a mask bit is set in OCW1 it inhibits
* further interrupts at that level and enables interrupts from all
* other levels that are not masked. In other words the ISR has no
* bearing on the levels that can generate interrupts.
*/
if (pic->smm)
serviced = 0;
PIC_PIN_FOREACH(pin, pic, tmp) {
bit = 1 << pin;
/*
* If there is already an interrupt in service at the same
* or higher priority then bail.
*/
if ((serviced & bit) != 0)
break;
/*
* If an interrupt is asserted and not masked then return
* the corresponding 'pin' to the caller.
*/
if ((pic->request & bit) != 0 && (pic->mask & bit) == 0)
return pin;
}
return -1;
}
static void vpic_notify_intr(struct vpic *vpic)
{
struct pic *pic;
int pin;
/*
* First check the slave.
*/
pic = &vpic->pic[1];
pin = vpic_get_highest_irrpin(pic);
if (!pic->intr_raised && pin != -1) {
dev_dbg(ACRN_DBG_PIC,
"pic slave notify pin = %d (imr 0x%x irr 0x%x isr 0x%x)\n",
pin, pic->mask, pic->request, pic->service);
/*
* Cascade the request from the slave to the master.
*/
pic->intr_raised = true;
vpic_set_pinstate(vpic, 2, true);
vpic_set_pinstate(vpic, 2, false);
} else {
dev_dbg(ACRN_DBG_PIC,
"pic slave no eligible interrupt (imr 0x%x irr 0x%x isr 0x%x)",
pic->mask, pic->request, pic->service);
}
/*
* Then check the master.
*/
pic = &vpic->pic[0];
pin = vpic_get_highest_irrpin(pic);
if (!pic->intr_raised && pin != -1) {
dev_dbg(ACRN_DBG_PIC,
"pic master notify pin = %d (imr 0x%x irr 0x%x isr 0x%x)\n",
pin, pic->mask, pic->request, pic->service);
/*
* From Section 3.6.2, "Interrupt Modes", in the
* MPtable Specification, Version 1.4
*
* PIC interrupts are routed to both the Local APIC
* and the I/O APIC to support operation in 1 of 3
* modes.
*
* 1. Legacy PIC Mode: the PIC effectively bypasses
* all APIC components. In this mode the local APIC is
* disabled and LINT0 is reconfigured as INTR to
* deliver the PIC interrupt directly to the CPU.
*
* 2. Virtual Wire Mode: the APIC is treated as a
* virtual wire which delivers interrupts from the PIC
* to the CPU. In this mode LINT0 is programmed as
* ExtINT to indicate that the PIC is the source of
* the interrupt.
*
* 3. Virtual Wire Mode via I/O APIC: PIC interrupts are
* fielded by the I/O APIC and delivered to the appropriate
* CPU. In this mode the I/O APIC input 0 is programmed
* as ExtINT to indicate that the PIC is the source of the
* interrupt.
*/
pic->intr_raised = true;
if (vpic->vm->vpic_wire_mode == VPIC_WIRE_INTR) {
struct vcpu *vcpu = vcpu_from_vid(vpic->vm, 0);
ASSERT(vcpu != NULL, "vm%d, vcpu0", vpic->vm->attr.id);
vcpu_inject_extint(vcpu);
} else {
vlapic_set_local_intr(vpic->vm, -1, APIC_LVT_LINT0);
/* notify vioapic pin0 if existing
* For vPIC + vIOAPIC mode, vpic master irq connected
* to vioapic pin0 (irq2)
* From MPSpec session 5.1
*/
vioapic_pulse_irq(vpic->vm, 0);
}
} else {
dev_dbg(ACRN_DBG_PIC,
"pic master no eligible interrupt (imr 0x%x irr 0x%x isr 0x%x)",
pic->mask, pic->request, pic->service);
}
}
static int vpic_icw1(__unused struct vpic *vpic, struct pic *pic, uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic icw1 0x%x\n",
vpic->vm, val);
pic->ready = false;
pic->icw_num = 1;
pic->request = 0;
pic->mask = 0;
pic->lowprio = 7;
pic->rd_cmd_reg = 0;
pic->poll = 0;
pic->smm = 0;
if ((val & ICW1_SNGL) != 0) {
dev_dbg(ACRN_DBG_PIC, "vpic cascade mode required\n");
return -1;
}
if ((val & ICW1_IC4) == 0) {
dev_dbg(ACRN_DBG_PIC, "vpic icw4 required\n");
return -1;
}
pic->icw_num++;
return 0;
}
static int vpic_icw2(__unused struct vpic *vpic, struct pic *pic, uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic icw2 0x%x\n",
vpic->vm, val);
pic->irq_base = val & 0xf8;
pic->icw_num++;
return 0;
}
static int vpic_icw3(__unused struct vpic *vpic, struct pic *pic,
__unused uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic icw3 0x%x\n",
vpic->vm, val);
pic->icw_num++;
return 0;
}
static int vpic_icw4(struct vpic *vpic, struct pic *pic, uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic icw4 0x%x\n",
vpic->vm, val);
if ((val & ICW4_8086) == 0) {
dev_dbg(ACRN_DBG_PIC,
"vpic microprocessor mode required\n");
return -1;
}
if ((val & ICW4_AEOI) != 0)
pic->aeoi = true;
if ((val & ICW4_SFNM) != 0) {
if (master_pic(vpic, pic)) {
pic->sfn = true;
} else {
dev_dbg(ACRN_DBG_PIC,
"Ignoring special fully nested mode on slave pic: %#x",
val);
}
}
pic->icw_num = 0;
pic->ready = true;
return 0;
}
bool vpic_is_pin_mask(struct vpic *vpic, uint8_t virt_pin)
{
struct pic *pic;
if (virt_pin < 8)
pic = &vpic->pic[0];
else if (virt_pin < 16) {
pic = &vpic->pic[1];
virt_pin -= 8;
} else
return true;
if (pic->mask & (1 << virt_pin))
return true;
else
return false;
}
static int vpic_ocw1(struct vpic *vpic, struct pic *pic, uint8_t val)
{
int pin, i, bit;
uint8_t old = pic->mask;
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic ocw1 0x%x\n",
vpic->vm, val);
pic->mask = val & 0xff;
/* query and setup if pin/irq is for passthrough device */
PIC_PIN_FOREACH(pin, pic, i) {
bit = (1 << pin);
/* remap for active: interrupt mask -> unmask
* remap for deactive: when vIOAPIC take it over
*/
if (((pic->mask & bit) == 0) && (old & bit)) {
struct ptdev_intx_info intx;
/* master pic pin2 connect with slave pic,
* not device, so not need pt remap
*/
if ((pin == 2) && master_pic(vpic, pic))
continue;
intx.virt_pin = pin;
intx.vpin_src = PTDEV_VPIN_PIC;
if (!master_pic(vpic, pic))
intx.virt_pin += 8;
ptdev_intx_pin_remap(vpic->vm, &intx);
}
}
return 0;
}
static int vpic_ocw2(struct vpic *vpic, struct pic *pic, uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic ocw2 0x%x\n",
vpic->vm, val);
pic->rotate = ((val & OCW2_R) != 0);
if ((val & OCW2_EOI) != 0) {
int isr_bit;
if ((val & OCW2_SL) != 0) {
/* specific EOI */
isr_bit = val & 0x7;
} else {
/* non-specific EOI */
isr_bit = vpic_get_highest_isrpin(pic);
}
if (isr_bit != -1) {
pic->service &= ~(1 << isr_bit);
if (pic->rotate)
pic->lowprio = isr_bit;
}
/* if level ack PTDEV */
if (pic->elc & (1 << (isr_bit & 0x7))) {
ptdev_intx_ack(vpic->vm,
master_pic(vpic, pic) ? isr_bit : isr_bit + 8,
PTDEV_VPIN_PIC);
}
} else if ((val & OCW2_SL) != 0 && pic->rotate == true) {
/* specific priority */
pic->lowprio = val & 0x7;
}
return 0;
}
static int vpic_ocw3(__unused struct vpic *vpic, struct pic *pic, uint8_t val)
{
dev_dbg(ACRN_DBG_PIC, "vm 0x%x: pic ocw3 0x%x\n",
vpic->vm, val);
if (val & OCW3_ESMM) {
pic->smm = val & OCW3_SMM ? 1 : 0;
dev_dbg(ACRN_DBG_PIC, "%s pic special mask mode %s\n",
master_pic(vpic, pic) ? "master" : "slave",
pic->smm ? "enabled" : "disabled");
}
if (val & OCW3_RR) {
/* read register command */
pic->rd_cmd_reg = val & OCW3_RIS;
/* Polling mode */
pic->poll = ((val & OCW3_P) != 0);
}
return 0;
}
static void vpic_set_pinstate(struct vpic *vpic, int pin, bool newstate)
{
struct pic *pic;
int oldcnt, newcnt;
bool level;
ASSERT(pin >= 0 && pin < 16,
"vpic_set_pinstate: invalid pin number");
pic = &vpic->pic[pin >> 3];
oldcnt = pic->acnt[pin & 0x7];
if (newstate)
pic->acnt[pin & 0x7]++;
else
pic->acnt[pin & 0x7]--;
newcnt = pic->acnt[pin & 0x7];
if (newcnt < 0) {
pr_warn("pic pin%d: bad acnt %d\n", pin, newcnt);
}
level = ((vpic->pic[pin >> 3].elc & (1 << (pin & 0x7))) != 0);
if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) {
/* rising edge or level */
dev_dbg(ACRN_DBG_PIC, "pic pin%d: asserted\n", pin);
pic->request |= (1 << (pin & 0x7));
} else if (oldcnt == 1 && newcnt == 0) {
/* falling edge */
dev_dbg(ACRN_DBG_PIC, "pic pin%d: deasserted\n", pin);
if (level)
pic->request &= ~(1 << (pin & 0x7));
} else {
dev_dbg(ACRN_DBG_PIC,
"pic pin%d: %s, ignored, acnt %d\n",
pin, newstate ? "asserted" : "deasserted", newcnt);
}
vpic_notify_intr(vpic);
}
static int vpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
{
struct vpic *vpic;
struct pic *pic;
if (irq < 0 || irq > 15)
return -EINVAL;
vpic = vm_pic(vm);
pic = &vpic->pic[irq >> 3];
if (pic->ready == false)
return 0;
VPIC_LOCK(vpic);
switch (irqstate) {
case IRQSTATE_ASSERT:
vpic_set_pinstate(vpic, irq, true);
break;
case IRQSTATE_DEASSERT:
vpic_set_pinstate(vpic, irq, false);
break;
case IRQSTATE_PULSE:
vpic_set_pinstate(vpic, irq, true);
vpic_set_pinstate(vpic, irq, false);
break;
default:
ASSERT(0, "vpic_set_irqstate: invalid irqstate");
}
VPIC_UNLOCK(vpic);
return 0;
}
/* hypervisor interface: assert/deassert/pulse irq */
int vpic_assert_irq(struct vm *vm, int irq)
{
return vpic_set_irqstate(vm, irq, IRQSTATE_ASSERT);
}
int vpic_deassert_irq(struct vm *vm, int irq)
{
return vpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT);
}
int vpic_pulse_irq(struct vm *vm, int irq)
{
return vpic_set_irqstate(vm, irq, IRQSTATE_PULSE);
}
int vpic_set_irq_trigger(struct vm *vm, int irq, enum vpic_trigger trigger)
{
struct vpic *vpic;
if (irq < 0 || irq > 15)
return -EINVAL;
/*
* See comment in vpic_elc_handler. These IRQs must be
* edge triggered.
*/
if (trigger == LEVEL_TRIGGER) {
switch (irq) {
case 0:
case 1:
case 2:
case 8:
case 13:
return -EINVAL;
}
}
vpic = vm_pic(vm);
VPIC_LOCK(vpic);
if (trigger == LEVEL_TRIGGER)
vpic->pic[irq >> 3].elc |= 1 << (irq & 0x7);
else
vpic->pic[irq >> 3].elc &= ~(1 << (irq & 0x7));
VPIC_UNLOCK(vpic);
return 0;
}
int vpic_get_irq_trigger(struct vm *vm, int irq, enum vpic_trigger *trigger)
{
struct vpic *vpic;
if (irq < 0 || irq > 15)
return -EINVAL;
vpic = vm_pic(vm);
if (!vpic)
return -EINVAL;
if (vpic->pic[irq>>3].elc & (1 << (irq & 0x7)))
*trigger = LEVEL_TRIGGER;
else
*trigger = EDGE_TRIGGER;
return 0;
}
void vpic_pending_intr(struct vm *vm, int *vecptr)
{
struct vpic *vpic;
struct pic *pic;
int pin;
vpic = vm_pic(vm);
pic = &vpic->pic[0];
VPIC_LOCK(vpic);
pin = vpic_get_highest_irrpin(pic);
if (pin == 2) {
pic = &vpic->pic[1];
pin = vpic_get_highest_irrpin(pic);
}
/*
* If there are no pins active at this moment then return the spurious
* interrupt vector instead.
*/
if (pin == -1) {
*vecptr = -1;
VPIC_UNLOCK(vpic);
return;
}
ASSERT(pin >= 0 && pin <= 7, "invalid pin");
*vecptr = pic->irq_base + pin;
dev_dbg(ACRN_DBG_PIC, "Got pending vector 0x%x\n", *vecptr);
VPIC_UNLOCK(vpic);
}
static void vpic_pin_accepted(struct pic *pic, int pin)
{
pic->intr_raised = false;
if ((pic->elc & (1 << pin)) == 0) {
/*only used edge trigger mode*/
pic->request &= ~(1 << pin);
}
if (pic->aeoi == true) {
if (pic->rotate == true)
pic->lowprio = pin;
} else {
pic->service |= (1 << pin);
}
}
void vpic_intr_accepted(struct vm *vm, int vector)
{
struct vpic *vpic;
int pin;
vpic = vm_pic(vm);
VPIC_LOCK(vpic);
pin = vector & 0x7;
if ((vector & ~0x7) == vpic->pic[1].irq_base) {
vpic_pin_accepted(&vpic->pic[1], pin);
/*
* If this vector originated from the slave,
* accept the cascaded interrupt too.
*/
vpic_pin_accepted(&vpic->pic[0], 2);
} else {
vpic_pin_accepted(&vpic->pic[0], pin);
}
vpic_notify_intr(vpic);
VPIC_UNLOCK(vpic);
}
static int vpic_read(struct vpic *vpic, struct pic *pic,
int port, uint32_t *eax)
{
int pin;
VPIC_LOCK(vpic);
if (pic->poll) {
pic->poll = 0;
pin = vpic_get_highest_irrpin(pic);
if (pin >= 0) {
vpic_pin_accepted(pic, pin);
*eax = 0x80 | pin;
} else {
*eax = 0;
}
} else {
if (port & ICU_IMR_OFFSET) {
/* read interrupt mask register */
*eax = pic->mask;
} else {
if (pic->rd_cmd_reg == OCW3_RIS) {
/* read interrupt service register */
*eax = pic->service;
} else {
/* read interrupt request register */
*eax = pic->request;
}
}
}
VPIC_UNLOCK(vpic);
return 0;
}
static int vpic_write(struct vpic *vpic, struct pic *pic,
int port, uint32_t *eax)
{
int error;
uint8_t val;
error = 0;
val = *eax;
VPIC_LOCK(vpic);
if (port & ICU_IMR_OFFSET) {
switch (pic->icw_num) {
case 2:
error = vpic_icw2(vpic, pic, val);
break;
case 3:
error = vpic_icw3(vpic, pic, val);
break;
case 4:
error = vpic_icw4(vpic, pic, val);
break;
default:
error = vpic_ocw1(vpic, pic, val);
break;
}
} else {
if (val & (1 << 4))
error = vpic_icw1(vpic, pic, val);
if (pic->ready) {
if (val & (1 << 3))
error = vpic_ocw3(vpic, pic, val);
else
error = vpic_ocw2(vpic, pic, val);
}
}
if (pic->ready)
vpic_notify_intr(vpic);
VPIC_UNLOCK(vpic);
return error;
}
static int vpic_master_handler(struct vm *vm, bool in, int port, int bytes,
uint32_t *eax)
{
struct vpic *vpic;
struct pic *pic;
vpic = vm_pic(vm);
pic = &vpic->pic[0];
if (bytes != 1)
return -1;
if (in)
return vpic_read(vpic, pic, port, eax);
return vpic_write(vpic, pic, port, eax);
}
static uint32_t vpic_master_io_read(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width)
{
uint32_t val = 0;
if (vpic_master_handler(vm, true, (int)addr, (int)width, &val) < 0)
pr_err("pic master read port 0x%x width=%d failed\n",
addr, width);
return val;
}
static void vpic_master_io_write(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width, uint32_t v)
{
uint32_t val = v;
if (vpic_master_handler(vm, false, (int)addr, (int)width, &val) < 0)
pr_err("%s: write port 0x%x width=%d value 0x%x failed\n",
__func__, addr, width, val);
}
static int vpic_slave_handler(struct vm *vm, bool in, int port, int bytes,
uint32_t *eax)
{
struct vpic *vpic;
struct pic *pic;
vpic = vm_pic(vm);
pic = &vpic->pic[1];
if (bytes != 1)
return -1;
if (in)
return vpic_read(vpic, pic, port, eax);
return vpic_write(vpic, pic, port, eax);
}
static uint32_t vpic_slave_io_read(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width)
{
uint32_t val = 0;
if (vpic_slave_handler(vm, true, (int)addr, (int)width, &val) < 0)
pr_err("pic slave read port 0x%x width=%d failed\n",
addr, width);
return val;
}
static void vpic_slave_io_write(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width, uint32_t v)
{
uint32_t val = v;
if (vpic_slave_handler(vm, false, (int)addr, (int)width, &val) < 0)
pr_err("%s: write port 0x%x width=%d value 0x%x failed\n",
__func__, addr, width, val);
}
static int vpic_elc_handler(struct vm *vm, bool in, int port, int bytes,
uint32_t *eax)
{
struct vpic *vpic;
bool is_master;
vpic = vm_pic(vm);
is_master = (port == IO_ELCR1);
if (bytes != 1)
return -1;
VPIC_LOCK(vpic);
if (in) {
if (is_master)
*eax = vpic->pic[0].elc;
else
*eax = vpic->pic[1].elc;
} else {
/*
* For the master PIC the cascade channel (IRQ2), the
* heart beat timer (IRQ0), and the keyboard
* controller (IRQ1) cannot be programmed for level
* mode.
*
* For the slave PIC the real time clock (IRQ8) and
* the floating point error interrupt (IRQ13) cannot
* be programmed for level mode.
*/
if (is_master)
vpic->pic[0].elc = (*eax & 0xf8);
else
vpic->pic[1].elc = (*eax & 0xde);
}
VPIC_UNLOCK(vpic);
return 0;
}
static uint32_t vpic_elc_io_read(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width)
{
uint32_t val = 0;
if (vpic_elc_handler(vm, true, (int)addr, (int)width, &val) < 0)
pr_err("pic elc read port 0x%x width=%d failed", addr, width);
return val;
}
static void vpic_elc_io_write(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t addr, size_t width, uint32_t v)
{
uint32_t val = v;
if (vpic_elc_handler(vm, false, (int)addr, (int)width, &val) < 0)
pr_err("%s: write port 0x%x width=%d value 0x%x failed\n",
__func__, addr, width, val);
}
void vpic_register_io_handler(struct vm *vm)
{
struct vm_io_range master_range = {
.flags = IO_ATTR_RW,
.base = 0x20,
.len = 2
};
struct vm_io_range slave_range = {
.flags = IO_ATTR_RW,
.base = 0xa0,
.len = 2
};
struct vm_io_range elcr_range = {
.flags = IO_ATTR_RW,
.base = 0x4d0,
.len = 2
};
register_io_emulation_handler(vm, &master_range,
&vpic_master_io_read, &vpic_master_io_write);
register_io_emulation_handler(vm, &slave_range,
&vpic_slave_io_read, &vpic_slave_io_write);
register_io_emulation_handler(vm, &elcr_range,
&vpic_elc_io_read, &vpic_elc_io_write);
}
void *vpic_init(struct vm *vm)
{
struct vpic *vpic;
vpic_register_io_handler(vm);
vpic = malloc(sizeof(struct vpic));
ASSERT(vpic != NULL, "");
vpic->vm = vm;
vpic->pic[0].mask = 0xff;
vpic->pic[1].mask = 0xff;
VPIC_LOCK_INIT(vpic);
return vpic;
}
void vpic_cleanup(struct vm *vm)
{
if (vm->vpic) {
free(vm->vpic);
vm->vpic = NULL;
}
}

441
hypervisor/arch/x86/idt.S Normal file
View File

@@ -0,0 +1,441 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <idt.h>
.altmacro
.global HOST_IDT
.global HOST_IDTR
.section .data
.align 8
.long 0
.short 0
HOST_IDTR:
.short HOST_IDT_SIZE - 1
.quad HOST_IDT
/*
* We'll rearrange and fix up the descriptors at runtime
*/
.macro interrupt_descriptor entry, dpl=0 ist=0
.long HOST_GDT_RING0_CODE_SEL << 16
.long 0x00008e00 + (dpl << 13) + ist
.quad entry
.endm
.macro trap_descriptor entry, dpl=0, ist=0
.long HOST_GDT_RING0_CODE_SEL << 16
.long 0x00008f00 + (dpl <<13) + ist
.quad entry
.endm
.macro _external_interrupt_descriptor vector
__external_interrupt_descriptor %vector
.endm
.macro __external_interrupt_descriptor vector
interrupt_descriptor external_interrupt_\vector
.endm
#define MACHINE_CHECK_IST (0x1)
#define DOUBLE_FAULT_IST (0x2)
#define STACK_FAULT_IST (0x3)
/*
* We'll use interrupt gates. Change to trap or task only as needed.
*/
.section .rodata
.align 16
HOST_IDT:
interrupt_descriptor excp_divide_error
interrupt_descriptor excp_debug, 3
interrupt_descriptor excp_nmi
interrupt_descriptor excp_breakpoint, 3
interrupt_descriptor excp_overflow, 3
interrupt_descriptor excp_bounds_check
interrupt_descriptor excp_illegal_opcode
interrupt_descriptor excp_device_not_available
interrupt_descriptor excp_double_fault, 0, DOUBLE_FAULT_IST
interrupt_descriptor excp_rsvd_09
interrupt_descriptor excp_invalid_tss
interrupt_descriptor excp_segment_not_present
interrupt_descriptor excp_stack_fault, 0, STACK_FAULT_IST
interrupt_descriptor excp_general_protection
interrupt_descriptor excp_page_fault
interrupt_descriptor excp_rsvd_0f
interrupt_descriptor excp_float_error
interrupt_descriptor excp_alignment_check
interrupt_descriptor expt_machine_check, 0, MACHINE_CHECK_IST
interrupt_descriptor excp_simd_fp_error
interrupt_descriptor excp_virtualization
interrupt_descriptor excp_rsvd_21
interrupt_descriptor excp_rsvd_22
interrupt_descriptor excp_rsvd_23
interrupt_descriptor excp_rsvd_24
interrupt_descriptor excp_rsvd_25
interrupt_descriptor excp_rsvd_26
interrupt_descriptor excp_rsvd_27
interrupt_descriptor excp_rsvd_28
interrupt_descriptor excp_rsvd_29
interrupt_descriptor excp_rsvd_30
interrupt_descriptor excp_rsvd_31
vector = 0x20
.rept (0x100 - 0x20)
_external_interrupt_descriptor vector
vector = vector + 1
.endr
.section .text
.align 16
excp_divide_error:
pushq $0x0 /* pseudo error code */
pushq $0x00
jmp excp_save_frame
.align 8
excp_debug:
pushq $0x0 /* pseudo error code */
pushq $0x01
jmp excp_save_frame
.align 8
excp_nmi:
.align 8
excp_breakpoint:
pushq $0x0 /* pseudo error code */
pushq $0x03
jmp excp_save_frame
.align 8
excp_overflow:
pushq $0x0 /* pseudo error code */
pushq $0x04
jmp excp_save_frame
.align 8
excp_bounds_check:
pushq $0x0 /* pseudo error code */
pushq $0x05
jmp excp_save_frame
.align 8
excp_illegal_opcode:
pushq $0x0 /* pseudo error code */
pushq $0x06
jmp excp_save_frame
.align 8
excp_device_not_available:
pushq $0x0 /* pseudo error code */
pushq $0x07
jmp excp_save_frame
.align 8
excp_double_fault:
pushq $0x08
jmp excp_save_frame
.align 8
excp_invalid_tss:
pushq $0x0A
jmp excp_save_frame
.align 8
excp_segment_not_present:
pushq $0x0B
jmp excp_save_frame
.align 8
excp_stack_fault:
pushq $0x0C
jmp excp_save_frame
.align 8
excp_general_protection:
pushq $0x0D
jmp excp_save_frame
.align 8
excp_page_fault:
pushq $0x0E
jmp excp_save_frame
.align 8
excp_float_error:
pushq $0x0 /* pseudo error code */
pushq $0x10
jmp excp_save_frame
.align 8
excp_alignment_check:
pushq $0x11
jmp excp_save_frame
.align 8
expt_machine_check:
pushq $0x0 /* pseudo error code */
pushq $0x12
jmp excp_save_frame
.align 8
excp_simd_fp_error:
pushq $0x0 /* pseudo error code */
pushq $0x13
jmp excp_save_frame
.align 8
excp_virtualization:
pushq $0x0 /* pseudo error code */
pushq $0x14
jmp excp_save_frame
/*
* Macros for rsvd vectors. Vectors 0x09, 0x0F, 0x15 through 0x1F
*/
.macro _rsvd_vector vector
__rsvd_vector %vector
.endm
.macro __rsvd_vector vector
.align 8
excp_rsvd_\vector\():
pushq $0x0 /* pseudo error code */
pushq $\vector
jmp excp_rsvd
.endm
.align 8
excp_rsvd_09:
_rsvd_vector 0x09
.align 8
excp_rsvd_0f:
_rsvd_vector 0x0f
vector = 0x15
.rept (0x20 - 0x15)
_rsvd_vector vector
vector = vector + 1
.endr
/*
* Macros for external interrupts. Vectors$0x20 through$0xFF
*/
.macro _external_interrupt vector
__external_interrupt %vector
.endm
.macro __external_interrupt vector
.align 8
external_interrupt_\vector\():
pushq $0x0 /* pseudo error code */
pushq $\vector
jmp external_interrupt_save_frame
.endm
vector =0x20
.rept (0x100 - 0x20)
_external_interrupt vector
vector = vector + 1
.endr
/*
* Common entry point for defined exceptions
*/
.align 8
excp_save_frame:
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %rax
pushq %rbp
pushq %rbx
pushq %r15
pushq %r14
pushq %r13
pushq %r12
/* Put current stack pointer into 1st param register (rdi) */
movq %rsp, %rdi
call dispatch_exception
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbx
popq %rbp
popq %rax
popq %rcx
popq %rdx
popq %rsi
popq %rdi
popq %r8
popq %r9
popq %r10
popq %r11
/* Skip vector and error code*/
add $16, %rsp
iretq
/*
* Common entry point for reserved exceptions.
* These should never execute.
* We put a handler on them anyway to highlight the unexpected.
*/
.align 8
excp_rsvd:
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %rax
pushq %rbp
pushq %rbx
pushq %r15
pushq %r14
pushq %r13
pushq %r12
/* Put current stack pointer into 1st param register (rdi) */
movq %rsp, %rdi
call dispatch_exception
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbx
popq %rbp
popq %rax
popq %rcx
popq %rdx
popq %rsi
popq %rdi
popq %r8
popq %r9
popq %r10
popq %r11
/* Skip vector and error code*/
add $16, %rsp
iretq
/*
* Common entry point for defined interrupts.
* Vectors 0x20 through 0xFF
*/
.align 8
external_interrupt_save_frame:
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %rax
pushq %rbp
pushq %rbx
pushq %r15
pushq %r14
pushq %r13
pushq %r12
/* Put current stack pointer into 1st param register (rdi) */
movq %rsp, %rdi
call dispatch_interrupt
/*
* We disable softirq path from interrupt IRET, since right now all IRQ
* are for Guest, and we can execute softirq in hv_main() loop
*/
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbx
popq %rbp
popq %rax
popq %rcx
popq %rdx
popq %rsi
popq %rdi
popq %r8
popq %r9
popq %r10
popq %r11
/* Skip vector and error code*/
add $16, %rsp
iretq

View File

@@ -0,0 +1,431 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define EXCEPTION_ERROR_CODE_VALID 8
#define INTERRPUT_QUEUE_BUFF_SIZE 255
#define ACRN_DBG_INTR 6
static const uint16_t exception_type[] = {
[0] = VMX_INT_TYPE_HW_EXP,
[1] = VMX_INT_TYPE_HW_EXP,
[2] = VMX_INT_TYPE_HW_EXP,
[3] = VMX_INT_TYPE_HW_EXP,
[4] = VMX_INT_TYPE_HW_EXP,
[5] = VMX_INT_TYPE_HW_EXP,
[6] = VMX_INT_TYPE_HW_EXP,
[7] = VMX_INT_TYPE_HW_EXP,
[8] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[9] = VMX_INT_TYPE_HW_EXP,
[10] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[11] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[12] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[13] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[14] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[15] = VMX_INT_TYPE_HW_EXP,
[16] = VMX_INT_TYPE_HW_EXP,
[17] = VMX_INT_TYPE_HW_EXP | EXCEPTION_ERROR_CODE_VALID,
[18] = VMX_INT_TYPE_HW_EXP,
[19] = VMX_INT_TYPE_HW_EXP,
[20] = VMX_INT_TYPE_HW_EXP,
[21] = VMX_INT_TYPE_HW_EXP,
[22] = VMX_INT_TYPE_HW_EXP,
[23] = VMX_INT_TYPE_HW_EXP,
[24] = VMX_INT_TYPE_HW_EXP,
[25] = VMX_INT_TYPE_HW_EXP,
[26] = VMX_INT_TYPE_HW_EXP,
[27] = VMX_INT_TYPE_HW_EXP,
[28] = VMX_INT_TYPE_HW_EXP,
[29] = VMX_INT_TYPE_HW_EXP,
[30] = VMX_INT_TYPE_HW_EXP,
[31] = VMX_INT_TYPE_HW_EXP
};
static int is_guest_irq_enabled(struct vcpu *vcpu)
{
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
uint32_t guest_rflags, guest_state;
int status = false;
/* Read the RFLAGS of the guest */
guest_rflags = cur_context->rflags;
/* Check the RFLAGS[IF] bit first */
if (guest_rflags & HV_ARCH_VCPU_RFLAGS_IF) {
/* Interrupts are allowed */
/* Check for temporarily disabled interrupts */
guest_state = exec_vmread(VMX_GUEST_INTERRUPTIBILITY_INFO);
if ((guest_state & (HV_ARCH_VCPU_BLOCKED_BY_STI |
HV_ARCH_VCPU_BLOCKED_BY_MOVSS)) == 0) {
status = true;
}
}
return status;
}
static bool vcpu_pending_request(struct vcpu *vcpu)
{
struct vlapic *vlapic;
int vector = 0;
int ret = 0;
/* Query vLapic to get vector to inject */
vlapic = vcpu->arch_vcpu.vlapic;
ret = vlapic_pending_intr(vlapic, &vector);
/* we need to check and raise request if we have pending event
* in LAPIC IRR
*/
if (ret != 0) {
/* we have pending IRR */
vcpu_make_request(vcpu, ACRN_REQUEST_EVENT);
}
return vcpu->arch_vcpu.pending_intr != 0;
}
int vcpu_make_request(struct vcpu *vcpu, int eventid)
{
bitmap_set(eventid, &vcpu->arch_vcpu.pending_intr);
/*
* if current hostcpu is not the target vcpu's hostcpu, we need
* to invoke IPI to wake up target vcpu
*
* TODO: Here we just compare with cpuid, since cpuid currently is
* global under pCPU / vCPU 1:1 mapping. If later we enabled vcpu
* scheduling, we need change here to determine it target vcpu is
* VMX non-root or root mode
*/
if ((int)get_cpu_id() != vcpu->pcpu_id)
send_single_ipi(vcpu->pcpu_id, VECTOR_NOTIFY_VCPU);
return 0;
}
static int vcpu_do_pending_event(struct vcpu *vcpu)
{
struct vlapic *vlapic = vcpu->arch_vcpu.vlapic;
int vector = 0;
int ret = 0;
if (is_apicv_enabled()) {
apicv_inject_pir(vlapic);
return 0;
}
/* Query vLapic to get vector to inject */
ret = vlapic_pending_intr(vlapic, &vector);
/*
* From the Intel SDM, Volume 3, 6.3.2 Section "Maskable
* Hardware Interrupts":
* - maskable interrupt vectors [16,255] can be delivered
* through the local APIC.
*/
if (ret == 0)
return -1;
if (!(vector >= 16 && vector <= 255)) {
dev_dbg(ACRN_DBG_INTR, "invalid vector %d from local APIC",
vector);
return -1;
}
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD, VMX_INT_INFO_VALID |
(vector & 0xFF));
vlapic_intr_accepted(vlapic, vector);
return 0;
}
static int vcpu_do_pending_extint(struct vcpu *vcpu)
{
struct vm *vm;
struct vcpu *primary;
int vector;
vm = vcpu->vm;
/* check if there is valid interrupt from vPIC, if yes just inject it */
/* PIC only connect with primary CPU */
primary = get_primary_vcpu(vm);
if (vm->vpic && vcpu == primary) {
vpic_pending_intr(vcpu->vm, &vector);
if (vector > 0) {
dev_dbg(ACRN_DBG_INTR, "VPIC: to inject PIC vector %d\n",
vector & 0xFF);
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
VMX_INT_INFO_VALID |
(vector & 0xFF));
vpic_intr_accepted(vcpu->vm, vector);
}
}
return 0;
}
static int vcpu_do_pending_gp(__unused struct vcpu *vcpu)
{
/* GP vector = 13 */
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
VMX_INT_INFO_VALID | 13);
return 0;
}
/* please keep this for interrupt debug:
* 1. Timer alive or not
* 2. native LAPIC interrupt pending/EOI status
* 3. CPU stuck or not
*/
void dump_lapic(void)
{
dev_dbg(ACRN_DBG_INTR,
"LAPIC: TIME %08x, init=0x%x cur=0x%x ISR=0x%x IRR=0x%x",
mmio_read_long(0xFEE00000 + LAPIC_LVT_TIMER_REGISTER),
mmio_read_long(0xFEE00000 + LAPIC_INITIAL_COUNT_REGISTER),
mmio_read_long(0xFEE00000 + LAPIC_CURRENT_COUNT_REGISTER),
mmio_read_long(0xFEE00000 + LAPIC_IN_SERVICE_REGISTER_7),
mmio_read_long(0xFEE00000 + LAPIC_INT_REQUEST_REGISTER_7));
}
int vcpu_inject_extint(struct vcpu *vcpu)
{
return vcpu_make_request(vcpu, ACRN_REQUEST_EXTINT);
}
int vcpu_inject_nmi(struct vcpu *vcpu)
{
return vcpu_make_request(vcpu, ACRN_REQUEST_NMI);
}
int vcpu_inject_gp(struct vcpu *vcpu)
{
return vcpu_make_request(vcpu, ACRN_REQUEST_GP);
}
int interrupt_win_exiting_handler(struct vcpu *vcpu)
{
int value32;
TRACE_2L(TRC_VMEXIT_INTERRUPT_WINDOW, 0, 0);
if (!vcpu)
return -1;
if (vcpu_pending_request(vcpu)) {
/* Do nothing
* acrn_do_intr_process will continue for this vcpu
*/
} else {
/* No interrupts to inject.
* Disable the interrupt window exiting
*/
vcpu->arch_vcpu.irq_window_enabled = 0;
value32 = exec_vmread(VMX_PROC_VM_EXEC_CONTROLS);
value32 &= ~(VMX_PROCBASED_CTLS_IRQ_WIN);
exec_vmwrite(VMX_PROC_VM_EXEC_CONTROLS, value32);
}
VCPU_RETAIN_RIP(vcpu);
return 0;
}
int external_interrupt_handler(struct vcpu *vcpu)
{
int vector = exec_vmread(VMX_EXIT_INT_INFO) & 0xFF;
struct intr_ctx ctx;
ctx.vector = vector;
/* do not RETAIN RIP for spurious interrupt */
if (dispatch_interrupt(&ctx) == 0)
VCPU_RETAIN_RIP(vcpu);
TRACE_2L(TRC_VMEXIT_EXTERNAL_INTERRUPT, vector, 0);
return 0;
}
int acrn_do_intr_process(struct vcpu *vcpu)
{
int ret = 0;
int vector;
int tmp;
bool intr_pending = false;
uint64_t *pending_intr_bits = &vcpu->arch_vcpu.pending_intr;
if (bitmap_test_and_clear(ACRN_REQUEST_TLB_FLUSH, pending_intr_bits))
mmu_invept(vcpu);
if (bitmap_test_and_clear(ACRN_REQUEST_TMR_UPDATE, pending_intr_bits))
vioapic_update_tmr(vcpu);
/* handling pending vector injection:
* there are many reason inject failed, we need re-inject again
*/
if (vcpu->arch_vcpu.exit_interrupt_info & VMX_INT_INFO_VALID) {
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
vcpu->arch_vcpu.exit_interrupt_info);
goto INTR_WIN;
}
/* handling exception request */
vector = vcpu->arch_vcpu.exception_info.exception;
/* If there is a valid exception, inject exception to guest */
if (vector >= 0) {
if (exception_type[vector] &
EXCEPTION_ERROR_CODE_VALID) {
exec_vmwrite(VMX_ENTRY_EXCEPTION_EC,
vcpu->arch_vcpu.exception_info.error);
}
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
VMX_INT_INFO_VALID |
((exception_type[vector] & 15) << 8)
| (vector & 0xFF));
vcpu->arch_vcpu.exception_info.exception = -1;
goto INTR_WIN;
}
/* Do pending interrupts process */
/* TODO: checkin NMI intr windows before inject */
if (bitmap_test_and_clear(ACRN_REQUEST_NMI, pending_intr_bits)) {
/* Inject NMI vector = 2 */
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
VMX_INT_INFO_VALID | (VMX_INT_TYPE_NMI << 8) | 2);
/* Intel SDM 10.8.1
* NMI, SMI, INIT, ExtINT, or SIPI directly deliver to CPU
* do not need EOI to LAPIC
* However, ExtINT need EOI to PIC
*/
goto INTR_WIN;
}
/* Guest interruptable or not */
if (!is_guest_irq_enabled(vcpu)) {
/* interrupt window unavailable */
goto INTR_WIN;
}
/* Inject external interrupt first */
if (bitmap_test_and_clear(ACRN_REQUEST_EXTINT, pending_intr_bits)) {
/* has pending external interrupts */
ret = vcpu_do_pending_extint(vcpu);
goto INTR_WIN;
}
/* Inject vLAPIC vectors */
if (bitmap_test_and_clear(ACRN_REQUEST_EVENT, pending_intr_bits)) {
/* has pending vLAPIC interrupts */
ret = vcpu_do_pending_event(vcpu);
goto INTR_WIN;
}
/* Inject GP event */
if (bitmap_test_and_clear(ACRN_REQUEST_GP, pending_intr_bits)) {
/* has pending GP interrupts */
ret = vcpu_do_pending_gp(vcpu);
goto INTR_WIN;
}
INTR_WIN:
/* check if we have new interrupt pending for next VMExit */
intr_pending = vcpu_pending_request(vcpu);
/* Enable interrupt window exiting if pending */
if (intr_pending && vcpu->arch_vcpu.irq_window_enabled == 0) {
vcpu->arch_vcpu.irq_window_enabled = 1;
tmp = exec_vmread(VMX_PROC_VM_EXEC_CONTROLS);
tmp |= (VMX_PROCBASED_CTLS_IRQ_WIN);
exec_vmwrite(VMX_PROC_VM_EXEC_CONTROLS, tmp);
}
return ret;
}
int exception_handler(struct vcpu *vcpu)
{
uint32_t intinfo, int_err_code;
uint32_t exception_vector;
uint32_t cpl;
int status = 0;
if (vcpu == NULL) {
TRACE_4I(TRC_VMEXIT_EXCEPTION_OR_NMI, 0, 0, 0, 0);
status = -EINVAL;
}
if (status != 0)
return status;
pr_dbg(" Handling guest exception");
/* Obtain VM-Exit information field pg 2912 */
intinfo = exec_vmread(VMX_EXIT_INT_INFO);
exception_vector = intinfo & 0xFF;
/* Check if exception caused by the guest is a HW exception. If the
* exit occurred due to a HW exception obtain the error code to be
* conveyed to get via the stack
*/
if (intinfo & VMX_INT_INFO_ERR_CODE_VALID) {
int_err_code = exec_vmread(VMX_EXIT_INT_EC);
/* get current privilege level and fault address */
cpl = exec_vmread(VMX_GUEST_CS_ATTR);
cpl = (cpl >> 5) & 3;
if (cpl < 3)
int_err_code &= ~4;
else
int_err_code |= 4;
} else {
int_err_code = 0;
}
/* Handle all other exceptions */
VCPU_RETAIN_RIP(vcpu);
vcpu->arch_vcpu.exception_info.exception = exception_vector;
vcpu->arch_vcpu.exception_info.error = int_err_code;
TRACE_4I(TRC_VMEXIT_EXCEPTION_OR_NMI,
exception_vector, int_err_code, 2, 0);
return status;
}

View File

@@ -0,0 +1,418 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <hv_debug.h>
/* Rate range 1 to 1000 or 1uSec to 1mSec */
#define APIC_TIMER_MAX 0xffffffff
#define HYPE_PERIOD_MAX 1000
#define APIC_DIVIDE_BY_ONE 0x0b
#define PIT_TARGET 0x3FFF
/* xAPIC/x2APIC Interrupt Command Register (ICR) structure */
union apic_icr {
uint64_t value;
struct {
uint32_t lo_32;
uint32_t hi_32;
} value_32;
struct {
uint64_t vector:8;
uint64_t delivery_mode:3;
uint64_t destination_mode:1;
uint64_t delivery_status:1;
uint64_t rsvd_1:1;
uint64_t level:1;
uint64_t trigger_mode:1;
uint64_t rsvd_2:2;
uint64_t shorthand:2;
uint64_t rsvd_3:12;
uint64_t rsvd_4:32;
} bits;
struct {
uint64_t rsvd_1:32;
uint64_t rsvd_2:24;
uint64_t dest_field:8;
} x_bits;
struct {
uint64_t rsvd_1:32;
uint64_t dest_field:32;
} x2_bits;
};
/* xAPIC/x2APIC Interrupt Command Register (ICR) structure */
union apic_lvt {
uint32_t value;
union {
struct {
uint32_t vector:8;
uint32_t rsvd_1:4;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t mode:2;
uint32_t rsvd_3:13;
} timer;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t rsvd_1:1;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t rsvd_3:15;
} cmci;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t rsvd_1:1;
uint32_t delivery_status:1;
uint32_t polarity:1;
uint32_t remote_irr:1;
uint32_t trigger_mode:1;
uint32_t mask:1;
uint32_t rsvd_2:15;
} lint;
struct {
uint32_t vector:8;
uint32_t rsvd_1:4;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t rsvd_3:15;
} error;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t rsvd_1:1;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t rsvd_3:15;
} pmc;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t rsvd_1:1;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t rsvd_3:15;
} thermal;
struct {
uint32_t vector:8;
uint32_t rsvd_1:4;
uint32_t delivery_status:1;
uint32_t rsvd_2:3;
uint32_t mask:1;
uint32_t rsvd_3:15;
} common;
} bits;
};
union lapic_base_msr {
uint64_t value;
struct {
uint64_t rsvd_1:8;
uint64_t bsp:1;
uint64_t rsvd_2:1;
uint64_t x2APIC_enable:1;
uint64_t xAPIC_enable:1;
uint64_t lapic_paddr:24;
uint64_t rsvd_3:28;
} fields;
};
struct lapic_info {
int init_status;
struct {
paddr_t paddr;
vaddr_t vaddr;
} xapic;
};
static struct lapic_info lapic_info;
static uint32_t read_lapic_reg32(uint32_t offset)
{
ASSERT((offset >= 0x020) && (offset <= 0x3FF), "");
return mmio_read_long(lapic_info.xapic.vaddr + offset);
}
static void write_lapic_reg32(uint32_t offset, uint32_t value)
{
ASSERT((offset >= 0x020) && (offset <= 0x3FF), "");
mmio_write_long(value, lapic_info.xapic.vaddr + offset);
}
static void clear_lapic_isr(void)
{
uint64_t isr_reg = LAPIC_IN_SERVICE_REGISTER_0;
/* This is a Intel recommended procedure and assures that the processor
* does not get hung up due to already set "in-service" interrupts left
* over from the boot loader environment. This actually occurs in real
* life, therefore we will ensure all the in-service bits are clear.
*/
do {
if (read_lapic_reg32(isr_reg)) {
write_lapic_reg32(LAPIC_EOI_REGISTER, 0);
continue;
}
isr_reg += 0x10;
} while (isr_reg <= LAPIC_IN_SERVICE_REGISTER_7);
}
static void map_lapic(void)
{
/* At some point we may need to translate this paddr to a vaddr. 1:1
* mapping for now.
*/
lapic_info.xapic.vaddr = lapic_info.xapic.paddr;
}
int early_init_lapic(void)
{
union lapic_base_msr lapic_base_msr;
/* Get local APIC base address */
lapic_base_msr.value = msr_read(MSR_IA32_APIC_BASE);
/* Initialize globals only 1 time */
if (lapic_info.init_status == false) {
/* Get Local APIC physical address. */
lapic_info.xapic.paddr = LAPIC_BASE;
/* Map in the local xAPIC */
map_lapic();
lapic_info.init_status = true;
}
/* Check if xAPIC mode enabled */
if (lapic_base_msr.fields.xAPIC_enable == 0) {
/* Ensure in xAPIC mode */
lapic_base_msr.fields.xAPIC_enable = 1;
lapic_base_msr.fields.x2APIC_enable = 0;
msr_write(MSR_IA32_APIC_BASE, lapic_base_msr.value);
} else {
/* Check if x2apic is disabled */
ASSERT(lapic_base_msr.fields.x2APIC_enable == 0,
"Disable X2APIC in BIOS");
}
return 0;
}
int init_lapic(uint32_t cpu_id)
{
/* Set the Logical Destination Register */
write_lapic_reg32(LAPIC_LOGICAL_DESTINATION_REGISTER,
(1 << cpu_id) << 24);
/* Set the Destination Format Register */
write_lapic_reg32(LAPIC_DESTINATION_FORMAT_REGISTER, 0xf << 28);
/* Mask all LAPIC LVT entries before enabling the local APIC */
write_lapic_reg32(LAPIC_LVT_CMCI_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_TIMER_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_THERMAL_SENSOR_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_PMC_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_LINT0_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_LINT1_REGISTER, LAPIC_LVT_MASK);
write_lapic_reg32(LAPIC_LVT_ERROR_REGISTER, LAPIC_LVT_MASK);
/* Enable Local APIC */
/* TODO: add spurious-interrupt handler */
write_lapic_reg32(LAPIC_SPURIOUS_VECTOR_REGISTER,
LAPIC_SVR_APIC_ENABLE_MASK | LAPIC_SVR_VECTOR);
/* Ensure there are no ISR bits set. */
clear_lapic_isr();
return 0;
}
int send_lapic_eoi(void)
{
write_lapic_reg32(LAPIC_EOI_REGISTER, 0);
return 0;
}
static void wait_for_delivery(void)
{
union apic_icr tmp;
do {
tmp.value_32.lo_32 =
read_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0);
} while (tmp.bits.delivery_status);
}
uint32_t get_cur_lapic_id(void)
{
uint32_t lapic_id;
lapic_id = read_lapic_reg32(LAPIC_ID_REGISTER);
lapic_id = (lapic_id >> 24);
return lapic_id;
}
int
send_startup_ipi(enum intr_cpu_startup_shorthand cpu_startup_shorthand,
uint32_t cpu_startup_dest, paddr_t cpu_startup_start_address)
{
union apic_icr icr;
uint8_t shorthand;
int status = 0;
uint32_t eax, ebx, ecx, edx;
uint32_t family;
if (cpu_startup_shorthand >= INTR_CPU_STARTUP_UNKNOWN)
status = -EINVAL;
ASSERT(status == 0, "Incorrect arguments");
icr.value = 0;
icr.bits.destination_mode = INTR_LAPIC_ICR_PHYSICAL;
if (cpu_startup_shorthand == INTR_CPU_STARTUP_USE_DEST) {
shorthand = INTR_LAPIC_ICR_USE_DEST_ARRAY;
icr.x_bits.dest_field = per_cpu(lapic_id, cpu_startup_dest);
} else { /* Use destination shorthand */
shorthand = INTR_LAPIC_ICR_ALL_EX_SELF;
icr.value_32.hi_32 = 0;
}
/*
* family calculation from SDM Vol. 2A
* CPUID with INPUT EAX=01h:Returns Model, Family, Stepping Information
*/
cpuid(CPUID_FEATURES, &eax, &ebx, &ecx, &edx);
family = (eax >> 8) & 0xff;
if (family == 0xF)
family += (eax >> 20) & 0xff;
/* Assert INIT IPI */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, icr.value_32.hi_32);
icr.bits.shorthand = shorthand;
icr.bits.delivery_mode = INTR_LAPIC_ICR_INIT;
icr.bits.level = INTR_LAPIC_ICR_ASSERT;
icr.bits.trigger_mode = INTR_LAPIC_ICR_LEVEL;
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, icr.value_32.lo_32);
wait_for_delivery();
/* Give 10ms for INIT sequence to complete for old processors.
* Modern processors (family == 6) don't need to wait here.
*/
if (family != 6)
mdelay(10);
/* De-assert INIT IPI */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, icr.value_32.hi_32);
icr.bits.level = INTR_LAPIC_ICR_DEASSERT;
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, icr.value_32.lo_32);
wait_for_delivery();
/* Send Start IPI with page number of secondary reset code */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, icr.value_32.hi_32);
icr.value_32.lo_32 = 0;
icr.bits.shorthand = shorthand;
icr.bits.delivery_mode = INTR_LAPIC_ICR_STARTUP;
icr.bits.vector = ((paddr_t) cpu_startup_start_address) >> 12;
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, icr.value_32.lo_32);
wait_for_delivery();
if (family == 6) /* 10us is enough for Modern processors */
udelay(10);
else /* 200us for old processors */
udelay(200);
/* Send another start IPI as per the Intel Arch specification */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, icr.value_32.hi_32);
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, icr.value_32.lo_32);
wait_for_delivery();
return status;
}
void send_single_ipi(uint32_t pcpu_id, uint32_t vector)
{
uint32_t dest_lapic_id, hi_32, lo_32;
/* Get the lapic ID of the destination processor. */
dest_lapic_id = per_cpu(lapic_id, pcpu_id);
/* Set the target processor. */
hi_32 = dest_lapic_id << 24;
/* Set the vector ID. */
lo_32 = vector;
/* Set the destination field to the target processor. */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, hi_32);
/* Write the vector ID to ICR. */
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, lo_32);
wait_for_delivery();
}
int send_shorthand_ipi(uint8_t vector,
enum intr_lapic_icr_shorthand shorthand,
enum intr_lapic_icr_delivery_mode delivery_mode)
{
union apic_icr icr;
int status = 0;
if ((shorthand < INTR_LAPIC_ICR_SELF)
|| (shorthand > INTR_LAPIC_ICR_ALL_EX_SELF)
|| (delivery_mode > INTR_LAPIC_ICR_NMI))
status = -EINVAL;
ASSERT(status == 0, "Incorrect arguments");
icr.value = 0;
icr.bits.shorthand = shorthand;
icr.bits.delivery_mode = delivery_mode;
icr.bits.vector = vector;
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_1, icr.value_32.hi_32);
write_lapic_reg32(LAPIC_INT_COMMAND_REGISTER_0, icr.value_32.lo_32);
wait_for_delivery();
return status;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
int interrupt_init(uint32_t cpu_id)
{
struct host_idt_descriptor *idtd = &HOST_IDTR;
int status;
set_idt(idtd);
status = init_lapic(cpu_id);
ASSERT(status == 0, "lapic init failed");
if (status != 0)
return -ENODEV;
status = init_default_irqs(cpu_id);
ASSERT(status == 0, "irqs init failed");
if (status != 0)
return -ENODEV;
CPU_IRQ_ENABLE();
return status;
}

292
hypervisor/arch/x86/io.c Normal file
View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include <hypercall.h>
int dm_emulate_pio_post(struct vcpu *vcpu)
{
int cur = vcpu->vcpu_id;
int cur_context = vcpu->arch_vcpu.cur_context;
struct vhm_request_buffer *req_buf =
(void *)HPA2HVA(vcpu->vm->sw.req_buf);
uint32_t mask =
0xFFFFFFFFul >> (32 - 8 * vcpu->req.reqs.pio_request.size);
uint64_t *rax;
ASSERT(cur_context == 0, "pio emulation only happen in normal wrold");
rax = &vcpu->arch_vcpu.contexts[cur_context].guest_cpu_regs.regs.rax;
vcpu->req.reqs.pio_request.value =
req_buf->req_queue[cur].reqs.pio_request.value;
/* VHM emulation data already copy to req, mark to free slot now */
req_buf->req_queue[cur].valid = false;
if (req_buf->req_queue[cur].processed != REQ_STATE_SUCCESS)
return -1;
if (vcpu->req.reqs.pio_request.direction == REQUEST_READ)
*rax = ((*rax) & ~mask) |
(vcpu->req.reqs.pio_request.value & mask);
return 0;
}
static void dm_emulate_pio_pre(struct vcpu *vcpu, uint64_t exit_qual,
uint32_t sz, uint64_t req_value)
{
vcpu->req.type = REQ_PORTIO;
if (VM_EXIT_IO_INSTRUCTION_ACCESS_DIRECTION(exit_qual))
vcpu->req.reqs.pio_request.direction = REQUEST_READ;
else
vcpu->req.reqs.pio_request.direction = REQUEST_WRITE;
vcpu->req.reqs.pio_request.address =
VM_EXIT_IO_INSTRUCTION_PORT_NUMBER(exit_qual);
vcpu->req.reqs.pio_request.size = sz;
vcpu->req.reqs.pio_request.value = req_value;
}
int io_instr_handler(struct vcpu *vcpu)
{
uint32_t sz;
uint32_t mask;
uint32_t port;
int8_t direction;
struct vm_io_handler *handler;
uint64_t exit_qual;
struct vm *vm = vcpu->vm;
int cur_context_idx = vcpu->arch_vcpu.cur_context;
struct run_context *cur_context;
int status = -EINVAL;
ASSERT(cur_context_idx == 0,
"pio emulation only happen in normal wrold");
cur_context = &vcpu->arch_vcpu.contexts[cur_context_idx];
exit_qual = vcpu->arch_vcpu.exit_qualification;
sz = VM_EXIT_IO_INSTRUCTION_SIZE(exit_qual) + 1;
port = VM_EXIT_IO_INSTRUCTION_PORT_NUMBER(exit_qual);
direction = VM_EXIT_IO_INSTRUCTION_ACCESS_DIRECTION(exit_qual);
mask = 0xfffffffful >> (32 - 8 * sz);
memset(&vcpu->req, 0, sizeof(struct vhm_request));
TRACE_4I(TRC_VMEXIT_IO_INSTRUCTION, port, direction, sz,
cur_context_idx);
for (handler = vm->arch_vm.io_handler;
handler; handler = handler->next) {
if ((port >= handler->desc.addr + handler->desc.len) ||
(port + sz <= handler->desc.addr))
continue;
/* Dom0 do not require IO emulation */
if (is_vm0(vm))
status = 0;
if (direction == 0) {
if (handler->desc.io_write == NULL)
continue;
handler->desc.io_write(handler, vm, port, sz,
cur_context->guest_cpu_regs.regs.rax);
pr_dbg("IO write on port %04x, data %08x", port,
cur_context->guest_cpu_regs.regs.rax & mask);
status = 0;
break;
} else if (handler->desc.io_read) {
uint32_t data = handler->desc.io_read(handler, vm,
port, sz);
cur_context->guest_cpu_regs.regs.rax &= ~mask;
cur_context->guest_cpu_regs.regs.rax |= data & mask;
pr_dbg("IO read on port %04x, data %08x", port, data);
status = 0;
break;
}
}
/* Go for VHM */
if (status != 0) {
uint64_t *rax = &cur_context->guest_cpu_regs.regs.rax;
dm_emulate_pio_pre(vcpu, exit_qual, sz, *rax);
status = acrn_insert_request_wait(vcpu, &vcpu->req);
}
if (status != 0) {
pr_fatal("IO %s access to port 0x%04x, size=%u",
direction ? "read" : "write", port, sz);
}
/* Catch any problems */
ASSERT(status == 0, "Invalid IO access");
return status;
}
static void register_io_handler(struct vm *vm, struct vm_io_handler *hdlr)
{
if (vm->arch_vm.io_handler)
hdlr->next = vm->arch_vm.io_handler;
vm->arch_vm.io_handler = hdlr;
}
static void empty_io_handler_list(struct vm *vm)
{
struct vm_io_handler *handler = vm->arch_vm.io_handler;
struct vm_io_handler *tmp;
while (handler) {
tmp = handler;
handler = tmp->next;
free(tmp);
}
vm->arch_vm.io_handler = NULL;
}
void free_io_emulation_resource(struct vm *vm)
{
empty_io_handler_list(vm);
/* Free I/O emulation bitmaps */
free(vm->arch_vm.iobitmap[0]);
free(vm->arch_vm.iobitmap[1]);
}
static void deny_guest_io_access(struct vm *vm, uint32_t address, uint32_t nbytes)
{
uint32_t *b;
uint32_t i;
uint32_t a;
for (i = 0; i < nbytes; i++) {
b = vm->arch_vm.iobitmap[0];
if (address & 0x8000)
b = vm->arch_vm.iobitmap[1];
a = address & 0x7fff;
b[a >> 5] |= (1 << (a & 0x1f));
address++;
}
}
static uint32_t
default_io_read(__unused struct vm_io_handler *hdlr, __unused struct vm *vm,
ioport_t address, size_t width)
{
uint32_t v = io_read(address, width);
return v;
}
static void default_io_write(__unused struct vm_io_handler *hdlr,
__unused struct vm *vm, ioport_t addr,
size_t width, uint32_t v)
{
io_write(v, addr, width);
}
static struct vm_io_handler *create_io_handler(uint32_t port, uint32_t len,
io_read_fn_t io_read_fn_ptr,
io_write_fn_t io_write_fn_ptr)
{
struct vm_io_handler *handler;
handler = calloc(1, sizeof(struct vm_io_handler));
if (handler != NULL) {
handler->desc.addr = port;
handler->desc.len = len;
handler->desc.io_read = io_read_fn_ptr;
handler->desc.io_write = io_write_fn_ptr;
} else {
pr_err("Error: out of memory");
}
return handler;
}
void setup_io_bitmap(struct vm *vm)
{
/* Allocate VM architecture state and IO bitmaps A and B */
vm->arch_vm.iobitmap[0] = alloc_page();
vm->arch_vm.iobitmap[1] = alloc_page();
ASSERT(vm->arch_vm.iobitmap[0] && vm->arch_vm.iobitmap[1], "");
if (is_vm0(vm)) {
memset(vm->arch_vm.iobitmap[0], 0x00, CPU_PAGE_SIZE);
memset(vm->arch_vm.iobitmap[1], 0x00, CPU_PAGE_SIZE);
} else {
/* block all IO port access from Guest */
memset(vm->arch_vm.iobitmap[0], 0xFF, CPU_PAGE_SIZE);
memset(vm->arch_vm.iobitmap[1], 0xFF, CPU_PAGE_SIZE);
}
}
void register_io_emulation_handler(struct vm *vm, struct vm_io_range *range,
io_read_fn_t io_read_fn_ptr,
io_write_fn_t io_write_fn_ptr)
{
struct vm_io_handler *handler = NULL;
io_read_fn_t io_read_fn = &default_io_read;
io_write_fn_t io_write_fn = &default_io_write;
if (range->flags == IO_ATTR_RW && io_read_fn_ptr && io_write_fn_ptr) {
io_read_fn = io_read_fn_ptr;
io_write_fn = io_write_fn_ptr;
} else if (range->flags == IO_ATTR_R) {
if (io_read_fn_ptr)
io_read_fn = io_read_fn_ptr;
io_write_fn = NULL;
}
if (is_vm0(vm))
deny_guest_io_access(vm, range->base, range->len);
handler = create_io_handler(range->base,
range->len, io_read_fn, io_write_fn);
register_io_handler(vm, handler);
}

View File

@@ -0,0 +1,439 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
/* Register offsets */
#define IOAPIC_REGSEL_OFFSET 0
#define IOAPIC_WINSWL_OFFSET 0x10
/* IOAPIC Redirection Table (RTE) Entry structure */
struct ioapic_rte {
uint32_t lo_32;
uint32_t hi_32;
} ioapic_rte;
struct gsi_table {
uint8_t ioapic_id;
uint8_t pin;
uint64_t addr;
};
static struct gsi_table gsi_table[NR_MAX_GSI];
static int nr_gsi;
static spinlock_t ioapic_lock;
/*
* the irq to ioapic pin mapping should extract from ACPI MADT table
* hardcoded here
*/
uint16_t legacy_irq_to_pin[NR_LEGACY_IRQ] = {
2, /* IRQ0*/
1, /* IRQ1*/
0, /* IRQ2 connected to Pin0 (ExtInt source of PIC) if existing */
3, /* IRQ3*/
4, /* IRQ4*/
5, /* IRQ5*/
6, /* IRQ6*/
7, /* IRQ7*/
8, /* IRQ8*/
9 | IOAPIC_RTE_TRGRLVL, /* IRQ9*/
10, /* IRQ10*/
11, /* IRQ11*/
12, /* IRQ12*/
13, /* IRQ13*/
14, /* IRQ14*/
15, /* IRQ15*/
};
static uint64_t map_ioapic(
uint64_t ioapic_paddr)
{
/* At some point we may need to translate this paddr to a vaddr.
* 1:1 mapping for now.
*/
return (vaddr_t) ioapic_paddr;
}
static inline uint32_t
ioapic_read_reg32(const uint64_t ioapic_base, const uint8_t offset)
{
uint32_t v;
spinlock_rflags;
spinlock_irqsave_obtain(&ioapic_lock);
/* Write IOREGSEL */
*(uint32_t *)(ioapic_base) = offset;
/* Read IOWIN */
v = *(uint32_t *)(ioapic_base + IOAPIC_WINSWL_OFFSET);
spinlock_irqrestore_release(&ioapic_lock);
return v;
}
static inline void
ioapic_write_reg32(const uint64_t ioapic_base,
const uint8_t offset, const uint32_t value)
{
spinlock_rflags;
spinlock_irqsave_obtain(&ioapic_lock);
/* Write IOREGSEL */
*(uint32_t *)(ioapic_base) = offset;
/* Write IOWIN */
*(uint32_t *)(ioapic_base + IOAPIC_WINSWL_OFFSET) = value;
spinlock_irqrestore_release(&ioapic_lock);
}
static inline uint64_t
get_ioapic_base(int apic_id)
{
uint64_t addr = -1UL;
/* should extract next ioapic from ACPI MADT table */
if (apic_id == 0)
addr = DEFAULT_IO_APIC_BASE;
else if (apic_id == 1)
addr = 0xfec3f000;
else if (apic_id == 2)
addr = 0xfec7f000;
else
ASSERT(apic_id <= 2, "ACPI MADT table missing");
return addr;
}
static inline void
ioapic_get_rte_entry(uint64_t ioapic_addr,
int pin, struct ioapic_rte *rte)
{
rte->lo_32 = ioapic_read_reg32(ioapic_addr, pin*2 + 0x10);
rte->hi_32 = ioapic_read_reg32(ioapic_addr, pin*2 + 0x11);
}
static inline void
ioapic_set_rte_entry(uint64_t ioapic_addr,
int pin, struct ioapic_rte *rte)
{
ioapic_write_reg32(ioapic_addr, pin*2 + 0x10, rte->lo_32);
ioapic_write_reg32(ioapic_addr, pin*2 + 0x11, rte->hi_32);
}
static inline struct ioapic_rte
create_rte_for_legacy_irq(int irq, int vr)
{
struct ioapic_rte rte = {0, 0};
/* Legacy IRQ 0-15 setup, default masked
* are actually defined in either MPTable or ACPI MADT table
* before we have ACPI table parsing in HV we use common hardcode
*/
rte.lo_32 |= IOAPIC_RTE_INTMSET;
rte.lo_32 |= (legacy_irq_to_pin[irq] & IOAPIC_RTE_TRGRLVL);
rte.lo_32 |= DEFAULT_DEST_MODE;
rte.lo_32 |= DEFAULT_DELIVERY_MODE;
rte.lo_32 |= (IOAPIC_RTE_INTVEC & vr);
/* FIXME: Fixed to active Low? */
rte.lo_32 |= IOAPIC_RTE_INTALO;
/* Dest field: legacy irq fixed to CPU0 */
rte.hi_32 |= 1 << 24;
return rte;
}
static inline struct ioapic_rte
create_rte_for_gsi_irq(int irq, int vr)
{
struct ioapic_rte rte = {0, 0};
if (irq < NR_LEGACY_IRQ)
return create_rte_for_legacy_irq(irq, vr);
/* irq default masked, level trig */
rte.lo_32 |= IOAPIC_RTE_INTMSET;
rte.lo_32 |= IOAPIC_RTE_TRGRLVL;
rte.lo_32 |= DEFAULT_DEST_MODE;
rte.lo_32 |= DEFAULT_DELIVERY_MODE;
rte.lo_32 |= (IOAPIC_RTE_INTVEC & vr);
/* FIXME: Fixed to active Low? */
rte.lo_32 |= IOAPIC_RTE_INTALO;
/* Dest field */
rte.hi_32 |= ALL_CPUS_MASK << 24;
return rte;
}
static void ioapic_set_routing(int gsi, int vr)
{
uint64_t addr;
struct ioapic_rte rte;
addr = gsi_table[gsi].addr;
rte = create_rte_for_gsi_irq(gsi, vr);
ioapic_set_rte_entry(addr, gsi_table[gsi].pin, &rte);
if (rte.lo_32 & IOAPIC_RTE_TRGRMOD)
update_irq_handler(gsi, handle_level_interrupt_common);
else
update_irq_handler(gsi, common_handler_edge);
dev_dbg(ACRN_DBG_IRQ, "GSI: irq:%d pin:%d rte:%x",
gsi, gsi_table[gsi].pin,
rte.lo_32);
}
void ioapic_get_rte(int irq, uint64_t *rte)
{
uint64_t addr;
struct ioapic_rte _rte;
if (!irq_is_gsi(irq))
return;
addr = gsi_table[irq].addr;
ioapic_get_rte_entry(addr, gsi_table[irq].pin, &_rte);
*rte = _rte.hi_32;
*rte = *rte << 32 | _rte.lo_32;
}
void ioapic_set_rte(int irq, uint64_t raw_rte)
{
uint64_t addr;
struct ioapic_rte rte;
if (!irq_is_gsi(irq))
return;
addr = gsi_table[irq].addr;
rte.lo_32 = raw_rte;
rte.hi_32 = raw_rte >> 32;
ioapic_set_rte_entry(addr, gsi_table[irq].pin, &rte);
dev_dbg(ACRN_DBG_IRQ, "GSI: irq:%d pin:%d rte:%x",
irq, gsi_table[irq].pin,
rte.lo_32);
}
int irq_gsi_num(void)
{
return nr_gsi;
}
bool irq_is_gsi(int irq)
{
return irq < nr_gsi;
}
int irq_to_pin(int irq)
{
if (irq_is_gsi(irq))
return gsi_table[irq].pin;
else
return -1;
}
int pin_to_irq(int pin)
{
int i;
if (pin < 0)
return IRQ_INVALID;
for (i = 0; i < nr_gsi; i++) {
if (gsi_table[i].pin == (uint8_t) pin)
return i;
}
return IRQ_INVALID;
}
void
irq_gsi_mask_unmask(int irq, bool mask)
{
uint64_t addr = gsi_table[irq].addr;
int pin = gsi_table[irq].pin;
struct ioapic_rte rte;
if (!irq_is_gsi(irq))
return;
ioapic_get_rte_entry(addr, pin, &rte);
if (mask)
rte.lo_32 |= IOAPIC_RTE_INTMSET;
else
rte.lo_32 &= ~IOAPIC_RTE_INTMASK;
ioapic_set_rte_entry(addr, pin, &rte);
dev_dbg(ACRN_DBG_PTIRQ, "update: irq:%d pin:%d rte:%x",
irq, pin, rte.lo_32);
}
void setup_ioapic_irq(void)
{
int ioapic_id;
int gsi;
int vr;
spinlock_init(&ioapic_lock);
for (ioapic_id = 0, gsi = 0; ioapic_id < NR_IOAPICS; ioapic_id++) {
int pin;
int max_pins;
int version;
uint64_t addr;
addr = map_ioapic(get_ioapic_base(ioapic_id));
version = ioapic_read_reg32(addr, IOAPIC_VER);
max_pins = (version & IOAPIC_MAX_RTE_MASK) >> MAX_RTE_SHIFT;
dev_dbg(ACRN_DBG_IRQ, "IOAPIC version: %x", version);
ASSERT(max_pins > NR_LEGACY_IRQ,
"Legacy IRQ num > total GSI");
for (pin = 0; pin < max_pins; pin++) {
gsi_table[gsi].ioapic_id = ioapic_id;
gsi_table[gsi].addr = addr;
if (gsi < NR_LEGACY_IRQ)
gsi_table[gsi].pin =
legacy_irq_to_pin[gsi] & 0xff;
else
gsi_table[gsi].pin = pin;
/* pinned irq before use it */
if (irq_mark_used(gsi) < 0) {
pr_err("failed to alloc IRQ[%d]", gsi);
gsi++;
continue;
}
/* assign vector for this GSI
* for legacy irq, reserved vector and never free
*/
if (gsi < NR_LEGACY_IRQ) {
vr = irq_desc_alloc_vector(gsi, false);
if (vr < 0) {
pr_err("failed to alloc VR");
gsi++;
continue;
}
} else
vr = 0; /* not to allocate VR right now */
ioapic_set_routing(gsi, vr);
gsi++;
}
}
/* system max gsi numbers */
nr_gsi = gsi;
ASSERT(nr_gsi < NR_MAX_GSI, "GSI table overflow");
}
void dump_ioapic(void)
{
int irq;
for (irq = 0; irq < nr_gsi; irq++) {
uint64_t addr = gsi_table[irq].addr;
int pin = gsi_table[irq].pin;
struct ioapic_rte rte;
ioapic_get_rte_entry(addr, pin, &rte);
dev_dbg(ACRN_DBG_IRQ, "DUMP: irq:%d pin:%d rte:%x",
irq, pin, rte.lo_32);
}
}
void get_rte_info(struct ioapic_rte *rte, bool *mask, bool *irr,
bool *phys, int *delmode, bool *level, int *vector, uint32_t *dest)
{
*mask = ((rte->lo_32 & IOAPIC_RTE_INTMASK) == IOAPIC_RTE_INTMSET);
*irr = ((rte->lo_32 & IOAPIC_RTE_REM_IRR) == IOAPIC_RTE_REM_IRR);
*phys = ((rte->lo_32 & IOAPIC_RTE_DESTMOD) == IOAPIC_RTE_DESTPHY);
*delmode = rte->lo_32 & IOAPIC_RTE_DELMOD;
*level = rte->lo_32 & IOAPIC_RTE_TRGRLVL ? true : false;
*vector = rte->lo_32 & IOAPIC_RTE_INTVEC;
*dest = rte->hi_32 >> APIC_ID_SHIFT;
}
int get_ioapic_info(char *str, int str_max_len)
{
int irq, len, size = str_max_len;
len = snprintf(str, size,
"\r\nIRQ\tPIN\tRTE.HI32\tRTE.LO32\tVEC\tDST\tDM\tTM\tDELM\tIRR\tMASK");
size -= len;
str += len;
for (irq = 0; irq < nr_gsi; irq++) {
uint64_t addr = gsi_table[irq].addr;
int pin = gsi_table[irq].pin;
struct ioapic_rte rte;
bool irr, phys, level, mask;
int delmode, vector;
uint32_t dest;
ioapic_get_rte_entry(addr, pin, &rte);
get_rte_info(&rte, &mask, &irr, &phys, &delmode, &level,
&vector, &dest);
len = snprintf(str, size, "\r\n%03d\t%03d\t0x%08X\t0x%08X\t",
irq, pin, rte.hi_32, rte.lo_32);
size -= len;
str += len;
len = snprintf(str, size, "0x%02X\t0x%02X\t%s\t%s\t%d\t%d\t%d",
vector, dest, phys ? "phys" : "logic",
level ? "level" : "edge", delmode >> 8, irr, mask);
size -= len;
str += len;
if (size < 2) {
pr_err("\r\nsmall buffer for ioapic dump");
return -1;
}
}
snprintf(str, size, "\r\n");
return 0;
}

761
hypervisor/arch/x86/irq.c Normal file
View File

@@ -0,0 +1,761 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
static spinlock_t exception_spinlock = { .head = 0, .tail = 0, };
struct irq_request_info {
/* vector set to 0xE0 ~ 0xFF for pri_register_handler
* and set to -1 for normal_register_handler
*/
int vector;
dev_handler_t func;
void *dev_data;
bool share;
bool lowpri;
char *name;
};
/* any field change in below required irq_lock protection with irqsave */
struct irq_desc {
int irq; /* index to irq_desc_base */
enum irq_state used; /* this irq have assigned to device */
enum irq_desc_state state; /* irq_desc status */
int vector; /* assigned vector */
void *handler_data; /* irq_handler private data */
int (*irq_handler)(struct irq_desc *irq_desc, void *handler_data);
struct dev_handler_node *dev_list;
spinlock_t irq_lock;
uint64_t *irq_cnt; /* this irq cnt happened on CPUs */
uint64_t irq_lost_cnt;
};
static struct irq_desc *irq_desc_base;
static int vector_to_irq[NR_MAX_VECTOR + 1];
static DEFINE_CPU_DATA(uint64_t[NR_MAX_IRQS], irq_count);
static DEFINE_CPU_DATA(uint64_t, spurious);
spurious_handler_t spurious_handler;
static void init_irq_desc(void)
{
int i, page_num = 0;
int desc_size = NR_MAX_IRQS * sizeof(struct irq_desc);
page_num = (desc_size + CPU_PAGE_SIZE-1) >> CPU_PAGE_SHIFT;
irq_desc_base = alloc_pages(page_num);
ASSERT(irq_desc_base, "page alloc failed!");
memset(irq_desc_base, 0, page_num * CPU_PAGE_SIZE);
for (i = 0; i < NR_MAX_IRQS; i++) {
irq_desc_base[i].irq = i;
irq_desc_base[i].vector = VECTOR_INVALID;
spinlock_init(&irq_desc_base[i].irq_lock);
}
for (i = 0; i <= NR_MAX_VECTOR; i++)
vector_to_irq[i] = IRQ_INVALID;
}
/*
* alloc vector 0x20-0xDF for irq
* lowpri: 0x20-0x7F
* highpri: 0x80-0xDF
*/
static int find_available_vector(bool lowpri)
{
int i, start, end;
if (lowpri) {
start = VECTOR_FOR_NOR_LOWPRI_START;
end = VECTOR_FOR_NOR_LOWPRI_END;
} else {
start = VECTOR_FOR_NOR_HIGHPRI_START;
end = VECTOR_FOR_NOR_HIGHPRI_END;
}
/* TODO: vector lock required */
for (i = start; i < end; i++) {
if (vector_to_irq[i] == IRQ_INVALID)
return i;
}
return -1;
}
/*
* check and set irq to be assigned
* return: -1 if irq already assigned otherwise return irq
*/
int irq_mark_used(int irq)
{
struct irq_desc *desc;
spinlock_rflags;
if (irq < 0)
return -1;
desc = irq_desc_base + irq;
spinlock_irqsave_obtain(&desc->irq_lock);
if (desc->used == IRQ_NOT_ASSIGNED)
desc->used = IRQ_ASSIGNED_NOSHARE;
spinlock_irqrestore_release(&desc->irq_lock);
return irq;
}
/*
* system find available irq and set assigned
* return: irq, -1 not found
*/
static int alloc_irq(void)
{
int i;
struct irq_desc *desc;
spinlock_rflags;
for (i = irq_gsi_num(); i < NR_MAX_IRQS; i++) {
desc = irq_desc_base + i;
spinlock_irqsave_obtain(&desc->irq_lock);
if (desc->used == IRQ_NOT_ASSIGNED) {
desc->used = IRQ_ASSIGNED_NOSHARE;
spinlock_irqrestore_release(&desc->irq_lock);
break;
}
spinlock_irqrestore_release(&desc->irq_lock);
}
return (i == NR_MAX_IRQS) ? -1:i;
}
/* need irq_lock protection before use */
static void _irq_desc_set_vector(int irq, int vr)
{
struct irq_desc *desc;
desc = irq_desc_base + irq;
vector_to_irq[vr] = irq;
desc->vector = vr;
}
/* lock version of set vector */
static void irq_desc_set_vector(int irq, int vr)
{
struct irq_desc *desc;
spinlock_rflags;
desc = irq_desc_base + irq;
spinlock_irqsave_obtain(&desc->irq_lock);
vector_to_irq[vr] = irq;
desc->vector = vr;
spinlock_irqrestore_release(&desc->irq_lock);
}
/* used with holding irq_lock outside */
static void _irq_desc_free_vector(int irq)
{
struct irq_desc *desc;
int vr;
if (irq > NR_MAX_IRQS || irq < 0)
return;
desc = irq_desc_base + irq;
vr = desc->vector;
desc->used = IRQ_NOT_ASSIGNED;
desc->state = IRQ_DESC_PENDING;
desc->vector = VECTOR_INVALID;
vr &= NR_MAX_VECTOR;
if (vector_to_irq[vr] == irq)
vector_to_irq[vr] = IRQ_INVALID;
}
static void disable_pic_irq(void)
{
io_write_byte(0xff, 0xA1);
io_write_byte(0xff, 0x21);
}
static bool
irq_desc_append_dev(struct irq_desc *desc, void *node, bool share)
{
struct dev_handler_node *dev_list;
bool added = true;
spinlock_rflags;
spinlock_irqsave_obtain(&desc->irq_lock);
dev_list = desc->dev_list;
/* assign if first node */
if (dev_list == NULL) {
desc->dev_list = node;
desc->used = (share)?IRQ_ASSIGNED_SHARED:IRQ_ASSIGNED_NOSHARE;
/* Only GSI possible for Level and it already init during
* ioapic setup.
* caller can later update it with update_irq_handler()
*/
if (!desc->irq_handler)
desc->irq_handler = common_handler_edge;
} else if (!share || desc->used == IRQ_ASSIGNED_NOSHARE) {
/* dev node added failed */
added = false;
} else {
/* dev_list point to last valid node */
while (dev_list->next)
dev_list = dev_list->next;
/* add node */
dev_list->next = node;
}
spinlock_irqrestore_release(&desc->irq_lock);
return added;
}
static struct dev_handler_node*
common_register_handler(int irq,
struct irq_request_info *info)
{
struct dev_handler_node *node = NULL;
struct irq_desc *desc;
bool added = false;
/* ======================================================
* This is low level ISR handler registering function
* case: irq = -1
* caller did not know which irq to use, and want system to
* allocate available irq for it. These irq are in range:
* nr_gsi ~ NR_MAX_IRQS
* a irq will be allocated and the vector will be assigned to this
* irq automatically.
*
* case: irq >=0 and irq < nr_gsi
* caller want to add device ISR handler into ioapic pins.
* two kind of devices: legacy device and PCI device with INTx
* a vector will automatically assigned.
*
* case: irq with speical type (not from IOAPIC/MSI)
* These irq value are pre-defined for Timer, IPI, Spurious etc
* vectors are pre-defined also
*
* return value: pinned irq and assigned vector for this irq.
* caller can use this irq to enable/disable/mask/unmask interrupt
* and if this irq is for:
* GSI legacy: nothing to do for legacy irq, already initialized
* GSI other: need to progam PCI INTx to match this irq pin
* MSI: caller need program vector to PCI device
*
* =====================================================
*/
ASSERT(info != NULL, "Invalid param");
/* HV select a irq for device if irq < 0
* this vector/irq match to APCI DSDT or PCI INTx/MSI
*/
if (irq < 0)
irq = alloc_irq();
else
irq = irq_mark_used(irq);
if (irq < 0) {
pr_err("failed to assign IRQ");
goto OUT;
}
node = calloc(1, sizeof(struct dev_handler_node));
if (node == NULL) {
pr_err("failed to alloc node");
irq_desc_try_free_vector(irq);
goto OUT;
}
desc = irq_desc_base + irq;
added = irq_desc_append_dev(desc, node, info->share);
if (!added) {
free(node);
node = NULL;
pr_err("failed to add node to non-shared irq");
}
OUT:
if (added) {
/* it is safe to call irq_desc_alloc_vector multiple times*/
if (info->vector >= VECTOR_FOR_PRI_START &&
info->vector <= VECTOR_FOR_PRI_END)
irq_desc_set_vector(irq, info->vector);
else if (info->vector < 0)
irq_desc_alloc_vector(irq, info->lowpri);
else {
pr_err("the input vector is not correct");
free(node);
return NULL;
}
node->dev_handler = info->func;
node->dev_data = info->dev_data;
node->desc = desc;
/* we are okay using strcpy_s here even with spinlock
* since no #PG in HV right now
*/
strcpy_s(node->name, 32, info->name);
dev_dbg(ACRN_DBG_IRQ, "[%s] %s irq%d vr:0x%x",
__func__, node->name, irq, desc->vector);
}
return node;
}
/* it is safe to call irq_desc_alloc_vector multiple times*/
int irq_desc_alloc_vector(int irq, bool lowpri)
{
int vr = -1;
struct irq_desc *desc;
spinlock_rflags;
/* irq should be always available at this time */
if (irq > NR_MAX_IRQS || irq < 0)
return false;
desc = irq_desc_base + irq;
spinlock_irqsave_obtain(&desc->irq_lock);
if (desc->vector != VECTOR_INVALID) {
/* already allocated a vector */
goto OUT;
}
/* FLAT mode, a irq connected to every cpu's same vector */
vr = find_available_vector(lowpri);
if (vr < 0) {
pr_err("no vector found for irq[%d]", irq);
goto OUT;
}
_irq_desc_set_vector(irq, vr);
OUT:
spinlock_irqrestore_release(&desc->irq_lock);
return vr;
}
void irq_desc_try_free_vector(int irq)
{
struct irq_desc *desc;
spinlock_rflags;
/* legacy irq's vector is reserved and should not be freed */
if (irq > NR_MAX_IRQS || irq < NR_LEGACY_IRQ)
return;
desc = irq_desc_base + irq;
spinlock_irqsave_obtain(&desc->irq_lock);
if (desc->dev_list == NULL)
_irq_desc_free_vector(irq);
spinlock_irqrestore_release(&desc->irq_lock);
}
int irq_to_vector(int irq)
{
if (irq < NR_MAX_IRQS)
return irq_desc_base[irq].vector;
else
return VECTOR_INVALID;
}
int dev_to_irq(struct dev_handler_node *node)
{
return node->desc->irq;
}
int dev_to_vector(struct dev_handler_node *node)
{
return node->desc->vector;
}
int init_default_irqs(unsigned int cpu_id)
{
if (cpu_id > 0)
return 0;
init_irq_desc();
/* we use ioapic only, disable legacy PIC */
disable_pic_irq();
setup_ioapic_irq();
init_softirq();
return 0;
}
void dispatch_exception(struct intr_ctx *ctx)
{
unsigned int cpu_id = get_cpu_id();
/* Obtain lock to ensure exception dump doesn't get corrupted */
spinlock_obtain(&exception_spinlock);
dump_exception(ctx, cpu_id);
/* Release lock to let other CPUs handle exception */
spinlock_release(&exception_spinlock);
/* Halt the CPU */
cpu_halt(cpu_id);
}
int handle_spurious_interrupt(int vector)
{
send_lapic_eoi();
get_cpu_var(spurious)++;
pr_warn("Spurious vector: 0x%x.", vector);
if (spurious_handler)
return spurious_handler(vector);
else
return 0;
}
/* do_IRQ() */
int dispatch_interrupt(struct intr_ctx *ctx)
{
int vr = ctx->vector;
int irq = vector_to_irq[vr];
struct irq_desc *desc;
if (irq == IRQ_INVALID)
goto ERR;
desc = irq_desc_base + irq;
per_cpu(irq_count, get_cpu_id())[irq]++;
if (vr != desc->vector)
goto ERR;
if (desc->used == IRQ_NOT_ASSIGNED || !desc->irq_handler) {
/* mask irq if possible */
goto ERR;
}
desc->irq_handler(desc, desc->handler_data);
return 0;
ERR:
return handle_spurious_interrupt(vr);
}
int handle_level_interrupt_common(struct irq_desc *desc,
__unused void *handler_data)
{
struct dev_handler_node *dev = desc->dev_list;
spinlock_rflags;
/*
* give other Core a try to return without hold irq_lock
* and record irq_lost count here
*/
if (desc->state != IRQ_DESC_PENDING) {
send_lapic_eoi();
desc->irq_lost_cnt++;
return 0;
}
spinlock_irqsave_obtain(&desc->irq_lock);
desc->state = IRQ_DESC_IN_PROCESS;
/* mask iopaic pin */
if (irq_is_gsi(desc->irq))
GSI_MASK_IRQ(desc->irq);
/* Send EOI to LAPIC/IOAPIC IRR */
send_lapic_eoi();
while (dev) {
if (dev->dev_handler)
dev->dev_handler(desc->irq, dev->dev_data);
dev = dev->next;
}
if (irq_is_gsi(desc->irq))
GSI_UNMASK_IRQ(desc->irq);
desc->state = IRQ_DESC_PENDING;
spinlock_irqrestore_release(&desc->irq_lock);
return 0;
}
int common_handler_edge(struct irq_desc *desc, __unused void *handler_data)
{
struct dev_handler_node *dev = desc->dev_list;
spinlock_rflags;
/*
* give other Core a try to return without hold irq_lock
* and record irq_lost count here
*/
if (desc->state != IRQ_DESC_PENDING) {
send_lapic_eoi();
desc->irq_lost_cnt++;
return 0;
}
spinlock_irqsave_obtain(&desc->irq_lock);
desc->state = IRQ_DESC_IN_PROCESS;
/* Send EOI to LAPIC/IOAPIC IRR */
send_lapic_eoi();
while (dev) {
if (dev->dev_handler)
dev->dev_handler(desc->irq, dev->dev_data);
dev = dev->next;
}
desc->state = IRQ_DESC_PENDING;
spinlock_irqrestore_release(&desc->irq_lock);
return 0;
}
int common_dev_handler_level(struct irq_desc *desc, __unused void *handler_data)
{
struct dev_handler_node *dev = desc->dev_list;
spinlock_rflags;
/*
* give other Core a try to return without hold irq_lock
* and record irq_lost count here
*/
if (desc->state != IRQ_DESC_PENDING) {
send_lapic_eoi();
desc->irq_lost_cnt++;
return 0;
}
spinlock_irqsave_obtain(&desc->irq_lock);
desc->state = IRQ_DESC_IN_PROCESS;
/* mask iopaic pin */
if (irq_is_gsi(desc->irq))
GSI_MASK_IRQ(desc->irq);
/* Send EOI to LAPIC/IOAPIC IRR */
send_lapic_eoi();
while (dev) {
if (dev->dev_handler)
dev->dev_handler(desc->irq, dev->dev_data);
dev = dev->next;
}
desc->state = IRQ_DESC_PENDING;
spinlock_irqrestore_release(&desc->irq_lock);
/* we did not unmask irq until guest EOI the vector */
return 0;
}
/* no desc->irq_lock for quick handling local interrupt like lapic timer */
int quick_handler_nolock(struct irq_desc *desc, __unused void *handler_data)
{
struct dev_handler_node *dev = desc->dev_list;
/* Send EOI to LAPIC/IOAPIC IRR */
send_lapic_eoi();
while (dev) {
if (dev->dev_handler)
dev->dev_handler(desc->irq, dev->dev_data);
dev = dev->next;
}
return 0;
}
void update_irq_handler(int irq, irq_handler_t func)
{
struct irq_desc *desc;
spinlock_rflags;
if (irq >= NR_MAX_IRQS)
return;
desc = irq_desc_base + irq;
spinlock_irqsave_obtain(&desc->irq_lock);
desc->irq_handler = func;
spinlock_irqrestore_release(&desc->irq_lock);
}
void unregister_handler_common(struct dev_handler_node *node)
{
struct dev_handler_node *head;
struct irq_desc *desc;
spinlock_rflags;
if (node == NULL)
return;
dev_dbg(ACRN_DBG_IRQ, "[%s] %s irq%d vr:0x%x",
__func__, node->name,
dev_to_irq(node),
dev_to_vector(node));
desc = node->desc;
spinlock_irqsave_obtain(&desc->irq_lock);
head = desc->dev_list;
if (head == node) {
desc->dev_list = NULL;
goto UNLOCK_EXIT;
}
while (head->next) {
if (head->next == node)
break;
head = head->next;
}
head->next = node->next;
UNLOCK_EXIT:
spinlock_irqrestore_release(&desc->irq_lock);
irq_desc_try_free_vector(desc->irq);
free(node);
}
/*
* Allocate IRQ with Vector from 0x20 ~ 0xDF
*/
struct dev_handler_node*
normal_register_handler(int irq,
dev_handler_t func,
void *dev_data,
bool share,
bool lowpri,
const char *name)
{
struct irq_request_info info;
info.vector = -1;
info.lowpri = lowpri;
info.func = func;
info.dev_data = dev_data;
info.share = share;
info.name = (char *)name;
return common_register_handler(irq, &info);
}
/*
* Allocate IRQ with vector from 0xE0 ~ 0xFF
* Allocate a IRQ and install isr on that specific cpu
* User can install same irq/isr on different CPU by call this function multiple
* times
*/
struct dev_handler_node*
pri_register_handler(int irq,
int vector,
dev_handler_t func,
void *dev_data,
const char *name)
{
struct irq_request_info info;
if (vector < VECTOR_FOR_PRI_START || vector > VECTOR_FOR_PRI_END)
return NULL;
info.vector = vector;
info.lowpri = false;
info.func = func;
info.dev_data = dev_data;
info.share = true;
info.name = (char *)name;
return common_register_handler(irq, &info);
}
int get_cpu_interrupt_info(char *str, int str_max)
{
int irq, vector, pcpu_id, len, size = str_max;
struct irq_desc *desc;
len = snprintf(str, size, "\r\nIRQ\tVECTOR");
size -= len;
str += len;
for (pcpu_id = 0; pcpu_id < phy_cpu_num; pcpu_id++) {
len = snprintf(str, size, "\tCPU%d", pcpu_id);
size -= len;
str += len;
}
len = snprintf(str, size, "\tLOST\tSHARE");
size -= len;
str += len;
for (irq = 0; irq < NR_MAX_IRQS; irq++) {
desc = irq_desc_base + irq;
vector = irq_to_vector(irq);
if (desc->used != IRQ_NOT_ASSIGNED &&
vector != VECTOR_INVALID) {
len = snprintf(str, size, "\r\n%d\t0x%X", irq, vector);
size -= len;
str += len;
for (pcpu_id = 0; pcpu_id < phy_cpu_num; pcpu_id++) {
len = snprintf(str, size, "\t%d",
per_cpu(irq_count, pcpu_id)[irq]++);
size -= len;
str += len;
}
len = snprintf(str, size, "\t%d\t%s",
desc->irq_lost_cnt,
desc->used == IRQ_ASSIGNED_SHARED ?
"shared" : "no-shared");
size -= len;
str += len;
}
}
snprintf(str, size, "\r\n");
return 0;
}

932
hypervisor/arch/x86/mmu.c Normal file
View File

@@ -0,0 +1,932 @@
/*-
* Copyright (c) 2011 NetApp, Inc.
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <hv_debug.h>
static void *mmu_pml4_addr;
enum mem_map_request_type {
PAGING_REQUEST_TYPE_MAP = 0, /* Creates a new mapping. */
PAGING_REQUEST_TYPE_UNMAP = 1, /* Removes a pre-existing entry */
PAGING_REQUEST_TYPE_MODIFY = 2,
/* Modifies a pre-existing entries attributes. */
PAGING_REQUEST_TYPE_UNKNOWN,
};
struct mm_capability {
/* EPT and MMU 1-GByte page supported flag */
bool ept_1gb_page_supported;
bool invept_supported;
bool invept_single_context_supported;
bool invept_global_context_supported;
bool invvpid_supported;
bool invvpid_single_context_supported;
bool invvpid_global_context_supported;
bool mmu_1gb_page_supported;
};
static struct mm_capability mm_caps;
#define INVEPT_TYPE_SINGLE_CONTEXT 1UL
#define INVEPT_TYPE_ALL_CONTEXTS 2UL
#define INVEPT_SET_ERROR_CODE \
" jnc 1f\n" \
" mov $1, %0\n" /* CF: error = 1 */ \
" jmp 3f\n" \
"1: jnz 2f\n" \
" mov $2, %0\n" /* ZF: error = 2 */ \
" jmp 3f\n" \
"2: mov $0, %0\n" \
"3:"
struct invept_desc {
uint64_t eptp;
uint64_t _res;
};
static inline void _invept(uint64_t type, struct invept_desc desc)
{
int error = 0;
asm volatile ("invept %1, %2\n"
INVEPT_SET_ERROR_CODE
: "=r" (error)
: "m" (desc), "r" (type)
: "memory");
ASSERT(error == 0, "invept error");
}
static void check_mmu_capability(void)
{
uint64_t val;
uint32_t eax, ebx, ecx, edx;
memset(&mm_caps, 0, sizeof(struct mm_capability));
/* Read the MSR register of EPT and VPID Capability - SDM A.10 */
val = msr_read(MSR_IA32_VMX_EPT_VPID_CAP);
mm_caps.ept_1gb_page_supported = (val & MSR_VMX_EPT_VPID_CAP_1GB)
? (true) : (false);
mm_caps.invept_supported =
(val & MSR_VMX_INVEPT) ? (true) : (false);
mm_caps.invept_single_context_supported =
(val & MSR_VMX_INVEPT_SINGLE_CONTEXT) ? (true) : (false);
mm_caps.invept_global_context_supported =
(val & MSR_VMX_INVEPT_GLOBAL_CONTEXT) ? (true) : (false);
mm_caps.invvpid_supported =
(val & MSR_VMX_INVVPID) ? (true) : (false);
mm_caps.invvpid_single_context_supported =
(val & MSR_VMX_INVVPID_SINGLE_CONTEXT) ? (true) : (false);
mm_caps.invvpid_global_context_supported =
(val & MSR_VMX_INVVPID_GLOBAL_CONTEXT) ? (true) : (false);
/* Read CPUID to check if PAGE1GB is supported
* SDM 4.1.4 If CPUID.80000001H:EDX.Page1GB[bit26]=1,
* 1-GByte pages are supported with 4-level paging
*/
cpuid(CPUID_EXTEND_FUNCTION_1, &eax, &ebx, &ecx, &edx);
mm_caps.mmu_1gb_page_supported = (edx & CPUID_EDX_PAGE1GB) ?
(true) : (false);
}
static inline bool check_invept_single_support(void)
{
return mm_caps.invept_supported &&
mm_caps.invept_single_context_supported;
}
static inline bool check_invept_global_support(void)
{
return mm_caps.invept_supported &&
mm_caps.invept_global_context_supported;
}
void mmu_invept(struct vcpu *vcpu)
{
struct invept_desc desc = {0};
if (check_invept_single_support()) {
desc.eptp = (uint64_t) vcpu->vm->arch_vm.ept | (3 << 3) | 6;
_invept(INVEPT_TYPE_SINGLE_CONTEXT, desc);
} else if (check_invept_global_support())
_invept(INVEPT_TYPE_ALL_CONTEXTS, desc);
}
static bool check_mmu_1gb_support(struct map_params *map_params)
{
bool status = false;
if (map_params->page_table_type == PT_EPT)
status = mm_caps.ept_1gb_page_supported;
else
status = mm_caps.mmu_1gb_page_supported;
return status;
}
static uint32_t map_mem_region(void *vaddr, void *paddr,
void *table_base, uint64_t attr, uint32_t table_level,
int ept_entry, enum mem_map_request_type request_type)
{
uint64_t table_entry;
uint64_t table_present;
uint32_t table_offset;
uint32_t mapped_size;
if (table_base == NULL || table_level >= IA32E_UNKNOWN
|| request_type >= PAGING_REQUEST_TYPE_UNKNOWN) {
/* Shouldn't go here */
ASSERT(false, "Incorrect Arguments. Failed to map region");
}
/* switch based on of table */
switch (table_level) {
case IA32E_PDPT:
/* Get offset to the entry in the PDPT for this address */
table_offset = IA32E_PDPTE_INDEX_CALC(vaddr);
/* PS bit must be set for these entries to be mapped */
attr |= IA32E_PDPTE_PS_BIT;
/* Set mapped size to 1 GB */
mapped_size = MEM_1G;
break;
case IA32E_PD:
/* Get offset to the entry in the PD for this address */
table_offset = IA32E_PDE_INDEX_CALC(vaddr);
/* PS bit must be set for these entries to be mapped */
attr |= IA32E_PDE_PS_BIT;
/* Set mapped size to 2 MB */
mapped_size = MEM_2M;
break;
case IA32E_PT:
/* Get offset to the entry in the PT for this address */
table_offset = IA32E_PTE_INDEX_CALC(vaddr);
/* NOTE: No PS bit in page table entries */
/* Set mapped size to 4 KB */
mapped_size = MEM_4K;
/* If not a EPT entry, see if the PAT bit is set for PDPT entry
*/
if ((!ept_entry) && (attr & IA32E_PDPTE_PAT_BIT)) {
/* The PAT bit is set; Clear it and set the page table
* PAT bit instead
*/
attr &= (uint64_t) (~((uint64_t) IA32E_PDPTE_PAT_BIT));
attr |= IA32E_PTE_PAT_BIT;
}
break;
case IA32E_PML4:
default:
/* Set mapping size to 0 - can't map memory in PML4 */
mapped_size = 0;
break;
}
/* Check to see if mapping should occur */
if (mapped_size != 0) {
/* Get current table entry */
uint64_t tmp = MEM_READ64(table_base + table_offset);
/* Check if EPT entry */
if (ept_entry) {
/* Use read/write/execute bits to determine presence of
* entry
*/
table_present = (IA32E_EPT_R_BIT |
IA32E_EPT_W_BIT | IA32E_EPT_X_BIT);
} else {
/* Use the P bit to determine if an entry is present */
table_present = IA32E_COMM_P_BIT;
}
switch (request_type) {
case PAGING_REQUEST_TYPE_MAP:
{
/* No need to confirm current table entry
* isn't already present
* support map-->remap
*/
table_entry = (ept_entry
? attr
: (attr | IA32E_COMM_P_BIT));
table_entry |= (uint64_t)paddr;
/* Write the table entry to map this memory */
MEM_WRITE64(table_base + table_offset, table_entry);
break;
}
case PAGING_REQUEST_TYPE_UNMAP:
{
if (tmp & table_present) {
/* Table is present.
* Write the table entry to map this memory
*/
MEM_WRITE64(table_base + table_offset, 0);
}
break;
}
case PAGING_REQUEST_TYPE_MODIFY:
{
/* Allow mapping or modification as requested. */
table_entry = (ept_entry
? attr : (attr | IA32E_COMM_P_BIT));
table_entry |= (uint64_t) paddr;
/* Write the table entry to map this memory */
MEM_WRITE64(table_base + table_offset, table_entry);
break;
}
default:
ASSERT("Bad memory map request type" == 0, "");
break;
}
}
/* Return mapped size to caller */
return mapped_size;
}
static uint32_t fetch_page_table_offset(void *addr, uint32_t table_level)
{
uint32_t table_offset;
/* Switch based on level of table */
switch (table_level) {
case IA32E_PML4:
/* Get offset to the entry in the PML4
* for this address
*/
table_offset = IA32E_PML4E_INDEX_CALC(addr);
break;
case IA32E_PDPT:
/* Get offset to the entry in the PDPT
* for this address
*/
table_offset = IA32E_PDPTE_INDEX_CALC(addr);
break;
case IA32E_PD:
/* Get offset to the entry in the PD
* for this address
*/
table_offset = IA32E_PDE_INDEX_CALC(addr);
break;
case IA32E_PT:
table_offset = IA32E_PTE_INDEX_CALC(addr);
break;
default:
pr_err("Wrong page table level = 0x%lx", table_level);
ASSERT(false, "Wrong page table level");
break;
}
return table_offset;
}
static inline uint32_t check_page_table_present(struct map_params *map_params,
uint64_t table_entry)
{
if (map_params->page_table_type == PT_EPT) {
table_entry &= (IA32E_EPT_R_BIT | IA32E_EPT_W_BIT |
IA32E_EPT_X_BIT);
} else {
table_entry &= (IA32E_COMM_P_BIT);
}
return (table_entry) ? PT_PRESENT : PT_NOT_PRESENT;
}
static uint64_t get_table_entry(struct map_params *map_params, void *addr,
void *table_base, uint32_t table_level)
{
uint32_t table_offset;
uint64_t table_entry;
int status = 0;
if (table_base == NULL
|| table_level >= IA32E_UNKNOWN
|| map_params == NULL) {
status = -EINVAL;
}
ASSERT(status == 0, "Incorrect Arguments");
table_offset = fetch_page_table_offset(addr, table_level);
/* Read the table entry */
table_entry = MEM_READ64(table_base + table_offset);
/* Return the next table in the walk */
return table_entry;
}
static void *walk_paging_struct(void *addr, void *table_base,
uint32_t table_level, struct map_params *map_params)
{
uint32_t table_offset;
uint64_t table_entry;
uint64_t table_present;
/* if table_level == IA32E_PT Just return the same address
* can't walk down any further
*/
void *sub_table_addr = ((table_level == IA32E_PT) ? table_base:NULL);
int status = 0;
if (table_base == NULL || table_level >= IA32E_UNKNOWN
|| map_params == NULL) {
status = -EINVAL;
}
ASSERT(status == 0, "Incorrect Arguments");
table_offset = fetch_page_table_offset(addr, table_level);
/* See if we can skip the rest */
if (sub_table_addr != table_base) {
/* Read the table entry */
table_entry = MEM_READ64(table_base + table_offset);
/* Check if EPT entry being created */
if (map_params->page_table_type == PT_EPT) {
/* Set table present bits to any of the
* read/write/execute bits
*/
table_present = (IA32E_EPT_R_BIT | IA32E_EPT_W_BIT |
IA32E_EPT_X_BIT);
} else {
/* Set table preset bits to P bit or r/w bit */
table_present = (IA32E_COMM_P_BIT | IA32E_COMM_RW_BIT);
}
/* Determine if a valid entry exists */
if ((table_entry & table_present) == 0) {
/* No entry present - need to allocate a new table */
sub_table_addr =
alloc_paging_struct();
/* Check to ensure memory available for this structure*/
if (sub_table_addr == 0) {
/* Error: Unable to find table memory necessary
* to map memory
*/
ASSERT(sub_table_addr == 0,
"Fail to find table memory "
"for map memory");
return sub_table_addr;
}
/* Write entry to current table to reference the new
* sub-table
*/
MEM_WRITE64(table_base + table_offset,
(uint64_t) sub_table_addr | table_present);
} else {
/* Get address of the sub-table */
sub_table_addr = (void *)(table_entry & IA32E_REF_MASK);
}
}
/* Return the next table in the walk */
return sub_table_addr;
}
void *get_paging_pml4(void)
{
/* Return address to caller */
return mmu_pml4_addr;
}
void enable_paging(void *pml4_base_addr)
{
CPU_CR_WRITE(cr3, (unsigned long)pml4_base_addr);
}
void init_paging(void)
{
struct map_params map_params;
struct e820_entry *entry;
uint32_t i;
int attr_wb = (MMU_MEM_ATTR_READ |
MMU_MEM_ATTR_WRITE |
MMU_MEM_ATTR_EXECUTE |
MMU_MEM_ATTR_WB_CACHE);
int attr_uc = (MMU_MEM_ATTR_READ |
MMU_MEM_ATTR_WRITE |
MMU_MEM_ATTR_EXECUTE |
MMU_MEM_ATTR_UNCACHED);
pr_dbg("HV MMU Initialization");
check_mmu_capability();
/* Allocate memory for Hypervisor PML4 table */
mmu_pml4_addr = alloc_paging_struct();
init_e820();
obtain_e820_mem_info();
/* Loop through all memory regions in the e820 table */
map_params.page_table_type = PT_HOST;
map_params.pml4_base = mmu_pml4_addr;
/* Map all memory regions to UC attribute */
map_mem(&map_params, (void *)e820_mem.mem_bottom,
(void *)e820_mem.mem_bottom,
(e820_mem.mem_top - e820_mem.mem_bottom),
attr_uc);
/* Modify WB attribute for E820_TYPE_RAM */
for (i = 0, entry = &e820[0];
i < e820_entries;
i++, entry = &e820[i]) {
if (entry->type == E820_TYPE_RAM) {
modify_mem(&map_params, (void *)entry->baseaddr,
(void *)entry->baseaddr,
entry->length, attr_wb);
}
}
pr_dbg("Enabling MMU ");
/* Enable paging */
enable_paging(mmu_pml4_addr);
}
void *alloc_paging_struct(void)
{
void *ptr = NULL;
/* Allocate a page from Hypervisor heap */
ptr = alloc_page();
ASSERT(ptr, "page alloc failed!");
memset(ptr, 0, CPU_PAGE_SIZE);
return ptr;
}
uint64_t config_page_table_attr(struct map_params *map_params, uint32_t flags)
{
int ept_entry = map_params->page_table_type;
uint64_t attr = 0;
/* Convert generic memory flags to architecture specific attributes */
/* Check if read access */
if (flags & MMU_MEM_ATTR_READ) {
/* Configure for read access */
attr |=
(ept_entry ? IA32E_EPT_R_BIT : MMU_MEM_ATTR_BIT_READ_WRITE);
}
/* Check for write access */
if (flags & MMU_MEM_ATTR_WRITE) {
/* Configure for write access */
attr |=
(ept_entry ? IA32E_EPT_W_BIT : MMU_MEM_ATTR_BIT_READ_WRITE);
}
/* Check for execute access */
if (flags & MMU_MEM_ATTR_EXECUTE) {
/* Configure for execute (EPT only) */
attr |= (ept_entry ? IA32E_EPT_X_BIT : 0);
}
/* EPT & VT-d share the same page tables, set SNP bit
* to force snooping of PCIe devices if the page
* is cachable
*/
if ((flags & MMU_MEM_ATTR_UNCACHED) != MMU_MEM_ATTR_UNCACHED
&& ept_entry == PT_EPT) {
attr |= IA32E_EPT_SNOOP_CTRL;
}
/* Check for cache / memory types */
if (flags & MMU_MEM_ATTR_WB_CACHE) {
/* Configure for write back cache */
attr |=
(ept_entry ? IA32E_EPT_WB : MMU_MEM_ATTR_TYPE_CACHED_WB);
} else if (flags & MMU_MEM_ATTR_WT_CACHE) {
/* Configure for write through cache */
attr |=
(ept_entry ? IA32E_EPT_WT : MMU_MEM_ATTR_TYPE_CACHED_WT);
} else if (flags & MMU_MEM_ATTR_UNCACHED) {
/* Configure for uncached */
attr |=
(ept_entry ? IA32E_EPT_UNCACHED : MMU_MEM_ATTR_TYPE_UNCACHED);
} else if (flags & MMU_MEM_ATTR_WC) {
/* Configure for write combining */
attr |=
(ept_entry ? IA32E_EPT_WC : MMU_MEM_ATTR_TYPE_WRITE_COMBINED);
} else {
/* Configure for write protected */
attr |=
(ept_entry ? IA32E_EPT_WP : MMU_MEM_ATTR_TYPE_WRITE_PROTECTED);
}
return attr;
}
void obtain_last_page_table_entry(struct map_params *map_params,
struct entry_params *entry, void *addr, bool direct)
{
uint64_t table_entry;
uint32_t table_present = 0;
/* Obtain the PML4 address */
void *table_addr = direct ? (map_params->pml4_base)
: (map_params->pml4_inverted);
/* Obtain page table entry from PML4 table*/
table_entry = get_table_entry(map_params, addr,
table_addr, IA32E_PML4);
table_present = check_page_table_present(map_params, table_entry);
if (table_present == PT_NOT_PRESENT) {
/* PML4E not present, return PML4 base address */
entry->entry_level = IA32E_PML4;
entry->entry_base = (uint64_t)table_addr;
entry->entry_present = PT_NOT_PRESENT;
entry->page_size = check_mmu_1gb_support(map_params) ?
(PAGE_SIZE_1G) : (PAGE_SIZE_2M);
entry->entry_off = fetch_page_table_offset(addr, IA32E_PML4);
entry->entry_val = table_entry;
return;
}
/* Obtain page table entry from PDPT table*/
table_addr = (void *)(table_entry & IA32E_REF_MASK);
table_entry = get_table_entry(map_params, addr,
table_addr, IA32E_PDPT);
table_present = check_page_table_present(map_params, table_entry);
if (table_present == PT_NOT_PRESENT) {
/* PDPTE not present, return PDPT base address */
entry->entry_level = IA32E_PDPT;
entry->entry_base = (uint64_t)table_addr;
entry->entry_present = PT_NOT_PRESENT;
entry->page_size = check_mmu_1gb_support(map_params) ?
(PAGE_SIZE_1G) : (PAGE_SIZE_2M);
entry->entry_off = fetch_page_table_offset(addr, IA32E_PDPT);
entry->entry_val = table_entry;
return;
}
if (table_entry & IA32E_PDPTE_PS_BIT) {
/* 1GB page size, return the base addr of the pg entry*/
entry->entry_level = IA32E_PDPT;
entry->entry_base = (uint64_t)table_addr;
entry->page_size = check_mmu_1gb_support(map_params) ?
(PAGE_SIZE_1G) : (PAGE_SIZE_2M);
entry->entry_present = PT_PRESENT;
entry->entry_off = fetch_page_table_offset(addr, IA32E_PDPT);
entry->entry_val = table_entry;
return;
}
/* Obtain page table entry from PD table*/
table_addr = (void *)(table_entry&IA32E_REF_MASK);
table_entry = get_table_entry(map_params, addr,
table_addr, IA32E_PD);
table_present = check_page_table_present(map_params, table_entry);
if (table_present == PT_NOT_PRESENT) {
/* PDE not present, return PDE base address */
entry->entry_level = IA32E_PD;
entry->entry_base = (uint64_t)table_addr;
entry->entry_present = PT_NOT_PRESENT;
entry->page_size = PAGE_SIZE_2M;
entry->entry_off = fetch_page_table_offset(addr, IA32E_PD);
entry->entry_val = table_entry;
return;
}
if (table_entry & IA32E_PDE_PS_BIT) {
/* 2MB page size, return the base addr of the pg entry*/
entry->entry_level = IA32E_PD;
entry->entry_base = (uint64_t)table_addr;
entry->entry_present = PT_PRESENT;
entry->page_size = PAGE_SIZE_2M;
entry->entry_off = fetch_page_table_offset(addr, IA32E_PD);
entry->entry_val = table_entry;
return;
}
/* Obtain page table entry from PT table*/
table_addr = (void *)(table_entry&IA32E_REF_MASK);
table_entry = get_table_entry(map_params, addr,
table_addr, IA32E_PT);
table_present = check_page_table_present(map_params, table_entry);
entry->entry_present = ((table_present == PT_PRESENT)
? (PT_PRESENT):(PT_NOT_PRESENT));
entry->entry_level = IA32E_PT;
entry->entry_base = (uint64_t)table_addr;
entry->page_size = PAGE_SIZE_4K;
entry->entry_off = fetch_page_table_offset(addr, IA32E_PT);
entry->entry_val = table_entry;
}
static uint64_t update_page_table_entry(struct map_params *map_params,
void *paddr, void *vaddr, uint64_t size, uint64_t attr,
enum mem_map_request_type request_type, bool direct)
{
uint64_t remaining_size = size;
uint32_t adjustment_size;
int ept_entry = map_params->page_table_type;
/* Obtain the PML4 address */
void *table_addr = direct ? (map_params->pml4_base)
: (map_params->pml4_inverted);
/* Walk from the PML4 table to the PDPT table */
table_addr = walk_paging_struct(vaddr, table_addr, IA32E_PML4,
map_params);
if ((remaining_size >= MEM_1G)
&& (MEM_ALIGNED_CHECK(vaddr, MEM_1G))
&& (MEM_ALIGNED_CHECK(paddr, MEM_1G))
&& check_mmu_1gb_support(map_params)) {
/* Map this 1 GByte memory region */
adjustment_size = map_mem_region(vaddr, paddr,
table_addr, attr, IA32E_PDPT,
ept_entry, request_type);
} else if ((remaining_size >= MEM_2M)
&& (MEM_ALIGNED_CHECK(vaddr, MEM_2M))
&& (MEM_ALIGNED_CHECK(paddr, MEM_2M))) {
/* Walk from the PDPT table to the PD table */
table_addr = walk_paging_struct(vaddr, table_addr,
IA32E_PDPT, map_params);
/* Map this 2 MByte memory region */
adjustment_size = map_mem_region(vaddr, paddr,
table_addr, attr, IA32E_PD, ept_entry,
request_type);
} else {
/* Walk from the PDPT table to the PD table */
table_addr = walk_paging_struct(vaddr,
table_addr, IA32E_PDPT, map_params);
/* Walk from the PD table to the page table */
table_addr = walk_paging_struct(vaddr,
table_addr, IA32E_PD, map_params);
/* Map this 4 KByte memory region */
adjustment_size = map_mem_region(vaddr, paddr,
table_addr, attr, IA32E_PT,
ept_entry, request_type);
}
return adjustment_size;
}
static uint64_t break_page_table(struct map_params *map_params, void *paddr,
void *vaddr, uint64_t page_size, bool direct)
{
uint32_t i = 0;
uint64_t pa;
uint64_t attr = 0x00;
uint64_t next_page_size = 0x00;
void *sub_tab_addr = NULL;
struct entry_params entry;
switch (page_size) {
/* Breaking 1GB page to 2MB page*/
case PAGE_SIZE_1G:
next_page_size = PAGE_SIZE_2M;
attr |= IA32E_PDE_PS_BIT;
pr_info("%s, Breaking 1GB -->2MB vaddr=0x%llx",
__func__, vaddr);
break;
/* Breaking 2MB page to 4KB page*/
case PAGE_SIZE_2M:
next_page_size = PAGE_SIZE_4K;
pr_info("%s, Breaking 2MB -->4KB vaddr=0x%llx",
__func__, vaddr);
break;
/* 4KB page, No action*/
case PAGE_SIZE_4K:
default:
next_page_size = PAGE_SIZE_4K;
pr_info("%s, Breaking 4KB no action vaddr=0x%llx",
__func__, vaddr);
break;
}
if (page_size != next_page_size) {
obtain_last_page_table_entry(map_params, &entry, vaddr, direct);
/* New entry present - need to allocate a new table */
sub_tab_addr = alloc_paging_struct();
/* Check to ensure memory available for this structure */
if (sub_tab_addr == 0) {
/* Error:
* Unable to find table memory necessary to map memory
*/
pr_err("Fail to find table memory for map memory");
ASSERT(sub_tab_addr == 0, "");
return 0;
}
/* the physical address maybe be not aligned of
* current page size, obtain the starting physical address
* aligned of current page size
*/
pa = ((((uint64_t)paddr) / page_size) * page_size);
if (map_params->page_table_type == PT_EPT) {
/* Keep original attribute(here &0x3f)
* bit 0(R) bit1(W) bit2(X) bit3~5 MT
*/
attr |= (entry.entry_val & 0x3f);
} else {
/* Keep original attribute(here &0x7f) */
attr |= (entry.entry_val & 0x7f);
}
/* write all entries and keep original attr*/
for (i = 0; i < IA32E_NUM_ENTRIES; i++) {
MEM_WRITE64(sub_tab_addr + (i * IA32E_COMM_ENTRY_SIZE),
(attr | (pa + (i * next_page_size))));
}
if (map_params->page_table_type == PT_EPT) {
/* Write the table entry to map this memory,
* SDM chapter28 figure 28-1
* bit 0(R) bit1(W) bit2(X) bit3~5 MUST be reserved
* (here &0x07)
*/
MEM_WRITE64(entry.entry_base + entry.entry_off,
((entry.entry_val & 0x07) |
((uint64_t)sub_tab_addr)));
} else {
/* Write the table entry to map this memory,
* SDM chapter4 figure 4-11
* bit0(P) bit1(RW) bit2(U/S) bit3(PWT) bit4(PCD)
* bit5(A) bit6(D or Ignore)
*/
MEM_WRITE64(entry.entry_base + entry.entry_off,
((entry.entry_val & 0x7f) |
((uint64_t)sub_tab_addr)));
}
}
return next_page_size;
}
static void modify_paging(struct map_params *map_params, void *paddr,
void *vaddr, uint64_t size, uint32_t flags,
enum mem_map_request_type request_type, bool direct)
{
int64_t remaining_size;
uint64_t adjust_size;
uint64_t attr;
int status = 0;
struct entry_params entry;
uint64_t page_size;
uint64_t vaddr_end = ((uint64_t)vaddr) + size;
/* if the address is not PAGE aligned, will drop
* the unaligned part
*/
paddr = (void *)ROUND_PAGE_UP((uint64_t)paddr);
vaddr = (void *)ROUND_PAGE_UP((uint64_t)vaddr);
vaddr_end = ROUND_PAGE_DOWN(vaddr_end);
remaining_size = vaddr_end - (uint64_t)vaddr;
if ((request_type >= PAGING_REQUEST_TYPE_UNKNOWN)
|| (map_params == NULL)) {
pr_err("%s: vaddr=0x%llx size=0x%llx req_type=0x%lx",
__func__, vaddr, size, request_type);
status = -EINVAL;
}
ASSERT(status == 0, "Incorrect Arguments");
attr = config_page_table_attr(map_params, flags);
/* Loop until the entire block of memory is appropriately
* MAP/UNMAP/MODIFY
*/
while (remaining_size > 0) {
obtain_last_page_table_entry(map_params, &entry, vaddr, direct);
/* filter the unmap request, no action in this case*/
page_size = entry.page_size;
if ((request_type == PAGING_REQUEST_TYPE_UNMAP)
&& (entry.entry_present == PT_NOT_PRESENT)) {
adjust_size =
page_size - ((uint64_t)(vaddr) % page_size);
vaddr += adjust_size;
paddr += adjust_size;
remaining_size -= adjust_size;
continue;
}
/* if the address is NOT aligned of current page size,
* or required memory size < page size
* need to break page firstly
*/
if (entry.entry_present == PT_PRESENT) {
/* Maybe need to recursive breaking in this case
* e.g. 1GB->2MB->4KB
*/
while ((uint64_t)remaining_size < page_size
|| (!MEM_ALIGNED_CHECK(vaddr, page_size))
|| (!MEM_ALIGNED_CHECK(paddr, page_size))) {
/* The breaking function return the page size
* of next level page table
*/
page_size = break_page_table(map_params,
paddr, vaddr, page_size, direct);
}
} else {
page_size = ((uint64_t)remaining_size < page_size)
? ((uint64_t)remaining_size) : (page_size);
}
/* The function return the memory size that one entry can map */
adjust_size = update_page_table_entry(map_params, paddr, vaddr,
page_size, attr, request_type, direct);
vaddr += adjust_size;
paddr += adjust_size;
remaining_size -= adjust_size;
}
}
void map_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags)
{
/* used for MMU and EPT*/
modify_paging(map_params, paddr, vaddr, size, flags,
PAGING_REQUEST_TYPE_MAP, true);
/* only for EPT */
if (map_params->page_table_type == PT_EPT) {
modify_paging(map_params, vaddr, paddr, size, flags,
PAGING_REQUEST_TYPE_MAP, false);
}
}
void unmap_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags)
{
/* used for MMU and EPT */
modify_paging(map_params, paddr, vaddr, size, flags,
PAGING_REQUEST_TYPE_UNMAP, true);
/* only for EPT */
if (map_params->page_table_type == PT_EPT) {
modify_paging(map_params, vaddr, paddr, size, flags,
PAGING_REQUEST_TYPE_UNMAP, false);
}
}
void modify_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags)
{
/* used for MMU and EPT*/
modify_paging(map_params, paddr, vaddr, size, flags,
PAGING_REQUEST_TYPE_MODIFY, true);
/* only for EPT */
if (map_params->page_table_type == PT_EPT) {
modify_paging(map_params, vaddr, paddr, size, flags,
PAGING_REQUEST_TYPE_MODIFY, false);
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include <irq.h>
static struct dev_handler_node *notification_node;
/* run in interrupt context */
static int kick_notification(__unused int irq, __unused void *data)
{
/* Notification vector does not require handling here, it's just used
* to kick taget cpu out of non-root mode.
*/
return 0;
}
static int request_notification_irq(dev_handler_t func, void *data,
const char *name)
{
int irq = -1; /* system allocate */
struct dev_handler_node *node = NULL;
if (notification_node != NULL) {
pr_info("%s, Notification vector already allocated on this CPU",
__func__);
return -EBUSY;
}
/* all cpu register the same notification vector */
node = pri_register_handler(irq, VECTOR_NOTIFY_VCPU, func, data, name);
if (node == NULL) {
pr_err("Failed to add notify isr");
return -1;
}
update_irq_handler(dev_to_irq(node), quick_handler_nolock);
notification_node = node;
return 0;
}
void setup_notification(void)
{
int cpu;
char name[32] = {0};
cpu = get_cpu_id();
if (cpu > 0)
return;
/* support IPI notification, VM0 will register all CPU */
snprintf(name, 32, "NOTIFY_ISR%d", cpu);
if (request_notification_irq(kick_notification, NULL, name) < 0) {
pr_err("Failed to setup notification");
return;
}
dev_dbg(ACRN_DBG_PTIRQ, "NOTIFY: irq[%d] setup vector %x",
dev_to_irq(notification_node),
dev_to_vector(notification_node));
}
void cleanup_notification(void)
{
if (notification_node)
unregister_handler_common(notification_node);
notification_node = NULL;
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
static DEFINE_CPU_DATA(uint64_t, softirq_pending);
void disable_softirq(int cpu_id)
{
bitmap_clr(SOFTIRQ_ATOMIC, &per_cpu(softirq_pending, cpu_id));
}
void enable_softirq(int cpu_id)
{
bitmap_set(SOFTIRQ_ATOMIC, &per_cpu(softirq_pending, cpu_id));
}
void init_softirq(void)
{
int cpu_id;
for (cpu_id = 0; cpu_id < phy_cpu_num; cpu_id++) {
per_cpu(softirq_pending, cpu_id) = 0;
bitmap_set(SOFTIRQ_ATOMIC, &per_cpu(softirq_pending, cpu_id));
}
}
void raise_softirq(int softirq_id)
{
int cpu_id = get_cpu_id();
uint64_t *bitmap = &per_cpu(softirq_pending, cpu_id);
if (cpu_id >= phy_cpu_num)
return;
bitmap_set(softirq_id, bitmap);
}
void exec_softirq(void)
{
int cpu_id = get_cpu_id();
uint64_t *bitmap = &per_cpu(softirq_pending, cpu_id);
uint64_t rflag;
int softirq_id;
if (cpu_id >= phy_cpu_num)
return;
/* Disable softirq
* SOFTIRQ_ATOMIC bit = 0 means softirq already in execution
*/
if (!bitmap_test_and_clear(SOFTIRQ_ATOMIC, bitmap))
return;
if (((*bitmap) & SOFTIRQ_MASK) == 0UL)
goto ENABLE_AND_EXIT;
/* check if we are in interrupt context */
CPU_RFLAGS_SAVE(&rflag);
if (!(rflag & (1<<9)))
goto ENABLE_AND_EXIT;
while (1) {
softirq_id = bitmap_ffs(bitmap);
if ((softirq_id < 0) || (softirq_id >= SOFTIRQ_MAX))
break;
bitmap_clr(softirq_id, bitmap);
switch (softirq_id) {
case SOFTIRQ_TIMER:
timer_softirq(cpu_id);
break;
case SOFTIRQ_DEV_ASSIGN:
ptdev_softirq(cpu_id);
break;
default:
break;
}
}
ENABLE_AND_EXIT:
enable_softirq(cpu_id);
}

561
hypervisor/arch/x86/timer.c Normal file
View File

@@ -0,0 +1,561 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define MAX_TIMER_ACTIONS 32
struct timer_statistics {
struct {
uint64_t pickup_id;
uint64_t pickup_time;
uint64_t pickup_deadline;
uint64_t added_id;
uint64_t added_time;
uint64_t added_deadline;
} last;
uint64_t total_pickup_cnt;
uint64_t total_added_cnt;
uint64_t irq_cnt;
long pending_cnt;
};
struct timer {
timer_handle_t func; /* callback if time reached */
uint64_t priv_data; /* func private data */
uint64_t deadline; /* tsc deadline to interrupt */
long handle; /* unique handle for user */
int cpu_id; /* armed on which CPU */
int id; /* timer ID, used by release */
struct list_head node; /* link all timers */
};
struct per_cpu_timers {
struct timer *timers_pool; /* it's timers pool for allocation */
uint64_t free_bitmap;
struct list_head timer_list; /* it's for runtime active timer list */
spinlock_t lock;
int cpu_id;
struct timer_statistics stat;
};
static DEFINE_CPU_DATA(struct per_cpu_timers, cpu_timers);
#define TIMER_IRQ (NR_MAX_IRQS - 1)
DEFINE_CPU_DATA(struct dev_handler_node *, timer_node);
static struct timer*
find_expired_timer(struct per_cpu_timers *cpu_timer, uint64_t tsc_now);
static struct timer *alloc_timer(int cpu_id)
{
int idx;
struct per_cpu_timers *cpu_timer;
struct timer *timer;
spinlock_rflags;
cpu_timer = &per_cpu(cpu_timers, cpu_id);
spinlock_irqsave_obtain(&cpu_timer->lock);
idx = bitmap_ffs(&cpu_timer->free_bitmap);
if (idx < 0) {
spinlock_irqrestore_release(&cpu_timer->lock);
return NULL;
}
bitmap_clr(idx, &cpu_timer->free_bitmap);
cpu_timer->stat.total_added_cnt++;
cpu_timer->stat.pending_cnt++;
/* assign unique handle and never duplicate */
timer = cpu_timer->timers_pool + idx;
timer->handle = cpu_timer->stat.total_added_cnt;
spinlock_irqrestore_release(&cpu_timer->lock);
ASSERT((cpu_timer->timers_pool[cpu_id].cpu_id == cpu_id),
"timer cpu_id did not match");
return timer;
}
static void release_timer(struct timer *timer)
{
struct per_cpu_timers *cpu_timer;
spinlock_rflags;
cpu_timer = &per_cpu(cpu_timers, timer->cpu_id);
timer->priv_data = 0;
timer->func = NULL;
timer->deadline = 0;
spinlock_irqsave_obtain(&cpu_timer->lock);
bitmap_set(timer->id, &cpu_timer->free_bitmap);
cpu_timer->stat.pending_cnt--;
spinlock_irqrestore_release(&cpu_timer->lock);
}
static int get_target_cpu(void)
{
/* we should search idle CPU to balance timer service */
return get_cpu_id();
}
static struct timer*
find_expired_timer(struct per_cpu_timers *cpu_timer, uint64_t tsc_now)
{
struct timer *timer;
struct list_head *pos;
spinlock_rflags;
spinlock_irqsave_obtain(&cpu_timer->lock);
list_for_each(pos, &cpu_timer->timer_list) {
timer = list_entry(pos, struct timer, node);
if (timer->deadline <= tsc_now)
goto UNLOCK;
}
timer = NULL;
UNLOCK:
spinlock_irqrestore_release(&cpu_timer->lock);
return timer;
}
/* need lock protect outside */
static struct timer*
_search_nearest_timer(struct per_cpu_timers *cpu_timer)
{
struct timer *timer;
struct timer *target = NULL;
struct list_head *pos;
list_for_each(pos, &cpu_timer->timer_list) {
timer = list_entry(pos, struct timer, node);
if (target == NULL)
target = timer;
else if (timer->deadline < target->deadline)
target = timer;
}
return target;
}
/* need lock protect outside */
static struct timer*
_search_timer_by_handle(struct per_cpu_timers *cpu_timer, long handle)
{
struct timer *timer;
struct list_head *pos;
list_for_each(pos, &cpu_timer->timer_list) {
timer = list_entry(pos, struct timer, node);
if (timer->handle == handle)
goto FOUND;
}
timer = NULL;
FOUND:
return timer;
}
static void
run_timer(struct per_cpu_timers *cpu_timer, struct timer *timer)
{
spinlock_rflags;
/* remove from list first */
spinlock_irqsave_obtain(&cpu_timer->lock);
list_del(&timer->node);
spinlock_irqrestore_release(&cpu_timer->lock);
/* deadline = 0 means stop timer, we should skip */
if (timer->func && timer->deadline != 0UL)
timer->func(timer->priv_data);
cpu_timer->stat.last.pickup_id = timer->id;
cpu_timer->stat.last.pickup_deadline = timer->deadline;
cpu_timer->stat.last.pickup_time = rdtsc();
cpu_timer->stat.total_pickup_cnt++;
TRACE_4I(TRACE_TIMER_ACTION_PCKUP, timer->id, timer->deadline,
timer->deadline >> 32, cpu_timer->stat.total_pickup_cnt);
}
/* run in interrupt context */
static int tsc_deadline_handler(__unused int irq, __unused void *data)
{
raise_softirq(SOFTIRQ_TIMER);
return 0;
}
static inline void schedule_next_timer(int cpu)
{
struct timer *timer;
struct per_cpu_timers *cpu_timer = &per_cpu(cpu_timers, cpu);
spinlock_rflags;
spinlock_irqsave_obtain(&cpu_timer->lock);
timer = _search_nearest_timer(cpu_timer);
if (timer) {
/* it is okay to program a expired time */
msr_write(MSR_IA32_TSC_DEADLINE, timer->deadline);
}
spinlock_irqrestore_release(&cpu_timer->lock);
}
int request_timer_irq(int cpu, dev_handler_t func, void *data, const char *name)
{
struct dev_handler_node *node = NULL;
if (cpu >= phy_cpu_num)
return -1;
if (per_cpu(timer_node, cpu)) {
pr_err("CPU%d timer isr already added", cpu);
unregister_handler_common(per_cpu(timer_node, cpu));
}
node = pri_register_handler(TIMER_IRQ, VECTOR_TIMER, func, data, name);
if (node != NULL) {
per_cpu(timer_node, cpu) = node;
update_irq_handler(TIMER_IRQ, quick_handler_nolock);
} else {
pr_err("Failed to add timer isr");
return -1;
}
return 0;
}
/*TODO: init in separate cpu */
static void init_timer_pool(void)
{
int i, j;
struct per_cpu_timers *cpu_timer;
struct timer *timers_pool;
/* Make sure only init one time*/
if (get_cpu_id() > 0)
return;
for (i = 0; i < phy_cpu_num; i++) {
cpu_timer = &per_cpu(cpu_timers, i);
cpu_timer->cpu_id = i;
timers_pool =
calloc(MAX_TIMER_ACTIONS, sizeof(struct timer));
ASSERT(timers_pool, "Create timers pool failed");
cpu_timer->timers_pool = timers_pool;
cpu_timer->free_bitmap = (1UL<<MAX_TIMER_ACTIONS)-1;
INIT_LIST_HEAD(&cpu_timer->timer_list);
spinlock_init(&cpu_timer->lock);
for (j = 0; j < MAX_TIMER_ACTIONS; j++) {
timers_pool[j].id = j;
timers_pool[j].cpu_id = i;
timers_pool[j].priv_data = 0;
timers_pool[j].func = NULL;
timers_pool[j].deadline = 0;
timers_pool[j].handle = -1UL;
}
}
}
static void init_tsc_deadline_timer(void)
{
uint32_t val;
val = VECTOR_TIMER;
val |= 0x40000; /* TSC deadline and unmask */
mmio_write_long(val, LAPIC_BASE + LAPIC_LVT_TIMER_REGISTER);
asm volatile("mfence" : : : "memory");
/* disarm timer */
msr_write(MSR_IA32_TSC_DEADLINE, 0UL);
}
void timer_init(void)
{
char name[32] = {0};
int cpu = get_cpu_id();
snprintf(name, 32, "timer_tick[%d]", cpu);
if (request_timer_irq(cpu, tsc_deadline_handler, NULL, name) < 0) {
pr_err("Timer setup failed");
return;
}
init_tsc_deadline_timer();
init_timer_pool();
}
void timer_cleanup(void)
{
int cpu = get_cpu_id();
if (per_cpu(timer_node, cpu))
unregister_handler_common(per_cpu(timer_node, cpu));
per_cpu(timer_node, cpu) = NULL;
}
int timer_softirq(int cpu_id)
{
struct per_cpu_timers *cpu_timer;
struct timer *timer;
int max = MAX_TIMER_ACTIONS;
/* handle passed timer */
cpu_timer = &per_cpu(cpu_timers, cpu_id);
cpu_timer->stat.irq_cnt++;
/* This is to make sure we are not blocked due to delay inside func()
* force to exit irq handler after we serviced >31 timers
* caller used to add_timer() in timer->func(), if there is a delay
* inside func(), it will infinitely loop here, because new added timer
* already passed due to previously func()'s delay.
*/
timer = find_expired_timer(cpu_timer, rdtsc());
while (timer && --max > 0) {
run_timer(cpu_timer, timer);
/* put back to timer pool */
release_timer(timer);
/* search next one */
timer = find_expired_timer(cpu_timer, rdtsc());
}
/* update nearest timer */
schedule_next_timer(cpu_id);
return 0;
}
/*
* add_timer is okay to add passed timer but not 0
* return: handle, this handle is unique and can be used to find back
* this added timer. handle will be invalid after timer expired
*/
long add_timer(timer_handle_t func, uint64_t data, uint64_t deadline)
{
struct timer *timer;
struct per_cpu_timers *cpu_timer;
int cpu_id = get_target_cpu();
spinlock_rflags;
if (deadline == 0 || func == NULL)
return -1;
/* possible interrupt context please avoid mem alloct here*/
timer = alloc_timer(cpu_id);
if (timer == NULL)
return -1;
timer->func = func;
timer->priv_data = data;
timer->deadline = deadline;
timer->cpu_id = get_target_cpu();
cpu_timer = &per_cpu(cpu_timers, timer->cpu_id);
/* We need irqsave here even softirq enabled to protect timer_list */
spinlock_irqsave_obtain(&cpu_timer->lock);
list_add_tail(&timer->node, &cpu_timer->timer_list);
cpu_timer->stat.last.added_id = timer->id;
cpu_timer->stat.last.added_time = rdtsc();
cpu_timer->stat.last.added_deadline = timer->deadline;
spinlock_irqrestore_release(&cpu_timer->lock);
TRACE_4I(TRACE_TIMER_ACTION_ADDED, timer->id, timer->deadline,
timer->deadline >> 32, cpu_timer->stat.total_added_cnt);
schedule_next_timer(cpu_id);
return timer->handle;
}
/*
* update_timer existing timer. if not found, add new timer
*/
long
update_timer(long handle, timer_handle_t func, uint64_t data,
uint64_t deadline)
{
struct timer *timer;
struct per_cpu_timers *cpu_timer;
int cpu_id = get_target_cpu();
spinlock_rflags;
bool ret = false;
if (deadline == 0)
return -1;
cpu_timer = &per_cpu(cpu_timers, cpu_id);
spinlock_irqsave_obtain(&cpu_timer->lock);
timer = _search_timer_by_handle(cpu_timer, handle);
if (timer) {
/* update deadline and re-sort */
timer->deadline = deadline;
timer->func = func;
timer->priv_data = data;
TRACE_4I(TRACE_TIMER_ACTION_UPDAT, timer->id,
timer->deadline, timer->deadline >> 32,
cpu_timer->stat.total_added_cnt);
ret = true;
}
spinlock_irqrestore_release(&cpu_timer->lock);
if (ret)
schedule_next_timer(cpu_id);
else {
/* if update failed, we add to new, and update handle */
/* TODO: the correct behavior should be return failure here */
handle = add_timer(func, data, deadline);
}
return handle;
}
/* NOTE: cpu_id referred to physical cpu id here */
bool cancel_timer(long handle, int cpu_id)
{
struct timer *timer;
struct per_cpu_timers *cpu_timer;
spinlock_rflags;
bool ret = false;
cpu_timer = &per_cpu(cpu_timers, cpu_id);
spinlock_irqsave_obtain(&cpu_timer->lock);
timer = _search_timer_by_handle(cpu_timer, handle);
if (timer) {
/* NOTE: we can not directly release timer here.
* Instead we set deadline to expired and clear func.
* This timer will be reclaim next timer
*/
timer->deadline = 0;
timer->func = NULL;
ret = true;
}
spinlock_irqrestore_release(&cpu_timer->lock);
return ret;
}
void dump_timer_pool_info(int cpu_id)
{
struct per_cpu_timers *cpu_timer =
&per_cpu(cpu_timers, cpu_id);
struct list_head *pos;
int cn = 0;
spinlock_rflags;
if (cpu_id >= phy_cpu_num)
return;
pr_info("Timer%d statistics: Pending: %d\n\t"
"total_pickup: %lld total_added: %lld total_irq: %lld",
cpu_id,
cpu_timer->stat.pending_cnt,
cpu_timer->stat.total_pickup_cnt,
cpu_timer->stat.total_added_cnt,
cpu_timer->stat.irq_cnt);
pr_info("LAST pickup[%d] time: 0x%llx deadline: 0x%llx",
cpu_timer->stat.last.pickup_id,
cpu_timer->stat.last.pickup_time,
cpu_timer->stat.last.pickup_deadline);
pr_info("LAST added[%d] time: 0x%llx deadline: 0x%llx",
cpu_timer->stat.last.added_id,
cpu_timer->stat.last.added_time,
cpu_timer->stat.last.added_deadline);
spinlock_irqsave_obtain(&cpu_timer->lock);
list_for_each(pos, &cpu_timer->timer_list) {
cn++;
pr_info("-->pending: %d trigger: 0x%llx", cn,
list_entry(pos, struct timer, node)->deadline);
}
spinlock_irqrestore_release(&cpu_timer->lock);
}
void check_tsc(void)
{
uint64_t temp64;
/* Ensure time-stamp timer is turned on for each CPU */
CPU_CR_READ(cr4, &temp64);
CPU_CR_WRITE(cr4, (temp64 & ~CR4_TSD));
}
uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us)
{
uint16_t initial_pit;
uint16_t current_pit;
uint32_t current_tsc;
#define PIT_TARGET 0x3FFF
if (timer_period_in_us < 1000)
pr_warn("Bad timer_period_in_us: %d\n",
timer_period_in_us);
/* Assume the 8254 delivers 18.2 ticks per second when 16 bits fully
* wrap. This is about 1.193MHz or a clock period of 0.8384uSec
*/
initial_pit = (uint16_t)(timer_period_in_us*1193000UL/1000000);
initial_pit += PIT_TARGET;
/* Port 0x43 ==> Control word write; Data 0x30 ==> Select Counter 0,
* Read/Write least significant byte first, mode 0, 16 bits.
*/
io_write_byte(0x30, 0x43);
io_write_byte(initial_pit & 0x00ff, 0x40); /* Write LSB */
io_write_byte(initial_pit >> 8, 0x40); /* Write MSB */
current_tsc = rdtsc();
do {
/* Port 0x43 ==> Control word write; 0x00 ==> Select
* Counter 0, Counter Latch Command, Mode 0; 16 bits
*/
io_write_byte(0x00, 0x43);
current_pit = io_read_byte(0x40); /* Read LSB */
current_pit |= io_read_byte(0x40) << 8; /* Read MSB */
/* Let the counter count down to PIT_TARGET */
} while (current_pit > PIT_TARGET);
current_tsc = rdtsc() - current_tsc;
return (uint64_t) current_tsc;
}

View File

@@ -0,0 +1,494 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
static int rdtscp_handler(struct vcpu *vcpu);
static int unhandled_vmexit_handler(struct vcpu *vcpu);
static int rdtsc_handler(struct vcpu *vcpu);
/* VM Dispatch table for Exit condition handling */
static const struct vm_exit_dispatch dispatch_table[] = {
[VMX_EXIT_REASON_EXCEPTION_OR_NMI] = {
.handler = exception_handler},
[VMX_EXIT_REASON_EXTERNAL_INTERRUPT] = {
.handler = external_interrupt_handler},
[VMX_EXIT_REASON_TRIPLE_FAULT] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_INIT_SIGNAL] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_STARTUP_IPI] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_IO_SMI] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_OTHER_SMI] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_INTERRUPT_WINDOW] = {
.handler = interrupt_win_exiting_handler},
[VMX_EXIT_REASON_NMI_WINDOW] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_TASK_SWITCH] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_CPUID] = {
.handler = cpuid_handler},
[VMX_EXIT_REASON_GETSEC] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_HLT] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_INVD] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_INVLPG] = {
.handler = unhandled_vmexit_handler,},
[VMX_EXIT_REASON_RDPMC] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_RDTSC] = {
.handler = rdtsc_handler},
[VMX_EXIT_REASON_RSM] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMCALL] = {
.handler = vmcall_handler},
[VMX_EXIT_REASON_VMCLEAR] {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMLAUNCH] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMPTRLD] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMPTRST] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMREAD] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMRESUME] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMWRITE] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMXOFF] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_VMXON] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_CR_ACCESS] = {
.handler = cr_access_handler,
.need_exit_qualification = 1},
[VMX_EXIT_REASON_DR_ACCESS] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_IO_INSTRUCTION] = {
.handler = io_instr_handler,
.need_exit_qualification = 1},
[VMX_EXIT_REASON_RDMSR] = {
.handler = rdmsr_handler},
[VMX_EXIT_REASON_WRMSR] = {
.handler = wrmsr_handler},
[VMX_EXIT_REASON_ENTRY_FAILURE_INVALID_GUEST_STATE] = {
.handler = unhandled_vmexit_handler,
.need_exit_qualification = 1},
[VMX_EXIT_REASON_ENTRY_FAILURE_MSR_LOADING] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_MWAIT] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_MONITOR_TRAP] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_MONITOR] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_PAUSE] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_ENTRY_FAILURE_MACHINE_CHECK] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_TPR_BELOW_THRESHOLD] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_APIC_ACCESS] = {
.handler = apicv_access_exit_handler},
[VMX_EXIT_REASON_VIRTUALIZED_EOI] = {
.handler = apicv_virtualized_eoi_exit_handler},
[VMX_EXIT_REASON_GDTR_IDTR_ACCESS] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_LDTR_TR_ACCESS] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_EPT_VIOLATION] = {
.handler = ept_violation_handler,
.need_exit_qualification = 1},
[VMX_EXIT_REASON_EPT_MISCONFIGURATION] = {
.handler = ept_misconfig_handler,
.need_exit_qualification = 1},
[VMX_EXIT_REASON_INVEPT] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_RDTSCP] = {
.handler = rdtscp_handler},
[VMX_EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_INVVPID] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_WBINVD] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_XSETBV] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_APIC_WRITE] = {
.handler = apicv_write_exit_handler}
};
struct vm_exit_dispatch *vmexit_handler(struct vcpu *vcpu)
{
struct vm_exit_dispatch *dispatch = HV_NULL;
uint16_t basic_exit_reason;
/* Obtain interrupt info */
vcpu->arch_vcpu.exit_interrupt_info =
exec_vmread(VMX_IDT_VEC_INFO_FIELD);
/* Calculate basic exit reason (low 16-bits) */
basic_exit_reason = vcpu->arch_vcpu.exit_reason & 0xFFFF;
/* Log details for exit */
pr_dbg("Exit Reason: 0x%016llx ", vcpu->arch_vcpu.exit_reason);
/* Ensure exit reason is within dispatch table */
if (basic_exit_reason < ARRAY_SIZE(dispatch_table)) {
/* Calculate dispatch table entry */
dispatch = (struct vm_exit_dispatch *)
(dispatch_table + basic_exit_reason);
/* See if an exit qualification is necessary for this exit
* handler
*/
if (dispatch->need_exit_qualification) {
/* Get exit qualification */
vcpu->arch_vcpu.exit_qualification =
exec_vmread(VMX_EXIT_QUALIFICATION);
}
}
/* Update current vcpu in VM that caused vm exit */
vcpu->vm->current_vcpu = vcpu;
/* Return pointer to exit dispatch entry */
return dispatch;
}
static int unhandled_vmexit_handler(__unused struct vcpu *vcpu)
{
pr_fatal("Error: Unhandled VM exit condition from guest at 0x%016llx ",
exec_vmread(VMX_GUEST_RIP));
pr_fatal("Exit Reason: 0x%016llx ", vcpu->arch_vcpu.exit_reason);
pr_err("Exit qualification: 0x%016llx ",
exec_vmread(VMX_EXIT_QUALIFICATION));
/* while(1); */
TRACE_2L(TRC_VMEXIT_UNHANDLED, vcpu->arch_vcpu.exit_reason, 0);
return 0;
}
static int write_cr0(struct vcpu *vcpu, uint64_t value)
{
uint32_t value32;
uint64_t value64;
pr_dbg("VMM: Guest trying to write 0x%08x to CR0", value);
/* Read host mask value */
value64 = exec_vmread(VMX_CR0_MASK);
/* Clear all bits being written by guest that are owned by host */
value &= ~value64;
/* Update CR0 in guest state */
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr0 |= value;
exec_vmwrite(VMX_GUEST_CR0,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr0);
pr_dbg("VMM: Guest allowed to write 0x%08x to CR0",
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr0);
/* If guest is trying to transition vcpu from unpaged real mode to page
* protected mode make necessary changes to VMCS structure to reflect
* transition from real mode to paged-protected mode
*/
if (!is_vcpu_bsp(vcpu) &&
(vcpu->arch_vcpu.cpu_mode == REAL_MODE) &&
(value & CR0_PG) && (value & CR0_PE)) {
/* Enable protected mode */
value32 = exec_vmread(VMX_ENTRY_CONTROLS);
value32 |= (VMX_ENTRY_CTLS_IA32E_MODE |
VMX_ENTRY_CTLS_LOAD_PAT |
VMX_ENTRY_CTLS_LOAD_EFER);
exec_vmwrite(VMX_ENTRY_CONTROLS, value32);
pr_dbg("VMX_ENTRY_CONTROLS: 0x%x ", value32);
/* Disable unrestricted mode */
value32 = exec_vmread(VMX_PROC_VM_EXEC_CONTROLS2);
value32 |= (VMX_PROCBASED_CTLS2_EPT |
VMX_PROCBASED_CTLS2_RDTSCP);
exec_vmwrite(VMX_PROC_VM_EXEC_CONTROLS2, value32);
pr_dbg("VMX_PROC_VM_EXEC_CONTROLS2: 0x%x ", value32);
/* Set up EFER */
value64 = exec_vmread64(VMX_GUEST_IA32_EFER_FULL);
value64 |= (MSR_IA32_EFER_SCE_BIT |
MSR_IA32_EFER_LME_BIT |
MSR_IA32_EFER_LMA_BIT | MSR_IA32_EFER_NXE_BIT);
exec_vmwrite64(VMX_GUEST_IA32_EFER_FULL, value64);
pr_dbg("VMX_GUEST_IA32_EFER: 0x%016llx ", value64);
}
return 0;
}
static int write_cr3(struct vcpu *vcpu, uint64_t value)
{
/* Write to guest's CR3 */
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3 = value;
/* Commit new value to VMCS */
exec_vmwrite(VMX_GUEST_CR3,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3);
return 0;
}
static int write_cr4(struct vcpu *vcpu, uint64_t value)
{
uint64_t temp64;
pr_dbg("VMM: Guest trying to write 0x%08x to CR4", value);
/* Read host mask value */
temp64 = exec_vmread(VMX_CR4_MASK);
/* Clear all bits being written by guest that are owned by host */
value &= ~temp64;
/* Write updated CR4 (bitwise OR of allowed guest bits and CR4 host
* value)
*/
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr4 |= value;
exec_vmwrite(VMX_GUEST_CR4,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr4);
pr_dbg("VMM: Guest allowed to write 0x%08x to CR4",
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr4);
return 0;
}
static int read_cr3(struct vcpu *vcpu, uint64_t *value)
{
*value = vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].cr3;
pr_dbg("VMM: reading 0x%08x from CR3", *value);
return 0;
}
int cpuid_handler(struct vcpu *vcpu)
{
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
emulate_cpuid(vcpu, (uint32_t)cur_context->guest_cpu_regs.regs.rax,
(uint32_t *)&cur_context->guest_cpu_regs.regs.rax,
(uint32_t *)&cur_context->guest_cpu_regs.regs.rbx,
(uint32_t *)&cur_context->guest_cpu_regs.regs.rcx,
(uint32_t *)&cur_context->guest_cpu_regs.regs.rdx);
TRACE_2L(TRC_VMEXIT_CPUID, vcpu->vcpu_id, 0);
return 0;
}
int cr_access_handler(struct vcpu *vcpu)
{
uint64_t *regptr;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
static const int reg_trans_tab[] = {
[0] = VMX_MACHINE_T_GUEST_RAX_INDEX,
[1] = VMX_MACHINE_T_GUEST_RCX_INDEX,
[2] = VMX_MACHINE_T_GUEST_RDX_INDEX,
[3] = VMX_MACHINE_T_GUEST_RBX_INDEX,
[4] = 0xFF, /* for sp reg, should not be used, just for init */
[5] = VMX_MACHINE_T_GUEST_RBP_INDEX,
[6] = VMX_MACHINE_T_GUEST_RSI_INDEX,
[7] = VMX_MACHINE_T_GUEST_RDI_INDEX,
[8] = VMX_MACHINE_T_GUEST_R8_INDEX,
[9] = VMX_MACHINE_T_GUEST_R9_INDEX,
[10] = VMX_MACHINE_T_GUEST_R10_INDEX,
[11] = VMX_MACHINE_T_GUEST_R11_INDEX,
[12] = VMX_MACHINE_T_GUEST_R12_INDEX,
[13] = VMX_MACHINE_T_GUEST_R13_INDEX,
[14] = VMX_MACHINE_T_GUEST_R14_INDEX,
[15] = VMX_MACHINE_T_GUEST_R15_INDEX
};
int idx = VM_EXIT_CR_ACCESS_REG_IDX(vcpu->arch_vcpu.exit_qualification);
ASSERT(idx != 4, "index should not be 4 (target SP)");
regptr = cur_context->guest_cpu_regs.longs + reg_trans_tab[idx];
switch ((VM_EXIT_CR_ACCESS_ACCESS_TYPE
(vcpu->arch_vcpu.exit_qualification) << 4) |
VM_EXIT_CR_ACCESS_CR_NUM(vcpu->arch_vcpu.exit_qualification)) {
case 0x00:
/* mov to cr0 */
write_cr0(vcpu, *regptr);
break;
case 0x03:
/* mov to cr3 */
write_cr3(vcpu, *regptr);
break;
case 0x04:
/* mov to cr4 */
write_cr4(vcpu, *regptr);
break;
case 0x13:
/* mov from cr3 */
read_cr3(vcpu, regptr);
break;
#if 0
case 0x14:
/* mov from cr4 (this should not happen) */
case 0x10:
/* mov from cr0 (this should not happen) */
#endif
case 0x08:
/* mov to cr8 */
vlapic_set_cr8(vcpu->arch_vcpu.vlapic, *regptr);
break;
case 0x18:
/* mov from cr8 */
*regptr = vlapic_get_cr8(vcpu->arch_vcpu.vlapic);
break;
default:
panic("Unhandled CR access");
return -EINVAL;
}
TRACE_2L(TRC_VMEXIT_CR_ACCESS,
VM_EXIT_CR_ACCESS_ACCESS_TYPE
(vcpu->arch_vcpu.exit_qualification),
VM_EXIT_CR_ACCESS_CR_NUM
(vcpu->arch_vcpu.exit_qualification));
return 0;
}
#if 0
/*
* VMX_PROCBASED_CTLS_INVLPG is not enabled in the VM-execution
* control therefore we don't need it's handler.
*
* INVLPG: this instruction Invalidates any translation lookaside buffer
*/
int invlpg_handler(__unused struct vcpu *vcpu)
{
pr_fatal("INVLPG executed");
return 0;
}
/*
* XSETBV instruction set's the XCR0 that is used to tell for which components
* states can be saved on a context switch using xsave.
*
* We don't handle this right now because we are on a platform that does not
* support XSAVE/XRSTORE feature as reflected by the instruction CPUID.
*
* to make sure this never get called until we support it we can prevent the
* reading of this bit in CPUID VMEXIT.
*
* Linux checks this in CPUID: cpufeature.h: #define cpu_has_xsave
*/
static int xsetbv_instr_handler(__unused struct vcpu *vcpu)
{
ASSERT("Not Supported" == 0, "XSETBV executed");
return 0;
}
#endif
static int rdtsc_handler(struct vcpu *vcpu)
{
uint64_t host_tsc, guest_tsc, tsc_offset;
uint32_t id;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
/* Read the host TSC value */
CPU_RDTSCP_EXECUTE(&host_tsc, &id);
/* Get the guest TSC offset value from VMCS */
tsc_offset =
exec_vmread64(VMX_TSC_OFFSET_FULL);
/* Update the guest TSC value by following: TSC_guest = TSC_host +
* TSC_guest_Offset
*/
guest_tsc = host_tsc + tsc_offset;
/* Return the TSC_guest in rax:rdx */
cur_context->guest_cpu_regs.regs.rax = (uint32_t) guest_tsc;
cur_context->guest_cpu_regs.regs.rdx = (uint32_t) (guest_tsc >> 32);
TRACE_2L(TRC_VMEXIT_RDTSC, host_tsc, tsc_offset);
return 0;
}
static int rdtscp_handler(struct vcpu *vcpu)
{
uint64_t host_tsc, guest_tsc, tsc_offset;
uint32_t id;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
/* Read the host TSC value */
CPU_RDTSCP_EXECUTE(&host_tsc, &id);
/* Get the guest TSC offset value from VMCS */
tsc_offset =
exec_vmread64(VMX_TSC_OFFSET_FULL);
/* Update the guest TSC value by following: * TSC_guest = TSC_host +
* TSC_guest_Offset
*/
guest_tsc = host_tsc + tsc_offset;
/* Return the TSC_guest in rax:rdx and IA32_TSC_AUX in rcx */
cur_context->guest_cpu_regs.regs.rax = (uint32_t) guest_tsc;
cur_context->guest_cpu_regs.regs.rdx = (uint32_t) (guest_tsc >> 32);
cur_context->guest_cpu_regs.regs.rcx = vcpu->arch_vcpu.msr_tsc_aux;
TRACE_2L(TRC_VMEXIT_RDTSCP, guest_tsc, vcpu->arch_vcpu.msr_tsc_aux);
return 0;
}

1346
hypervisor/arch/x86/vmx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <vmx.h>
#include <msr.h>
#include <guest.h>
#include <vcpu.h>
#include <cpu.h>
#include <types.h>
.text
/*int vmx_vmrun(struct run_context *context, int launch, int ibrs_type) */
.code64
.align 8
.global vmx_vmrun
vmx_vmrun:
/* Save all host GPRs that must be preserved across function calls
per System V ABI */
push %rdx
push %rbx
push %rbp
push %r12
push %r13
push %r14
push %r15
/* Save RDI on top of host stack for easy access to VCPU pointer
on return from guest context */
push %rdi
/* rdx = ibrs_type */
/* if ibrs_type != IBRS_NONE, means IBRS feature is supported,
* restore MSR SPEC_CTRL to guest
*/
cmp $IBRS_NONE,%rdx
je next
movl $MSR_IA32_SPEC_CTRL,%ecx
mov VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET(%rdi),%rax
movl $0,%edx
wrmsr
next:
/* Load VMCS_HOST_RSP_FIELD field value */
mov $VMX_HOST_RSP,%rdx
/* Write the current stack pointer to the VMCS_HOST_RSP_FIELD */
vmwrite %rsp,%rdx
/* Error occurred - handle error */
jbe vm_eval_error
/* Compare the launch flag to see if launching (1) or resuming (0) */
cmp $VM_LAUNCH, %rsi
mov VMX_MACHINE_T_GUEST_CR2_OFFSET(%rdi),%rax
mov %rax,%cr2
mov VMX_MACHINE_T_GUEST_RAX_OFFSET(%rdi),%rax
mov VMX_MACHINE_T_GUEST_RBX_OFFSET(%rdi),%rbx
mov VMX_MACHINE_T_GUEST_RCX_OFFSET(%rdi),%rcx
mov VMX_MACHINE_T_GUEST_RDX_OFFSET(%rdi),%rdx
mov VMX_MACHINE_T_GUEST_RBP_OFFSET(%rdi),%rbp
mov VMX_MACHINE_T_GUEST_RSI_OFFSET(%rdi),%rsi
mov VMX_MACHINE_T_GUEST_R8_OFFSET(%rdi),%r8
mov VMX_MACHINE_T_GUEST_R9_OFFSET(%rdi),%r9
mov VMX_MACHINE_T_GUEST_R10_OFFSET(%rdi),%r10
mov VMX_MACHINE_T_GUEST_R11_OFFSET(%rdi),%r11
mov VMX_MACHINE_T_GUEST_R12_OFFSET(%rdi),%r12
mov VMX_MACHINE_T_GUEST_R13_OFFSET(%rdi),%r13
mov VMX_MACHINE_T_GUEST_R14_OFFSET(%rdi),%r14
mov VMX_MACHINE_T_GUEST_R15_OFFSET(%rdi),%r15
mov VMX_MACHINE_T_GUEST_RDI_OFFSET(%rdi),%rdi
/* Execute appropriate VMX instruction */
je vm_launch
/* Execute a VM resume */
vmresume
vm_launch:
/* Execute a VM launch */
vmlaunch
.global vm_exit
vm_exit:
/* Get VCPU data structure pointer from top of host stack and
save guest RDI in its place */
xchg 0(%rsp),%rdi
/* Save current GPRs to guest state area */
mov %rax,VMX_MACHINE_T_GUEST_RAX_OFFSET(%rdi)
mov %cr2,%rax
mov %rax,VMX_MACHINE_T_GUEST_CR2_OFFSET(%rdi)
mov %rbx,VMX_MACHINE_T_GUEST_RBX_OFFSET(%rdi)
mov %rcx,VMX_MACHINE_T_GUEST_RCX_OFFSET(%rdi)
mov %rdx,VMX_MACHINE_T_GUEST_RDX_OFFSET(%rdi)
mov %rbp,VMX_MACHINE_T_GUEST_RBP_OFFSET(%rdi)
mov %rsi,VMX_MACHINE_T_GUEST_RSI_OFFSET(%rdi)
mov %r8,VMX_MACHINE_T_GUEST_R8_OFFSET(%rdi)
mov %r9,VMX_MACHINE_T_GUEST_R9_OFFSET(%rdi)
mov %r10,VMX_MACHINE_T_GUEST_R10_OFFSET(%rdi)
mov %r11,VMX_MACHINE_T_GUEST_R11_OFFSET(%rdi)
mov %r12,VMX_MACHINE_T_GUEST_R12_OFFSET(%rdi)
mov %r13,VMX_MACHINE_T_GUEST_R13_OFFSET(%rdi)
mov %r14,VMX_MACHINE_T_GUEST_R14_OFFSET(%rdi)
mov %r15,VMX_MACHINE_T_GUEST_R15_OFFSET(%rdi)
/* Load guest RDI off host stack and into RDX */
mov 0(%rsp),%rdx
/* Save guest RDI to guest state area */
mov %rdx,VMX_MACHINE_T_GUEST_RDI_OFFSET(%rdi)
/* Save RDI to RSI for later SPEC_CTRL save*/
mov %rdi,%rsi
vm_eval_error:
/* Restore host GPR System V required registers */
pop %rdi
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbp
pop %rbx
pop %rdx
/* Check vm fail, refer to 64-ia32 spec section 26.2 in volume#3 */
mov $VM_FAIL,%rax
jc vm_return
jz vm_return
/* Clear host registers to prevent speculative use */
xor %rcx,%rcx
xor %r8,%r8
xor %r9,%r9
xor %r10,%r10
xor %r11,%r11
/* rdx = ibrs_type */
/* IBRS_NONE: no ibrs setting, just flush rsb
* IBRS_RAW: set IBRS then flush rsb
* IBRS_OPT: set STIBP & IBPB then flush rsb
*/
cmp $IBRS_NONE,%rdx
je stuff_rsb
cmp $IBRS_OPT,%rdx
je ibrs_opt
/* Save guest MSR SPEC_CTRL, low 32 bit is enough */
movl $MSR_IA32_SPEC_CTRL,%ecx
rdmsr
mov %rax,VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET(%rsi)
movl $SPEC_ENABLE_IBRS,%eax
movl $0,%edx
wrmsr
jmp stuff_rsb
ibrs_opt:
movl $MSR_IA32_PRED_CMD,%ecx
movl $PRED_SET_IBPB,%eax
movl $0,%edx
wrmsr
/* Save guest MSR SPEC_CTRL, low 32 bit is enough */
movl $MSR_IA32_SPEC_CTRL,%ecx
rdmsr
mov %rax,VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET(%rsi)
movl $SPEC_ENABLE_STIBP,%eax
movl $0,%edx
wrmsr
/* stuff rsb by 32 CALLs, make sure no any "ret" is executed before this
* stuffing rsb, otherwise, someone may insert some code before this for
* future update.
*/
stuff_rsb:
/* stuff 32 RSB, rax = 32/2 */
mov $16,%rax
.align 16
3:
call 4f
33:
pause
jmp 33b
.align 16
4:
call 5f
44:
pause
jmp 44b
.align 16
5: dec %rax
jnz 3b
/* stuff 32 RSB, rsp += 8*32 */
add $(8*32),%rsp
mov $VM_SUCCESS,%rax
vm_return:
/* Return to caller */
ret

1162
hypervisor/arch/x86/vtd.c Normal file

File diff suppressed because it is too large Load Diff

263
hypervisor/boot/acpi.c Normal file
View File

@@ -0,0 +1,263 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
* Copyright (c) 2018 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <hypervisor.h>
#include <hv_lib.h>
#include <cpu.h>
#include <acrn_common.h>
#include <bsp_extern.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "acpi.h"
#define ACPI_SIG_RSDP "RSD PTR " /* Root System Description Ptr */
#define ACPI_OEM_ID_SIZE 6
#define ACPI_SIG_MADT "APIC" /* Multiple APIC Description Table */
#define ACPI_SIG_DMAR "DMAR"
#define RSDP_CHECKSUM_LENGTH 20
#define ACPI_NAME_SIZE 4
#define ACPI_MADT_TYPE_LOCAL_APIC 0
#define ACPI_MADT_ENABLED 1
#define ACPI_OEM_TABLE_ID_SIZE 8
struct acpi_table_rsdp {
/* ACPI signature, contains "RSD PTR " */
char signature[8];
/* ACPI 1.0 checksum */
uint8_t checksum;
/* OEM identification */
char oem_id[ACPI_OEM_ID_SIZE];
/* Must be (0) for ACPI 1.0 or (2) for ACPI 2.0+ */
uint8_t revision;
/* 32-bit physical address of the RSDT */
uint32_t rsdt_physical_address;
/* Table length in bytes, including header (ACPI 2.0+) */
uint32_t length;
/* 64-bit physical address of the XSDT (ACPI 2.0+) */
uint64_t xsdt_physical_address;
/* Checksum of entire table (ACPI 2.0+) */
uint8_t extended_checksum;
/* Reserved, must be zero */
uint8_t reserved[3];
};
struct acpi_table_rsdt {
/* Common ACPI table header */
struct acpi_table_header header;
/* Array of pointers to ACPI tables */
uint32_t table_offset_entry[1];
} __packed;
struct acpi_table_xsdt {
/* Common ACPI table header */
struct acpi_table_header header;
/* Array of pointers to ACPI tables */
uint64_t table_offset_entry[1];
} __packed;
struct acpi_subtable_header {
uint8_t type;
uint8_t length;
};
struct acpi_table_madt {
/* Common ACPI table header */
struct acpi_table_header header;
/* Physical address of local APIC */
uint32_t address;
uint32_t flags;
};
struct acpi_madt_local_apic {
struct acpi_subtable_header header;
/* ACPI processor id */
uint8_t processor_id;
/* Processor's local APIC id */
uint8_t id;
uint32_t lapic_flags;
};
static void *global_rsdp;
static uint64_t madt;
static struct acpi_table_rsdp*
biosacpi_search_rsdp(char *base, int length)
{
struct acpi_table_rsdp *rsdp;
uint8_t *cp, sum;
int ofs, idx;
/* search on 16-byte boundaries */
for (ofs = 0; ofs < length; ofs += 16) {
rsdp = (struct acpi_table_rsdp *)(base + ofs);
/* compare signature, validate checksum */
if (!strncmp(rsdp->signature, ACPI_SIG_RSDP,
strnlen_s(ACPI_SIG_RSDP, 8))) {
cp = (uint8_t *)rsdp;
sum = 0;
for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++)
sum += *(cp + idx);
if (sum != 0)
continue;
return rsdp;
}
}
return NULL;
}
static void *get_rsdp(void)
{
struct acpi_table_rsdp *rsdp = NULL;
uint16_t *addr;
/* EBDA is addressed by the 16 bit pointer at 0x40E */
addr = (uint16_t *)0x40E;
rsdp = biosacpi_search_rsdp((char *)((uint64_t)(*addr << 4)), 0x400);
if (rsdp != NULL)
return rsdp;
/* Check the upper memory BIOS space, 0xe0000 - 0xfffff. */
rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000);
if (rsdp != NULL)
return rsdp;
return rsdp;
}
static int
probe_table(uint64_t address, const char *sig)
{
struct acpi_table_header *table = (struct acpi_table_header *)address;
if (strncmp(table->signature, sig, ACPI_NAME_SIZE) != 0)
return 0;
return 1;
}
uint64_t get_acpi_tbl(char *sig)
{
struct acpi_table_rsdp *rsdp;
struct acpi_table_rsdt *rsdt;
struct acpi_table_xsdt *xsdt;
uint64_t addr = 0;
int i, count;
rsdp = (struct acpi_table_rsdp *)global_rsdp;
if (rsdp->revision >= 2 && rsdp->xsdt_physical_address) {
/*
* AcpiOsGetRootPointer only verifies the checksum for
* the version 1.0 portion of the RSDP. Version 2.0 has
* an additional checksum that we verify first.
*/
xsdt = (struct acpi_table_xsdt *)(rsdp->xsdt_physical_address);
count = (xsdt->header.length -
sizeof(struct acpi_table_header)) /
sizeof(uint64_t);
for (i = 0; i < count; i++) {
if (probe_table(xsdt->table_offset_entry[i], sig)) {
addr = xsdt->table_offset_entry[i];
break;
}
}
} else {
/* Root table is an RSDT (32-bit physical addresses) */
rsdt = (struct acpi_table_rsdt *)
((void *)(uint64_t)rsdp->rsdt_physical_address);
count = (rsdt->header.length -
sizeof(struct acpi_table_header)) /
sizeof(uint32_t);
for (i = 0; i < count; i++) {
if (probe_table(rsdt->table_offset_entry[i], sig)) {
addr = rsdt->table_offset_entry[i];
break;
}
}
}
return addr;
}
static int _parse_madt(uint64_t madt, uint8_t *lapic_id_base)
{
int pcpu_id = 0;
struct acpi_madt_local_apic *processor;
struct acpi_table_madt *madt_ptr;
void *first;
void *end;
struct acpi_subtable_header *entry;
madt_ptr = (struct acpi_table_madt *)madt;
first = madt_ptr + 1;
end = (char *)madt_ptr + madt_ptr->header.length;
for (entry = first; (void *)entry < end; ) {
if (entry->length < sizeof(struct acpi_subtable_header))
continue;
if (entry->type == ACPI_MADT_TYPE_LOCAL_APIC) {
processor = (struct acpi_madt_local_apic *)entry;
if (processor->lapic_flags & ACPI_MADT_ENABLED) {
*lapic_id_base++ = processor->id;
pcpu_id++;
}
}
entry = (struct acpi_subtable_header *)
(((uint64_t)entry) + entry->length);
}
return pcpu_id;
}
/* The lapic_id info gotten from madt will be returned in lapic_id_base */
int parse_madt(uint8_t *lapic_id_base)
{
global_rsdp = get_rsdp();
ASSERT(global_rsdp != NULL, "fail to get rsdp");
madt = get_acpi_tbl(ACPI_SIG_MADT);
ASSERT(madt != 0, "fail to get madt");
return _parse_madt(madt, lapic_id_base);
}
uint64_t get_dmar_table(void)
{
return get_acpi_tbl(ACPI_SIG_DMAR);
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 "bsp_cfg.h"
#ifdef CONFIG_DMAR_PARSE_ENABLED
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "vtd.h"
#include "acpi.h"
#define PCI_CONFIG_ADDRESS 0xcf8
#define PCI_CONFIG_DATA 0xcfc
#define PCI_CONFIG_ACCESS_EN 0x80000000
enum acpi_dmar_type {
ACPI_DMAR_TYPE_HARDWARE_UNIT = 0,
ACPI_DMAR_TYPE_RESERVED_MEMORY = 1,
ACPI_DMAR_TYPE_ROOT_ATS = 2,
ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3,
ACPI_DMAR_TYPE_NAMESPACE = 4,
ACPI_DMAR_TYPE_RESERVED = 5
};
/* Values for entry_type in ACPI_DMAR_DEVICE_SCOPE - device types */
enum acpi_dmar_scope_type {
ACPI_DMAR_SCOPE_TYPE_NOT_USED = 0,
ACPI_DMAR_SCOPE_TYPE_ENDPOINT = 1,
ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2,
ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3,
ACPI_DMAR_SCOPE_TYPE_HPET = 4,
ACPI_DMAR_SCOPE_TYPE_NAMESPACE = 5,
ACPI_DMAR_SCOPE_TYPE_RESERVED = 6 /* 6 and greater are reserved */
};
struct acpi_table_dmar {
/* Common ACPI table header */
struct acpi_table_header header;
/* Host address Width */
uint8_t width;
uint8_t flags;
uint8_t reserved[10];
};
/* DMAR subtable header */
struct acpi_dmar_header {
uint16_t type;
uint16_t length;
};
struct acpi_dmar_hardware_unit {
struct acpi_dmar_header header;
uint8_t flags;
uint8_t reserved;
uint16_t segment;
/* register base address */
uint64_t address;
};
struct find_iter_args {
int i;
struct acpi_dmar_hardware_unit *res;
};
struct acpi_dmar_pci_path {
uint8_t device;
uint8_t function;
};
struct acpi_dmar_device_scope {
uint8_t entry_type;
uint8_t length;
uint16_t reserved;
uint8_t enumeration_id;
uint8_t bus;
};
typedef int (*dmar_iter_t)(struct acpi_dmar_header*, void*);
static struct dmar_info dmar_info_parsed;
static int dmar_unit_cnt;
static void
dmar_iterate_tbl(dmar_iter_t iter, void *arg)
{
struct acpi_table_dmar *dmar_tbl;
struct acpi_dmar_header *dmar_header;
char *ptr, *ptr_end;
dmar_tbl = (struct acpi_table_dmar *)get_dmar_table();
ASSERT(dmar_tbl != NULL, "");
ptr = (char *)dmar_tbl + sizeof(*dmar_tbl);
ptr_end = (char *)dmar_tbl + dmar_tbl->header.length;
for (;;) {
if (ptr >= ptr_end)
break;
dmar_header = (struct acpi_dmar_header *)ptr;
if (dmar_header->length <= 0) {
pr_err("drhd: corrupted DMAR table, l %d\n",
dmar_header->length);
break;
}
ptr += dmar_header->length;
if (!iter(dmar_header, arg))
break;
}
}
static int
drhd_count_iter(struct acpi_dmar_header *dmar_header, __unused void *arg)
{
if (dmar_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT)
dmar_unit_cnt++;
return 1;
}
static int
drhd_find_iter(struct acpi_dmar_header *dmar_header, void *arg)
{
struct find_iter_args *args;
if (dmar_header->type != ACPI_DMAR_TYPE_HARDWARE_UNIT)
return 1;
args = arg;
if (args->i == 0) {
args->res = (struct acpi_dmar_hardware_unit *)dmar_header;
return 0;
}
args->i--;
return 1;
}
static struct acpi_dmar_hardware_unit *
drhd_find_by_index(int idx)
{
struct find_iter_args args;
args.i = idx;
args.res = NULL;
dmar_iterate_tbl(drhd_find_iter, &args);
return args.res;
}
static uint8_t get_secondary_bus(uint8_t bus, uint8_t dev, uint8_t func)
{
uint32_t data;
io_write_long(PCI_CONFIG_ACCESS_EN | (bus << 16) | (dev << 11) |
(func << 8) | 0x18, PCI_CONFIG_ADDRESS);
data = io_read_long(PCI_CONFIG_DATA);
return (data >> 8) & 0xff;
}
static uint16_t
dmar_path_bdf(int path_len, int busno,
const struct acpi_dmar_pci_path *path)
{
int i;
uint8_t bus;
uint8_t dev;
uint8_t fun;
bus = (uint8_t)busno;
dev = path->device;
fun = path->function;
for (i = 1; i < path_len; i++) {
bus = get_secondary_bus(bus, dev, fun);
dev = path[i].device;
fun = path[i].function;
}
return (bus << 8 | DEVFUN(dev, fun));
}
static int
handle_dmar_devscope(struct dmar_dev_scope *dev_scope,
void *addr, int remaining)
{
int path_len;
uint16_t bdf;
struct acpi_dmar_pci_path *path;
struct acpi_dmar_device_scope *apci_devscope = addr;
if (remaining < (int)sizeof(struct acpi_dmar_device_scope))
return -1;
if (remaining < apci_devscope->length)
return -1;
path = (struct acpi_dmar_pci_path *)(apci_devscope + 1);
path_len = (apci_devscope->length -
sizeof(struct acpi_dmar_device_scope)) /
sizeof(struct acpi_dmar_pci_path);
bdf = dmar_path_bdf(path_len, apci_devscope->bus, path);
dev_scope->bus = (bdf >> 8) & 0xff;
dev_scope->devfun = bdf & 0xff;
return apci_devscope->length;
}
static uint32_t
get_drhd_dev_scope_cnt(struct acpi_dmar_hardware_unit *drhd)
{
struct acpi_dmar_device_scope *scope;
char *start;
char *end;
uint32_t count = 0;
start = (char *)drhd + sizeof(struct acpi_dmar_hardware_unit);
end = (char *)drhd + drhd->header.length;
while (start < end) {
scope = (struct acpi_dmar_device_scope *)start;
if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT ||
scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE ||
scope->entry_type == ACPI_DMAR_SCOPE_TYPE_NAMESPACE)
count++;
start += scope->length;
}
return count;
}
static int
handle_one_drhd(struct acpi_dmar_hardware_unit *acpi_drhd,
struct dmar_drhd *drhd)
{
struct dmar_dev_scope *dev_scope;
struct acpi_dmar_device_scope *ads;
int remaining, consumed;
char *cp;
uint32_t dev_count;
drhd->segment = acpi_drhd->segment;
drhd->flags = acpi_drhd->flags;
drhd->reg_base_addr = acpi_drhd->address;
if (drhd->flags & DRHD_FLAG_INCLUDE_PCI_ALL_MASK) {
drhd->dev_cnt = 0;
drhd->devices = NULL;
return 0;
}
dev_count = get_drhd_dev_scope_cnt(acpi_drhd);
drhd->dev_cnt = dev_count;
if (dev_count) {
drhd->devices =
calloc(dev_count, sizeof(struct dmar_dev_scope));
ASSERT(drhd->devices, "");
} else {
drhd->devices = NULL;
return 0;
}
remaining = acpi_drhd->header.length -
sizeof(struct acpi_dmar_hardware_unit);
dev_scope = drhd->devices;
while (remaining > 0) {
cp = (char *)acpi_drhd + acpi_drhd->header.length - remaining;
consumed = handle_dmar_devscope(dev_scope, cp, remaining);
if (((drhd->segment << 16) |
(dev_scope->bus << 8) |
dev_scope->devfun) == CONFIG_GPU_SBDF) {
ASSERT(dev_count == 1, "no dedicated iommu for gpu");
drhd->ignore = true;
}
if (consumed <= 0)
break;
remaining -= consumed;
/* skip IOAPIC & HPET */
ads = (struct acpi_dmar_device_scope *)cp;
if (ads->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC &&
ads->entry_type != ACPI_DMAR_SCOPE_TYPE_HPET)
dev_scope++;
else
pr_dbg("drhd: skip dev_scope type %d",
ads->entry_type);
}
return 0;
}
int parse_dmar_table(void)
{
int i;
struct acpi_dmar_hardware_unit *acpi_drhd;
/* find out how many dmar units */
dmar_iterate_tbl(drhd_count_iter, NULL);
/* alloc memory for dmar uint */
dmar_info_parsed.drhd_units =
calloc(dmar_unit_cnt, sizeof(struct dmar_drhd));
ASSERT(dmar_info_parsed.drhd_units, "");
dmar_info_parsed.drhd_count = dmar_unit_cnt;
for (i = 0; i < dmar_unit_cnt; i++) {
acpi_drhd = drhd_find_by_index(i);
if (acpi_drhd == NULL)
continue;
if (acpi_drhd->flags & DRHD_FLAG_INCLUDE_PCI_ALL_MASK)
ASSERT((i+1) == dmar_unit_cnt,
"drhd with flags set should be the last one");
handle_one_drhd(acpi_drhd, &dmar_info_parsed.drhd_units[i]);
}
return 0;
}
struct dmar_info *get_dmar_info(void)
{
parse_dmar_table();
return &dmar_info_parsed;
}
#endif

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 ACPI_H
#define ACPI_H
struct acpi_table_header {
/* ASCII table signature */
char signature[4];
/* Length of table in bytes, including this header */
uint32_t length;
/* ACPI Specification minor version number */
uint8_t revision;
/* To make sum of entire table == 0 */
uint8_t checksum;
/* ASCII OEM identification */
char oem_id[6];
/* ASCII OEM table identification */
char oem_table_id[8];
/* OEM revision number */
uint32_t oem_revision;
/* ASCII ASL compiler vendor ID */
char asl_compiler_id[4];
/* ASL compiler version */
uint32_t asl_compiler_revision;
};
int parse_madt(uint8_t *lapic_id_base);
uint64_t get_dmar_table(void);
#endif /* !ACPI_H */

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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.
*/
/************************************************************************
*
* FILE NAME
*
* bsp_extern.h
*
* DESCRIPTION
*
* This file defines the generic BSP interface
*
************************************************************************/
#ifndef BSP_EXTERN_H
#define BSP_EXTERN_H
#define UOS_DEFAULT_START_ADDR (0x100000000)
/**********************************/
/* EXTERNAL VARIABLES */
/**********************************/
/* BSP Interfaces */
void init_bsp(void);
/* External Interfaces */
struct _vm_description_array;
const struct _vm_description_array *get_vm_desc_base(void);
#endif /* BSP_EXTERN_H */

View File

@@ -0,0 +1,94 @@
#include "bsp_cfg.h"
ENTRY(cpu_primary_start_32)
MEMORY
{
/* Low 1MB of memory for secondary processor start-up */
lowram : ORIGIN = CONFIG_LOW_RAM_START, LENGTH = CONFIG_LOW_RAM_SIZE
/* 32 MBytes of RAM for HV */
ram : ORIGIN = CONFIG_RAM_START, LENGTH = CONFIG_RAM_SIZE
}
SECTIONS
{
.boot :
{
_ld_ram_start = . ;
KEEP(*(multiboot_header)) ;
} > ram
.entry :
{
KEEP(*(entry)) ;
} > ram
.text :
{
*(.text .text*) ;
*(.gnu.linkonce.t*)
*(.note.gnu.build-id)
} > ram
.rodata :
{
*(.rodata*) ;
} > ram
_ld_cpu_secondary_reset_load = .;
.cpu_secondary : AT (_ld_cpu_secondary_reset_load)
{
_ld_cpu_secondary_reset_start = .;
*(.cpu_secondary_reset);
. = ALIGN(4);
_ld_cpu_secondary_reset_end = .;
} > lowram
_ld_cpu_secondary_reset_size = _ld_cpu_secondary_reset_end - _ld_cpu_secondary_reset_start;
.data (_ld_cpu_secondary_reset_load + _ld_cpu_secondary_reset_size):
{
*(.data) ;
*(.data*) ;
*(.sdata)
*(.gnu.linkonce.d*)
} > ram
.bss_noinit (NOLOAD):
{
. = ALIGN(4) ;
*(.bss_noinit) ;
*(.bss_noinit*) ;
. = ALIGN(4) ;
} > ram
.bss (NOLOAD):
{
. = ALIGN(4) ;
_ld_bss_start = . ;
*(.bss) ;
*(.bss*) ;
*(COMMON) ;
. = ALIGN(4) ;
_ld_bss_end = . ;
} > ram
.discard (NOLOAD):
{
. = ALIGN(4096) ;
_ld_cpu_data_start = .;
*(.cpu_data) ;
. = ALIGN(4096) ;
_ld_cpu_data_end = .;
} > ram
_ld_ram_size = LENGTH(ram) ;
_ld_ram_end = _ld_ram_size + _ld_ram_start ;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 BSP_CFG_H
#define BSP_CFG_H
#define NR_IOAPICS 1
#define STACK_SIZE 8192
#define LOG_BUF_SIZE 0x100000
#define LOG_DESTINATION 3
#define CPU_UP_TIMEOUT 100
#define CONFIG_SERIAL_MMIO_BASE 0xfc000000
#define MALLOC_ALIGN 16
#define NUM_ALLOC_PAGES 4096
#define HEAP_SIZE 0x100000
#define CONSOLE_LOGLEVEL_DEFAULT 2
#define MEM_LOGLEVEL_DEFAULT 4
#define CONFIG_LOW_RAM_START 0x00001000
#define CONFIG_LOW_RAM_SIZE 0x000CF000
#define CONFIG_RAM_START 0x6E000000
#define CONFIG_RAM_SIZE 0x02000000 /* 32M */
#define CONFIG_RETPOLINE
#endif /* BSP_CFG_H */

94
hypervisor/bsp/sbl/sbl.c Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <vtd.h>
/* IOAPIC id */
#define SBL_IOAPIC_ID 8
/* IOAPIC base address */
#define SBL_IOAPIC_ADDR 0xfec00000
/* IOAPIC range size */
#define SBL_IOAPIC_SIZE 0x100000
/* Local APIC base address */
#define SBL_LAPIC_ADDR 0xfee00000
/* Local APIC range size */
#define SBL_LAPIC_SIZE 0x100000
/* Number of PCI IRQ assignments */
#define SBL_PCI_IRQ_ASSIGNMENT_NUM 28
#ifndef CONFIG_DMAR_PARSE_ENABLED
static struct dmar_dev_scope default_drhd_unit_dev_scope0[] = {
{ .bus = 0, .devfun = DEVFUN(0x2, 0), },
};
static struct dmar_drhd drhd_info_array[] = {
{
.dev_cnt = 1,
.segment = 0,
.flags = 0,
.reg_base_addr = 0xFED64000,
/* Ignore the iommu for intel graphic device since GVT-g needs
* vtd disabled for gpu
*/
.ignore = true,
.devices = default_drhd_unit_dev_scope0,
},
{
/* No need to specify devices since
* DRHD_FLAG_INCLUDE_PCI_ALL_MASK set
*/
.dev_cnt = 0,
.segment = 0,
.flags = DRHD_FLAG_INCLUDE_PCI_ALL_MASK,
.reg_base_addr = 0xFED65000,
.ignore = false,
.devices = NULL,
},
};
static struct dmar_info sbl_dmar_info = {
.drhd_count = 2,
.drhd_units = drhd_info_array,
};
struct dmar_info *get_dmar_info(void)
{
return &sbl_dmar_info;
}
#endif
void init_bsp(void)
{
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#define NUM_USER_VMS 2
/* Number of CPUs in VM0 */
#define VM0_NUM_CPUS 1
/* Logical CPU IDs assigned to VM0 */
int VM0_CPUS[VM0_NUM_CPUS] = {0};
/* Number of CPUs in VM1 */
#define VM1_NUM_CPUS 2
/* Logical CPU IDs assigned with VM1 */
int VM1_CPUS[VM1_NUM_CPUS] = {3, 1};
const struct vm_description_array vm_desc = {
/* Number of user virtual machines */
.num_vm_desc = NUM_USER_VMS,
/* Virtual Machine descriptions */
.vm_desc_array = {
{
/* Internal variable, MUSTBE init to -1 */
.vm_attr_name = "vm_0",
.vm_hw_num_cores = VM0_NUM_CPUS,
.vm_hw_logical_core_ids = &VM0_CPUS[0],
.vm_state_info_privilege = VM_PRIVILEGE_LEVEL_HIGH,
.vm_created = false,
},
}
};
const struct vm_description_array *get_vm_desc_base(void)
{
return &vm_desc;
}

View File

@@ -0,0 +1,3 @@
title ACRN OS
linux /EFI/org.clearlinux/acrn.efi
options sos=bzImage pci_devices_ignore=(0:18:2) noxsave maxcpus=1 console=tty0 console=ttyS0 i915.nuclear_pageflip=1 root=/dev/sda3 rw rootwait clocksource=hpet ignore_loglevel no_timer_check consoleblank=0 i915.tsd_init=7 i915.tsd_delay=2000 i915.avail_planes_per_pipe=0x00000F i915.domain_plane_owners=0x011111110000 i915.enable_guc_loading=0 i915.enable_guc_submission=0 i915.enable_preemption=1 i915.context_priority_mode=2 i915.enable_gvt=1 hvlog=2M@0x1FE00000 cma=2560M@0x100000000-0

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <acrn_hv_defs.h>
#include <hv_debug.h>
#include <multiboot.h>
#define MAX_PORT 0x10000 /* port 0 - 64K */
#define DEFAULT_UART_PORT 0x3F8
#define ACRN_DBG_PARSE 6
#define MAX_CMD_LEN 64
static const char * const cmd_list[] = {
"uart=disabled", /* to disable uart */
"uart=port@", /* like uart=port@0x3F8 */
"uart=mmio@", /*like: uart=mmio@0xFC000000 */
};
enum IDX_CMD {
IDX_DISABLE_UART,
IDX_PORT_UART,
IDX_MMIO_UART,
IDX_MAX_CMD,
};
static void handle_cmd(const char *cmd, int len)
{
int i;
for (i = 0; i < IDX_MAX_CMD; i++) {
int tmp = strnlen_s(cmd_list[i], MAX_CMD_LEN);
/*cmd prefix should be same with one in cmd_list */
if (len < tmp)
continue;
if (strncmp(cmd_list[i], cmd, tmp) != 0)
continue;
if (i == IDX_DISABLE_UART) {
/* set uart disabled*/
uart16550_set_property(0, 0, 0);
} else if ((i == IDX_PORT_UART) || (i == IDX_MMIO_UART)) {
uint64_t addr = strtoul(cmd + tmp, NULL, 16);
dev_dbg(ACRN_DBG_PARSE, "uart addr=0x%llx", addr);
if (i == IDX_PORT_UART) {
if (addr > MAX_PORT)
addr = DEFAULT_UART_PORT;
uart16550_set_property(1, 1, addr);
} else {
uart16550_set_property(1, 0, addr);
}
}
}
}
int parse_hv_cmdline(void)
{
const char *start;
const char *end;
struct multiboot_info *mbi = NULL;
if (boot_regs[0] != MULTIBOOT_INFO_MAGIC) {
ASSERT(0, "no multiboot info found");
return -EINVAL;
}
mbi = (struct multiboot_info *)((uint64_t)boot_regs[1]);
dev_dbg(ACRN_DBG_PARSE, "Multiboot detected, flag=0x%x", mbi->mi_flags);
if (!(mbi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)) {
dev_dbg(ACRN_DBG_PARSE, "no hv cmdline!");
return -EINVAL;
}
start = (char *)(uint64_t)mbi->mi_cmdline;
dev_dbg(ACRN_DBG_PARSE, "hv cmdline: %s", start);
do {
while (*start == ' ')
start++;
end = start + 1;
while (*end != ' ' && *end)
end++;
handle_cmd(start, end - start);
start = end + 1;
} while (*end && *start);
return 0;
}

View File

@@ -0,0 +1,105 @@
#
# Copyright (c) 2011, 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:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Intel Corporation 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 OWNER 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.
#
RELEASE:=0
HV_OBJDIR:=build
HV_FILE:=acrn
EFI_OBJDIR:=$(HV_OBJDIR)/bsp/uefi/efi
C_SRCS = boot.c pe.c malloc.c
ACRN_OBJS := $(patsubst %.c,$(EFI_OBJDIR)/%.o,$(C_SRCS))
OBJCOPY=objcopy
HOST = $(shell $(CC) -dumpmachine | sed "s/\(-\).*$$//")
ARCH := $(shell $(CC) -dumpmachine | sed "s/\(-\).*$$//")
ifeq ($(ARCH),x86_64)
LIBDIR := $(shell if [ -d /usr/lib64 ]; then echo /usr/lib64; \
else if [ -d /usr/lib ]; then echo /usr/lib; fi ; fi;)
FORMAT=efi-app-x86-64
else
ARCH=ia32
LIBDIR=/usr/lib32
FORMAT=efi-app-ia32
endif
INCDIR := /usr/include
# gnuefi sometimes installs these under a gnuefi/ directory, and sometimes not
CRT0 := $(LIBDIR)/crt0-efi-$(ARCH).o
LDSCRIPT := $(LIBDIR)/elf_$(ARCH)_efi.lds
CFLAGS=-I. -I.. -I$(INCDIR)/efi -I$(INCDIR)/efi/$(ARCH) \
-DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \
-Wall -I../fs/ -D$(ARCH)
ifeq ($(ARCH),ia32)
ifeq ($(HOST),x86_64)
CFLAGS += -m32
endif
endif
ifeq ($(ARCH),x86_64)
CFLAGS += -mno-red-zone
endif
LDFLAGS=-T $(LDSCRIPT) -Bsymbolic -shared -nostdlib -znocombreloc \
-L$(LIBDIR) $(CRT0)
EFIBIN=$(HV_OBJDIR)/$(HV_FILE).efi
BOOT=$(EFI_OBJDIR)/boot.efi
all: $(EFIBIN)
$(OBJCOPY) --add-section .hv="$(HV_OBJDIR)/$(HV_FILE).bin" --change-section-vma .hv=0x6e000 --set-section-flags .hv=alloc,data,contents,load --section-alignment 0x1000 $(EFI_OBJDIR)/boot.efi $(EFIBIN)
install: $(EFIBIN)
install -D $(EFIBIN) $(DESTDIR)/usr/share/$(HV_FILE).efi
$(EFIBIN): $(BOOT)
$(EFI_OBJDIR)/boot.efi: $(EFI_OBJDIR)/boot.so
$(EFI_OBJDIR)/boot.so: $(ACRN_OBJS) $(FS)
$(LD) $(LDFLAGS) -o $@ $^ -lgnuefi -lefi $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
clean:
rm -f $(BOOT) $(HV_OBJDIR)/$(HV_FILE).efi $(EFI_OBJDIR)/boot.so $(ACRN_OBJS) $(FS)
$(EFI_OBJDIR)/%.o:%.S
[ ! -e $@ ] && mkdir -p $(dir $@); \
$(CC) $(CFLAGS) -c -o $@ $<
$(EFI_OBJDIR)/%.o: %.c
[ ! -e $@ ] && mkdir -p $(dir $@); \
$(CC) $(patsubst %, -I%, $(INCLUDE_PATH)) -I. -c $(CFLAGS) $(ARCH_CFLAGS) $< -o $@
%.efi: %.so
$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
-j .rela -j .reloc --target=$(FORMAT) $*.so $@

View File

@@ -0,0 +1,615 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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 <efi.h>
#include <efilib.h>
#include "efilinux.h"
#include "stdlib.h"
#include "boot.h"
#include "multiboot.h"
#define ERROR_STRING_LENGTH 32
#define EFI_LOADER_SIGNATURE "EL64"
#define LEAGCY_BIOS
#define ACPI_XSDT_ENTRY_SIZE (sizeof (UINT64))
#define ACPI_NAME_SIZE 4
#define ACPI_OEM_ID_SIZE 6
#define ACPI_OEM_TABLE_ID_SIZE 8
EFI_SYSTEM_TABLE *sys_table;
EFI_BOOT_SERVICES *boot;
EFI_RUNTIME_SERVICES *runtime;
/**
* memory_map - Allocate and fill out an array of memory descriptors
* @map_buf: buffer containing the memory map
* @map_size: size of the buffer containing the memory map
* @map_key: key for the current memory map
* @desc_size: size of the desc
* @desc_version: memory descriptor version
*
* On success, @map_size contains the size of the memory map pointed
* to by @map_buf and @map_key, @desc_size and @desc_version are
* updated.
*/
EFI_STATUS
memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size,
UINTN *map_key, UINTN *desc_size, UINT32 *desc_version)
{
EFI_STATUS err;
*map_size = sizeof(**map_buf) * 31;
get_map:
/*
* Because we're about to allocate memory, we may
* potentially create a new memory descriptor, thereby
* increasing the size of the memory map. So increase
* the buffer size by the size of one memory
* descriptor, just in case.
*/
*map_size += sizeof(**map_buf);
err = allocate_pool(EfiLoaderData, *map_size,
(void **)map_buf);
if (err != EFI_SUCCESS) {
Print(L"Failed to allocate pool for memory map");
goto failed;
}
err = get_memory_map(map_size, *map_buf, map_key,
desc_size, desc_version);
if (err != EFI_SUCCESS) {
if (err == EFI_BUFFER_TOO_SMALL) {
/*
* 'map_size' has been updated to reflect the
* required size of a map buffer.
*/
free_pool((void *)*map_buf);
goto get_map;
}
Print(L"Failed to get memory map");
goto failed;
}
failed:
return err;
}
static inline BOOLEAN isspace(CHAR8 ch)
{
return ((unsigned char)ch <= ' ');
}
#if 0
static void print_ch(char *str)
{
int j;
CHAR16 *buf;
int len = strlen(str);
buf = malloc((strlen(str) + 1)* 2);
for (j=0; j<len; j++)
buf[j] = str[j];
buf[j] = 0;
Print(L"CHAR16::: %s\n", buf);
free(buf);
}
#endif
struct acpi_table_rsdp {
char signature[8]; /* ACPI signature, contains "RSD PTR " */
UINT8 checksum; /* ACPI 1.0 checksum */
char oem_id[ACPI_OEM_ID_SIZE]; /* OEM identification */
UINT8 revision; /* Must be (0) for ACPI 1.0 or (2) for ACPI 2.0+ */
UINT32 rsdt_physical_address; /* 32-bit physical address of the RSDT */
UINT32 length; /* Table length in bytes, including header (ACPI 2.0+) */
UINT64 xsdt_physical_address; /* 64-bit physical address of the XSDT (ACPI 2.0+) */
UINT8 extended_checksum; /* Checksum of entire table (ACPI 2.0+) */
UINT8 reserved[3]; /* Reserved, must be zero */
};
struct acpi_table_header {
char signature[ACPI_NAME_SIZE]; /* ASCII table signature */
UINT32 length; /* Length of table in bytes, including this header */
UINT8 revision; /* ACPI Specification minor version number */
UINT8 checksum; /* To make sum of entire table == 0 */
char oem_id[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */
char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */
UINT32 oem_revision; /* OEM revision number */
char asl_compiler_id[ACPI_NAME_SIZE]; /* ASCII ASL compiler vendor ID */
UINT32 asl_compiler_revision; /* ASL compiler version */
};
typedef void(*hv_func)(int, struct multiboot_info*, struct efi_ctx*);
EFI_IMAGE_ENTRY_POINT get_pe_entry(CHAR8 *base);
static inline void hv_jump(EFI_PHYSICAL_ADDRESS hv_start,
struct multiboot_info* mbi, struct efi_ctx* pe)
{
hv_func hf;
asm volatile ("cli");
/* The 64-bit kernel entry is 512 bytes after the start. */
hf = (hv_func)(hv_start + 0x200);
/*
* The first parameter is a dummy because the kernel expects
* boot_params in %[re]si.
*/
hf(MULTIBOOT_INFO_MAGIC, mbi, pe);
}
EFI_STATUS get_path(CHAR16* name, EFI_LOADED_IMAGE *info, EFI_DEVICE_PATH **path)
{
unsigned int pathlen;
EFI_STATUS efi_status = EFI_SUCCESS;
CHAR16 *pathstr, *pathname;
int i;
for (i = 0; i < StrLen(name); i++) {
if (name[i] == '/')
name[i] = '\\';
}
pathstr = DevicePathToStr(info->FilePath);
for (i = 0; i < StrLen(pathstr); i++) {
if (pathstr[i] == '/')
pathstr[i] = '\\';
}
pathlen = StrLen(pathstr);
if (name[0] == '\\') {
*path = FileDevicePath(info->DeviceHandle, name);
goto out;
}
for (i=pathlen - 1; i > 0; i--) {
if (pathstr[i] == '\\') break;
}
pathstr[i] = '\0';
pathlen = StrLen(pathstr);
pathlen++;
pathname = AllocatePool((pathlen + 1 + StrLen(name))*sizeof(CHAR16));
if (!pathname) {
Print(L"Failed to allocate memory for pathname\n");
efi_status = EFI_OUT_OF_RESOURCES;
goto out;
}
StrCpy(pathname, pathstr);
StrCat(pathname, L"\\");
StrCat(pathname, name);
*path = FileDevicePath(info->DeviceHandle, pathname);
out:
FreePool(pathstr);
return efi_status;
}
/**
* load_kernel - Load a kernel image into memory from the boot device
*/
EFI_STATUS
load_sos_image(EFI_HANDLE image, CHAR16 *name, CHAR16 *cmdline)
{
UINTN map_size, _map_size, map_key;
UINT32 desc_version;
UINTN desc_size;
EFI_MEMORY_DESCRIPTOR *map_buf;
EFI_PHYSICAL_ADDRESS addr;
EFI_LOADED_IMAGE *info = NULL;
EFI_STATUS err;
struct multiboot_mmap *mmap;
struct multiboot_info *mbi;
struct acpi_table_rsdp *rsdp;
int i, j;
err = handle_protocol(image, &LoadedImageProtocol, (void **)&info);
if (err != EFI_SUCCESS)
goto out;
EFI_HANDLE bz_hd;
EFI_DEVICE_PATH *path;
EFI_LOADED_IMAGE *bz_info = NULL;
EFI_IMAGE_ENTRY_POINT pe_entry;
struct efi_ctx* pe;
err = get_path(name, info, &path);
if (err != EFI_SUCCESS) {
Print(L"fail to get bzImage.efi path");
goto out;
}
err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image, path, NULL, 0, &bz_hd);
if (err != EFI_SUCCESS) {
Print(L"failed to load bzImage %lx\n", err);
goto out;
}
err = handle_protocol(bz_hd, &LoadedImageProtocol, (void **)&bz_info);
if (err != EFI_SUCCESS)
goto out;
if (cmdline) {
bz_info->LoadOptions = cmdline;
bz_info->LoadOptionsSize = (StrLen(cmdline) + 1) * sizeof(CHAR16);
}
pe_entry = get_pe_entry(bz_info->ImageBase);
if (pe_entry == NULL) {
Print(L"fail to get pe entry of bzImage\n");
goto out;
}
err = emalloc(sizeof(struct efi_ctx), 8, &addr);
if (err != EFI_SUCCESS)
goto out;
pe = (struct efi_ctx*)(UINTN)addr;
pe->entry = pe_entry;
pe->handle = bz_hd;
pe->table = sys_table;
/* multiboot info */
err = emalloc(16384, 8, &addr);
if (err != EFI_SUCCESS)
goto out;
mbi = (struct multiboot_info *)(UINTN)addr;
memset((void *)mbi, 0x0, sizeof(*mbi));
/* allocate mmap[] */
err = emalloc(sizeof(struct multiboot_mmap)*128, 8, &addr);
if (err != EFI_SUCCESS)
goto out;
mmap = (struct multiboot_mmap *)(UINTN)addr;
memset((void *)mmap, 0x0, sizeof(*mmap)*128);
EFI_CONFIGURATION_TABLE *config_table = sys_table->ConfigurationTable;
for (i = 0; i < sys_table->NumberOfTableEntries;i++) {
EFI_GUID acpi_20_table_guid = ACPI_20_TABLE_GUID;
EFI_GUID acpi_table_guid = ACPI_TABLE_GUID;
if (CompareGuid(&acpi_20_table_guid, &config_table->VendorGuid) == 0) {
rsdp = config_table->VendorTable;
break;
}
if (CompareGuid(&acpi_table_guid, &config_table->VendorGuid) == 0)
rsdp = config_table->VendorTable;
config_table++;
}
if (!rsdp) {
Print(L"unable to find RSDP\n");
goto out;
}
/* We're just interested in the map's size for now */
map_size = 0;
err = get_memory_map(&map_size, NULL, NULL, NULL, NULL);
if (err != EFI_SUCCESS && err != EFI_BUFFER_TOO_SMALL)
goto out;
again:
_map_size = map_size;
err = emalloc(map_size, 1, &addr);
if (err != EFI_SUCCESS)
goto out;
map_buf = (EFI_MEMORY_DESCRIPTOR *)(UINTN)addr;
/*
* Remember! We've already allocated map_buf with emalloc (and
* 'map_size' contains its size) which means that it should be
* positioned below our allocation for the kernel. Use that
* space for the memory map.
*/
err = get_memory_map(&map_size, map_buf, &map_key,
&desc_size, &desc_version);
if (err != EFI_SUCCESS) {
if (err == EFI_BUFFER_TOO_SMALL) {
/*
* Argh! The buffer that we allocated further
* up wasn't large enough which means we need
* to allocate them again, but this time
* larger. 'map_size' has been updated by the
* call to memory_map().
*/
efree((UINTN)map_buf, _map_size);
goto again;
}
goto out;
}
/*
* Convert the EFI memory map to E820.
*/
for (i = 0, j = 0; i < map_size / desc_size; i++) {
EFI_MEMORY_DESCRIPTOR *d;
unsigned int e820_type = 0;
d = (EFI_MEMORY_DESCRIPTOR *)((unsigned long)map_buf + (i * desc_size));
switch(d->Type) {
case EfiReservedMemoryType:
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
case EfiMemoryMappedIO:
case EfiMemoryMappedIOPortSpace:
case EfiPalCode:
e820_type = E820_RESERVED;
break;
case EfiUnusableMemory:
e820_type = E820_UNUSABLE;
break;
case EfiACPIReclaimMemory:
e820_type = E820_ACPI;
break;
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory:
e820_type = E820_RAM;
break;
case EfiACPIMemoryNVS:
e820_type = E820_NVS;
break;
default:
continue;
}
if (e820_type == E820_RAM) {
UINT64 start = d->PhysicalStart;
UINT64 end = d->PhysicalStart + (d->NumberOfPages<<EFI_PAGE_SHIFT);
if (start <= ACRN_HV_ADDR && end > (ACRN_HV_ADDR + ACRN_HV_SIZE))
Print(L"e820[%d] start=%lx len=%lx\n", i, d->PhysicalStart, d->NumberOfPages << EFI_PAGE_SHIFT);
}
if (j && mmap[j-1].mm_type == e820_type &&
(mmap[j-1].mm_base_addr + mmap[j-1].mm_length) == d->PhysicalStart) {
mmap[j-1].mm_length += d->NumberOfPages << EFI_PAGE_SHIFT;
} else {
mmap[j].mm_base_addr = d->PhysicalStart;
mmap[j].mm_length = d->NumberOfPages << EFI_PAGE_SHIFT;
mmap[j].mm_type = e820_type;
j++;
}
}
/* switch hv memory region(0x20000000 ~ 0x22000000) to availiable RAM in e820 table */
mmap[j].mm_base_addr = ACRN_HV_ADDR;
mmap[j].mm_length = ACRN_HV_SIZE;
mmap[j].mm_type = E820_RAM;
j++;
/* reserve secondary memory region(0x1000 ~ 0x10000) for hv */
err = __emalloc(ACRN_SECONDARY_SIZE, ACRN_SECONDARY_ADDR, &addr, EfiReservedMemoryType);
if (err != EFI_SUCCESS)
goto out;
mbi->mi_flags |= MULTIBOOT_INFO_HAS_MMAP | MULTIBOOT_INFO_HAS_CMDLINE;
mbi->mi_mmap_length = j*sizeof(struct multiboot_mmap);
//mbi->mi_cmdline = (UINTN)"uart=mmio@0x92230000";
//mbi->mi_cmdline = (UINTN)"uart=port@0x3F8";
mbi->mi_cmdline = (UINTN)"uart=disabled";
mbi->mi_mmap_addr = (UINTN)mmap;
#ifdef LEAGCY_BIOS
/* copy rsdt in low memory space(0~0x1000) for hypervisor parsing */
memcpy((void *)0x500, (void*)rsdp, sizeof(struct acpi_table_rsdp));
*(UINT16*)(0x40E) = 0x50;
#endif
//Print(L"start 9!\n");
asm volatile ("mov %%cr0, %0":"=r"(pe->cr0));
asm volatile ("mov %%cr3, %0":"=r"(pe->cr3));
asm volatile ("mov %%cr4, %0":"=r"(pe->cr4));
asm volatile ("sidt %0" :: "m" (pe->idt));
asm volatile ("sgdt %0" :: "m" (pe->gdt));
asm volatile ("str %0" :: "m" (pe->tr_sel));
asm volatile ("sldt %0" :: "m" (pe->ldt_sel));
asm volatile ("mov %%cs, %%ax": "=a"(pe->cs_sel));
asm volatile ("lar %%eax, %%eax"
:"=a"(pe->cs_ar)
:"a"(pe->cs_sel)
);
pe->cs_ar = (pe->cs_ar >> 8) & 0xf0ff; /* clear bits 11:8 */
asm volatile ("mov %%es, %%ax": "=a"(pe->es_sel));
asm volatile ("mov %%ss, %%ax": "=a"(pe->ss_sel));
asm volatile ("mov %%ds, %%ax": "=a"(pe->ds_sel));
asm volatile ("mov %%fs, %%ax": "=a"(pe->fs_sel));
asm volatile ("mov %%gs, %%ax": "=a"(pe->gs_sel));
uint32_t idx = 0xC0000080; /* MSR_IA32_EFER */
uint32_t msrl, msrh;
asm volatile ("rdmsr":"=a"(msrl), "=d"(msrh): "c"(idx));
pe->efer = ((uint64_t)msrh<<32) | msrl;
asm volatile ("pushf\n\t"
"pop %0\n\t"
:"=r"(pe->rflags):);
asm volatile ("movq %%rsp, %0":"=r"(pe->rsp));
hv_jump(ACRN_HV_ADDR, mbi, pe);
out:
return err;
}
static EFI_STATUS
parse_args(CHAR16 *options, UINT32 size, CHAR16 **name,
CHAR16 **hcmdline, CHAR16 **scmdline)
{
CHAR16 *n, *p, *cmdline, *search;
UINTN i = 0;
*hcmdline = NULL;
*scmdline = NULL;
*name = NULL;
cmdline = StrDuplicate(options);
search = PoolPrint(L"sos=");
n = strstr_16(cmdline, search);
if (!n) {
Print(L"Failed to get sos\n");
return EFI_OUT_OF_RESOURCES;
}
FreePool(search);
n += 4;
p = n;
i = 0;
while (*n && !isspace((CHAR8)*n)) {
n++; i++;
}
*n++ = '\0';
*name = p;
*scmdline = n;
return EFI_SUCCESS;
}
/**
* efi_main - The entry point for the OS loader image.
* @image: firmware-allocated handle that identifies the image
* @sys_table: EFI system table
*/
EFI_STATUS
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table)
{
WCHAR *error_buf;
EFI_STATUS err;
EFI_LOADED_IMAGE *info;
EFI_PHYSICAL_ADDRESS addr;
CHAR16 *options = NULL, *name;
UINT32 options_size = 0;
CHAR16 *hcmdline, *scmdline;
UINTN sec_addr;
UINTN sec_size;
char *section;
InitializeLib(image, _table);
sys_table = _table;
boot = sys_table->BootServices;
runtime = sys_table->RuntimeServices;
if (CheckCrc(sys_table->Hdr.HeaderSize, &sys_table->Hdr) != TRUE)
return EFI_LOAD_ERROR;
err = handle_protocol(image, &LoadedImageProtocol, (void **)&info);
if (err != EFI_SUCCESS)
goto failed;
options = info->LoadOptions;
options_size = info->LoadOptionsSize;
err = parse_args(options, options_size, &name, &hcmdline, &scmdline);
if (err != EFI_SUCCESS)
return err;
section = ".hv";
err = get_pe_section(info->ImageBase, section, &sec_addr, &sec_size);
if (EFI_ERROR(err)) {
Print(L"Unable to locate section of ACRNHV %r ", err);
goto failed;
}
err = __emalloc(ACRN_HV_SIZE, ACRN_HV_ADDR, &addr, EfiReservedMemoryType);
if (err != EFI_SUCCESS)
goto failed;
/* Copy ACRNHV binary to fixed phys addr. LoadImage and StartImage ?? */
memcpy((char*)addr, info->ImageBase + sec_addr, sec_size);
/* load sos and run hypervisor */
err = load_sos_image(image, name, scmdline);
if (err != EFI_SUCCESS)
goto free_args;
return EFI_SUCCESS;
free_args:
free(name);
failed:
/*
* We need to be careful not to trash 'err' here. If we fail
* to allocate enough memory to hold the error string fallback
* to returning 'err'.
*/
if (allocate_pool(EfiLoaderData, ERROR_STRING_LENGTH,
(void **)&error_buf) != EFI_SUCCESS) {
Print(L"Couldn't allocate pages for error string\n");
return err;
}
StatusToString(error_buf, err);
Print(L": %s\n", error_buf);
return exit(image, err, ERROR_STRING_LENGTH, error_buf);
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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 __ACRNBOOT_H__
#define __ACRNBOOT_H__
#define E820_RAM 1
#define E820_RESERVED 2
#define E820_ACPI 3
#define E820_NVS 4
#define E820_UNUSABLE 5
#define ACRN_HV_SIZE 0x2000000
#define ACRN_HV_ADDR 0x20000000
#define ACRN_SECONDARY_SIZE 0xf000
#define ACRN_SECONDARY_ADDR 0x8000
EFI_STATUS get_pe_section(CHAR8 *base, char *section, UINTN *vaddr, UINTN *size);
EFI_STATUS load_sos_image(EFI_HANDLE image, CHAR16 *name, CHAR16 *cmdline);
struct efi_info {
UINT32 efi_loader_signature;
UINT32 efi_systab;
UINT32 efi_memdesc_size;
UINT32 efi_memdesc_version;
UINT32 efi_memmap;
UINT32 efi_memmap_size;
UINT32 efi_systab_hi;
UINT32 efi_memmap_hi;
};
typedef struct {
UINT16 limit;
UINT64 *base;
} __attribute__((packed)) dt_addr_t;
struct e820_entry {
UINT64 addr; /* start of memory segment */
UINT64 size; /* size of memory segment */
UINT32 type; /* type of memory segment */
} __attribute__((packed));
struct efi_ctx {
EFI_IMAGE_ENTRY_POINT entry;
EFI_HANDLE handle;
EFI_SYSTEM_TABLE* table;
dt_addr_t gdt;
dt_addr_t idt;
uint16_t tr_sel;
uint16_t ldt_sel;
uint64_t cr0;
uint64_t cr3;
uint64_t cr4;
uint64_t rflags;
uint16_t cs_sel;
uint32_t cs_ar;
uint16_t es_sel;
uint16_t ss_sel;
uint16_t ds_sel;
uint16_t fs_sel;
uint16_t gs_sel;
uint64_t rsp;
uint64_t efer;
}__attribute__((packed));
#endif

View File

@@ -0,0 +1,238 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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.
*
* This file contains some wrappers around the gnu-efi functions. As
* we're not going through uefi_call_wrapper() directly, this allows
* us to get some type-safety for function call arguments and for the
* compiler to check that the number of function call arguments is
* correct.
*
* It's also a good place to document the EFI interface.
*/
#ifndef __EFILINUX_H__
#define __EFILINUX_H__
#define EFILINUX_VERSION_MAJOR 1
#define EFILINUX_VERSION_MINOR 0
extern EFI_SYSTEM_TABLE *sys_table;
extern EFI_BOOT_SERVICES *boot;
extern EFI_RUNTIME_SERVICES *runtime;
/**
* allocate_pages - Allocate memory pages from the system
* @atype: type of allocation to perform
* @mtype: type of memory to allocate
* @num_pages: number of contiguous 4KB pages to allocate
* @memory: used to return the address of allocated pages
*
* Allocate @num_pages physically contiguous pages from the system
* memory and return a pointer to the base of the allocation in
* @memory if the allocation succeeds. On success, the firmware memory
* map is updated accordingly.
*
* If @atype is AllocateAddress then, on input, @memory specifies the
* address at which to attempt to allocate the memory pages.
*/
static inline EFI_STATUS
allocate_pages(EFI_ALLOCATE_TYPE atype, EFI_MEMORY_TYPE mtype,
UINTN num_pages, EFI_PHYSICAL_ADDRESS *memory)
{
return uefi_call_wrapper(boot->AllocatePages, 4, atype,
mtype, num_pages, memory);
}
/**
* free_pages - Return memory allocated by allocate_pages() to the firmware
* @memory: physical base address of the page range to be freed
* @num_pages: number of contiguous 4KB pages to free
*
* On success, the firmware memory map is updated accordingly.
*/
static inline EFI_STATUS
free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN num_pages)
{
return uefi_call_wrapper(boot->FreePages, 2, memory, num_pages);
}
/**
* allocate_pool - Allocate pool memory
* @type: the type of pool to allocate
* @size: number of bytes to allocate from pool of @type
* @buffer: used to return the address of allocated memory
*
* Allocate memory from pool of @type. If the pool needs more memory
* pages are allocated from EfiConventionalMemory in order to grow the
* pool.
*
* All allocations are eight-byte aligned.
*/
static inline EFI_STATUS
allocate_pool(EFI_MEMORY_TYPE type, UINTN size, void **buffer)
{
return uefi_call_wrapper(boot->AllocatePool, 3, type, size, buffer);
}
/**
* free_pool - Return pool memory to the system
* @buffer: the buffer to free
*
* Return @buffer to the system. The returned memory is marked as
* EfiConventionalMemory.
*/
static inline EFI_STATUS free_pool(void *buffer)
{
return uefi_call_wrapper(boot->FreePool, 1, buffer);
}
/**
* get_memory_map - Return the current memory map
* @size: the size in bytes of @map
* @map: buffer to hold the current memory map
* @key: used to return the key for the current memory map
* @descr_size: used to return the size in bytes of EFI_MEMORY_DESCRIPTOR
* @descr_version: used to return the version of EFI_MEMORY_DESCRIPTOR
*
* Get a copy of the current memory map. The memory map is an array of
* EFI_MEMORY_DESCRIPTORs. An EFI_MEMORY_DESCRIPTOR describes a
* contiguous block of memory.
*
* On success, @key is updated to contain an identifer for the current
* memory map. The firmware's key is changed every time something in
* the memory map changes. @size is updated to indicate the size of
* the memory map pointed to by @map.
*
* @descr_size and @descr_version are used to ensure backwards
* compatibility with future changes made to the EFI_MEMORY_DESCRIPTOR
* structure. @descr_size MUST be used when the size of an
* EFI_MEMORY_DESCRIPTOR is used in a calculation, e.g when iterating
* over an array of EFI_MEMORY_DESCRIPTORs.
*
* On failure, and if the buffer pointed to by @map is too small to
* hold the memory map, EFI_BUFFER_TOO_SMALL is returned and @size is
* updated to reflect the size of a buffer required to hold the memory
* map.
*/
static inline EFI_STATUS
get_memory_map(UINTN *size, EFI_MEMORY_DESCRIPTOR *map, UINTN *key,
UINTN *descr_size, UINT32 *descr_version)
{
return uefi_call_wrapper(boot->GetMemoryMap, 5, size, map,
key, descr_size, descr_version);
}
/**
* exit_boot_serivces - Terminate all boot services
* @image: firmware-allocated handle that identifies the image
* @key: key to the latest memory map
*
* This function is called when efilinux wants to take complete
* control of the system. efilinux should not make calls to boot time
* services after this function is called.
*/
static inline EFI_STATUS
exit_boot_services(EFI_HANDLE image, UINTN key)
{
return uefi_call_wrapper(boot->ExitBootServices, 2, image, key);
}
/**
* handle_protocol - Query @handle to see if it supports @protocol
* @handle: the handle being queried
* @protocol: the GUID of the protocol
* @interface: used to return the protocol interface
*
* Query @handle to see if @protocol is supported. If it is supported,
* @interface contains the protocol interface.
*/
static inline EFI_STATUS
handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
{
return uefi_call_wrapper(boot->HandleProtocol, 3,
handle, protocol, interface);
}
/**
* exit - Terminate a loaded EFI image
* @image: firmware-allocated handle that identifies the image
* @status: the image's exit code
* @size: size in bytes of @reason. Ignored if @status is EFI_SUCCESS
* @reason: a NUL-terminated status string, optionally followed by binary data
*
* This function terminates @image and returns control to the boot
* services. This function MUST NOT be called until all loaded child
* images have exited. All memory allocated by the image must be freed
* before calling this function, apart from the buffer @reason, which
* will be freed by the firmware.
*/
static inline EFI_STATUS
exit(EFI_HANDLE image, EFI_STATUS status, UINTN size, CHAR16 *reason)
{
return uefi_call_wrapper(boot->Exit, 4, image, status, size, reason);
}
#define PAGE_SIZE 4096
static const CHAR16 *memory_types[] = {
L"EfiReservedMemoryType",
L"EfiLoaderCode",
L"EfiLoaderData",
L"EfiBootServicesCode",
L"EfiBootServicesData",
L"EfiRuntimeServicesCode",
L"EfiRuntimeServicesData",
L"EfiConventionalMemory",
L"EfiUnusableMemory",
L"EfiACPIReclaimMemory",
L"EfiACPIMemoryNVS",
L"EfiMemoryMappedIO",
L"EfiMemoryMappedIOPortSpace",
L"EfiPalCode",
};
static inline const CHAR16 *memory_type_to_str(UINT32 type)
{
if (type > sizeof(memory_types)/sizeof(CHAR16 *))
return L"Unknown";
return memory_types[type];
}
extern EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf,
UINTN *map_size, UINTN *map_key,
UINTN *desc_size, UINT32 *desc_version);
#endif /* __EFILINUX_H__ */

View File

@@ -0,0 +1,271 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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 <efi.h>
#include <efilib.h>
#include "efilinux.h"
#include "stdlib.h"
/**
* emalloc - Allocate memory with a strict alignment requirement
* @size: size in bytes of the requested allocation
* @align: the required alignment of the allocation
* @addr: a pointer to the allocated address on success
*
* If we cannot satisfy @align we return 0.
*/
EFI_STATUS emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr)
{
UINTN map_size, map_key, desc_size;
EFI_MEMORY_DESCRIPTOR *map_buf;
UINTN d, map_end;
UINT32 desc_version;
EFI_STATUS err;
UINTN nr_pages = EFI_SIZE_TO_PAGES(size);
err = memory_map(&map_buf, &map_size, &map_key,
&desc_size, &desc_version);
if (err != EFI_SUCCESS)
goto fail;
d = (UINTN)map_buf;
map_end = (UINTN)map_buf + map_size;
for (; d < map_end; d += desc_size) {
EFI_MEMORY_DESCRIPTOR *desc;
EFI_PHYSICAL_ADDRESS start, end, aligned;
desc = (EFI_MEMORY_DESCRIPTOR *)d;
if (desc->Type != EfiConventionalMemory)
continue;
if (desc->NumberOfPages < nr_pages)
continue;
start = desc->PhysicalStart;
end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT);
/* Low-memory is super-precious! */
if (end <= 1 << 20)
continue;
if (start < 1 << 20) {
size -= (1 << 20) - start;
start = (1 << 20);
}
aligned = (start + align -1) & ~(align -1);
if ((aligned + size) <= end) {
err = allocate_pages(AllocateAddress, EfiLoaderData,
nr_pages, &aligned);
if (err == EFI_SUCCESS) {
*addr = aligned;
break;
}
}
}
if (d == map_end)
err = EFI_OUT_OF_RESOURCES;
free_pool(map_buf);
fail:
return err;
}
EFI_STATUS __emalloc(UINTN size, UINTN align, EFI_PHYSICAL_ADDRESS *addr, EFI_MEMORY_TYPE mem_type)
{
UINTN map_size, map_key, desc_size;
EFI_MEMORY_DESCRIPTOR *map_buf;
UINTN d, map_end;
UINT32 desc_version;
EFI_STATUS err;
UINTN nr_pages = EFI_SIZE_TO_PAGES(size);
err = memory_map(&map_buf, &map_size, &map_key,
&desc_size, &desc_version);
if (err != EFI_SUCCESS)
goto fail;
d = (UINTN)map_buf;
map_end = (UINTN)map_buf + map_size;
for (; d < map_end; d += desc_size) {
EFI_MEMORY_DESCRIPTOR *desc;
EFI_PHYSICAL_ADDRESS start, end, aligned;
desc = (EFI_MEMORY_DESCRIPTOR *)d;
if (desc->Type != EfiConventionalMemory)
continue;
if (desc->NumberOfPages < nr_pages)
continue;
start = desc->PhysicalStart;
end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT);
/* Low-memory is super-precious! */
if (end <= 1 << 20)
continue;
if (start < 1 << 20) {
size -= (1 << 20) - start;
start = (1 << 20);
}
aligned = align;//(start + align -1) & ~(align -1);
if ((aligned + size) <= end) {
//Print(L"trying to allocate memory at %0x!\n", aligned);
err = allocate_pages(AllocateAddress, mem_type,
nr_pages, &aligned);
if (err == EFI_SUCCESS) {
//Print(L"trying to allocate memory at %0x, success!\n", aligned);
*addr = aligned;
break;
} {
//Print(L"trying to allocate memory at %0x, failure!\n", aligned);
}
}
}
if (d == map_end)
err = EFI_OUT_OF_RESOURCES;
free_pool(map_buf);
fail:
return err;
}
/**
* efree - Return memory allocated with emalloc
* @memory: the address of the emalloc() allocation
* @size: the size of the allocation
*/
void efree(EFI_PHYSICAL_ADDRESS memory, UINTN size)
{
UINTN nr_pages = EFI_SIZE_TO_PAGES(size);
free_pages(memory, nr_pages);
}
/**
* malloc - Allocate memory from the EfiLoaderData pool
* @size: size in bytes of the requested allocation
*
* Return a pointer to an allocation of @size bytes of type
* EfiLoaderData.
*/
void *malloc(UINTN size)
{
EFI_STATUS err;
void *buffer;
err = allocate_pool(EfiLoaderData, size, &buffer);
if (err != EFI_SUCCESS)
buffer = NULL;
return buffer;
}
/**
* free - Release memory to the EfiLoaderData pool
* @buffer: pointer to the malloc() allocation to free
*/
void free(void *buffer)
{
if (buffer)
free_pool(buffer);
}
/**
* calloc - Allocate zeroed memory for an array of elements
* @nmemb: number of elements
* @size: size of each element
*/
void *calloc(UINTN nmemb, UINTN size)
{
void *buffer;
/*
* There's no equivalent of UINTN_MAX, so for safety we refuse to
* allocate anything larger than 32 bits.
*/
UINTN bytes = nmemb * size;
if ((nmemb | size) > 0xffffU) {
if (size && bytes / size != nmemb)
return NULL;
}
buffer = malloc(bytes);
if (buffer)
memset(buffer, 0, bytes);
return buffer;
}
EFI_STATUS dump_e820(void)
{
UINTN map_size, map_key, desc_size;
EFI_MEMORY_DESCRIPTOR *map_buf;
UINTN d, map_end;
UINTN i;
UINT32 desc_version;
EFI_STATUS err;
err = memory_map(&map_buf, &map_size, &map_key,
&desc_size, &desc_version);
if (err != EFI_SUCCESS)
goto fail;
d = (UINTN)map_buf;
map_end = (UINTN)map_buf + map_size;
for (i = 0; d < map_end; d += desc_size, i++) {
EFI_MEMORY_DESCRIPTOR *desc;
EFI_PHYSICAL_ADDRESS start, end;
desc = (EFI_MEMORY_DESCRIPTOR *)d;
if (desc->Type != EfiConventionalMemory)
continue;
start = desc->PhysicalStart;
end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT);
Print(L"[%d]start:%lx, end:%lx, type:%d\n", i, start, end, desc->Type);
}
free_pool(map_buf);
fail:
return err;
}

View File

@@ -0,0 +1,186 @@
/* [ORIGIN: src/sys/arch/i386/include/... */
/* $NetBSD: multiboot.h,v 1.8 2009/02/22 18:05:42 ahoka Exp $ */
/*-
* Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Julio M. Merino Vidal.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* multiboot.h
*/
#ifndef _MULTIBOOT_H
#define _MULTIBOOT_H
#include <stdint.h>
//typedef uintptr_t uint32_t;
typedef uintptr_t vaddr_t;
struct multiboot_info;
extern struct multiboot_info mbi;
// ========================================================================
/*
* Multiboot header structure.
*/
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_HEADER_MODS_ALIGNED 0x00000001
#define MULTIBOOT_HEADER_WANT_MEMORY 0x00000002
#define MULTIBOOT_HEADER_HAS_VBE 0x00000004
#define MULTIBOOT_HEADER_HAS_ADDR 0x00010000
#if !defined(_LOCORE)
struct multiboot_header {
uint32_t mh_magic;
uint32_t mh_flags;
uint32_t mh_checksum;
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
uint32_t mh_header_addr;
uint32_t mh_load_addr;
uint32_t mh_load_end_addr;
uint32_t mh_bss_end_addr;
uint32_t mh_entry_addr;
/* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. */
uint32_t mh_mode_type;
uint32_t mh_width;
uint32_t mh_height;
uint32_t mh_depth;
};
#endif /* !defined(_LOCORE) */
/*
* Symbols defined in locore.S.
*/
extern struct multiboot_header *Multiboot_Header;
// ========================================================================
/*
* Multiboot information structure.
*/
#define MULTIBOOT_INFO_MAGIC 0x2BADB002
#define MULTIBOOT_INFO_HAS_MEMORY 0x00000001
#define MULTIBOOT_INFO_HAS_BOOT_DEVICE 0x00000002
#define MULTIBOOT_INFO_HAS_CMDLINE 0x00000004
#define MULTIBOOT_INFO_HAS_MODS 0x00000008
#define MULTIBOOT_INFO_HAS_AOUT_SYMS 0x00000010
#define MULTIBOOT_INFO_HAS_ELF_SYMS 0x00000020
#define MULTIBOOT_INFO_HAS_MMAP 0x00000040
#define MULTIBOOT_INFO_HAS_DRIVES 0x00000080
#define MULTIBOOT_INFO_HAS_CONFIG_TABLE 0x00000100
#define MULTIBOOT_INFO_HAS_LOADER_NAME 0x00000200
#define MULTIBOOT_INFO_HAS_APM_TABLE 0x00000400
#define MULTIBOOT_INFO_HAS_VBE 0x00000800
#if !defined(_LOCORE)
struct multiboot_info {
uint32_t mi_flags;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MEMORY. */
uint32_t mi_mem_lower;
uint32_t mi_mem_upper;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_BOOT_DEVICE. */
uint8_t mi_boot_device_part3;
uint8_t mi_boot_device_part2;
uint8_t mi_boot_device_part1;
uint8_t mi_boot_device_drive;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CMDLINE. */
uint32_t mi_cmdline;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MODS. */
uint32_t mi_mods_count;
uint32_t mi_mods_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_{AOUT,ELF}_SYMS. */
uint32_t mi_elfshdr_num;
uint32_t mi_elfshdr_size;
uint32_t mi_elfshdr_addr;
uint32_t mi_elfshdr_shndx;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MMAP. */
uint32_t mi_mmap_length;
uint32_t mi_mmap_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_DRIVES. */
uint32_t mi_drives_length;
uint32_t mi_drives_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CONFIG_TABLE. */
uint32_t unused_mi_config_table;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_LOADER_NAME. */
uint32_t mi_loader_name;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_APM. */
uint32_t unused_mi_apm_table;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_VBE. */
uint32_t unused_mi_vbe_control_info;
uint32_t unused_mi_vbe_mode_info;
uint32_t unused_mi_vbe_interface_seg;
uint32_t unused_mi_vbe_interface_off;
uint32_t unused_mi_vbe_interface_len;
};
/*
* Memory mapping. This describes an entry in the memory mappings table
* as pointed to by mi_mmap_addr.
*
* Be aware that mm_size specifies the size of all other fields *except*
* for mm_size. In order to jump between two different entries, you
* have to count mm_size + 4 bytes.
*/
struct __attribute__((packed)) multiboot_mmap {
uint32_t mm_size;
uint64_t mm_base_addr;
uint64_t mm_length;
uint32_t mm_type;
};
/*
* Modules. This describes an entry in the modules table as pointed
* to by mi_mods_addr.
*/
struct multiboot_module {
uint32_t mmo_start;
uint32_t mmo_end;
char * mmo_string;
uint32_t mmo_reserved;
};
#endif /* !defined(_LOCORE) */
// ========================================================================
#endif /* _MULTIBOOT_H */

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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.
*
* This file contains some wrappers around the gnu-efi functions. As
* we're not going through uefi_call_wrapper() directly, this allows
* us to get some type-safety for function call arguments and for the
* compiler to check that the number of function call arguments is
* correct.
*
* It's also a good place to document the EFI interface.
*/
#include <efi.h>
#include <efilib.h>
#include "stdlib.h"
#define DOS_FILE_MAGIC_NUMBER 0x5A4D //"MZ"
struct DosFileHeader {
uint16_t mMagic;
uint16_t LastSize;
uint16_t nBlocks;
uint16_t nReloc;
uint16_t HdrSize;
uint16_t MinAlloc;
uint16_t MaxAlloc;
uint16_t ss;
uint16_t sp;
uint16_t Checksum;
uint16_t ip;
uint16_t cs;
uint16_t RelocPos;
uint16_t nOverlay;
uint16_t reserved[4];
uint16_t OEMId;
uint16_t OEMInfo;
uint16_t reserved2[10];
uint32_t ExeHeader;
} __attribute__((packed));
#define IMAGE_FILE_MACHINE_I386 0x14c
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define PE_FILE_MAGIC_NUMBER 0x00004550 //"PE\0\0"
struct PeHeader {
uint32_t mMagic;
uint16_t mMachine;
uint16_t mNumberOfSections;
uint32_t mTimeDateStamp;
uint32_t mPointerToSymbolTable;
uint32_t mNumberOfSymbols;
uint16_t mSizeOfOptionalHeader;
uint16_t mCharacteristics;
} __attribute__((packed));
struct OptionHeader {
uint16_t Format;
uint8_t MajorLinkVer;
uint8_t MinorLinkVer;
uint32_t CodeSize;
uint32_t InitializedDataSize;
uint32_t UninitializedDataSize;
uint32_t EntryPoint;
uint32_t BaseOfCode;
uint32_t BaseOfDate;
} __attribute__((packed));
struct PeSectionHeader {
char mName[8];
uint32_t mVirtualSize;
uint32_t mVirtualAddress;
uint32_t mSizeOfRawData;
uint32_t mPointerToRawData;
uint32_t mPointerToRealocations;
uint32_t mPointerToLinenumbers;
uint16_t mNumberOfRealocations;
uint16_t mNumberOfLinenumbers;
uint32_t mCharacteristics;
} __attribute__((packed));
EFI_STATUS get_pe_section(CHAR8 *base, char *section, UINTN *vaddr, UINTN *size)
{
struct PeSectionHeader *ph;
struct DosFileHeader *dh;
struct PeHeader *pe;
UINTN i;
UINTN offset;
dh = (struct DosFileHeader *)base;
if (dh->mMagic != DOS_FILE_MAGIC_NUMBER)
return EFI_LOAD_ERROR;
pe = (struct PeHeader *)&base[dh->ExeHeader];
if (pe->mMagic != PE_FILE_MAGIC_NUMBER)
return EFI_LOAD_ERROR;
if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64)
&& (pe->mMachine != IMAGE_FILE_MACHINE_I386))
return EFI_LOAD_ERROR;
offset = dh->ExeHeader + sizeof(*pe) + pe->mSizeOfOptionalHeader;
for (i = 0; i < pe->mNumberOfSections; i++) {
ph = (struct PeSectionHeader *)&base[offset];
if (CompareMem(ph->mName, section, strlen(section)) == 0) {
*vaddr = (UINTN)ph->mVirtualAddress;
*size = (UINTN)ph->mVirtualSize;
break;
}
offset += sizeof(*ph);
}
return EFI_SUCCESS;
}
EFI_IMAGE_ENTRY_POINT get_pe_entry(CHAR8 *base)
{
struct DosFileHeader* dh;
struct PeHeader* pe;
struct OptionHeader* oh;
UINTN offset;
dh = (struct DosFileHeader *)base;
if (dh->mMagic != DOS_FILE_MAGIC_NUMBER)
return NULL;
pe = (struct PeHeader *)&base[dh->ExeHeader];
if (pe->mMagic != PE_FILE_MAGIC_NUMBER)
return NULL;
if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64)
&& (pe->mMachine != IMAGE_FILE_MACHINE_I386))
return NULL;
offset = dh->ExeHeader + sizeof(*pe);
oh = (struct OptionHeader*)&base[offset];
return (EFI_IMAGE_ENTRY_POINT)((UINT64)base + oh->EntryPoint);
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2011, 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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 OWNER 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.
*
* This file contains some wrappers around the gnu-efi functions. As
* we're not going through uefi_call_wrapper() directly, this allows
* us to get some type-safety for function call arguments and for the
* compiler to check that the number of function call arguments is
* correct.
*
* It's also a good place to document the EFI interface.
*/
#ifndef __STDLIB_H__
#define __STDLIB_H__
extern void *malloc(UINTN size);
extern void free(void *buf);
extern void *calloc(UINTN nmemb, UINTN size);
extern EFI_STATUS emalloc(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *);
extern EFI_STATUS __emalloc(UINTN, UINTN, EFI_PHYSICAL_ADDRESS *, EFI_MEMORY_TYPE);
extern void efree(EFI_PHYSICAL_ADDRESS, UINTN);
static inline void memset(void *dstv, char ch, UINTN size)
{
char *dst = dstv;
int i;
for (i = 0; i < size; i++)
dst[i] = ch;
}
static inline void memcpy(char *dst, const char *src, UINTN size)
{
int i;
for (i = 0; i < size; i++)
*dst++ = *src++;
}
static inline int strlen(const char *str)
{
int len;
len = 0;
while (*str++)
len++;
return len;
}
static inline char *strstr(const char *haystack, const char *needle)
{
const char *p;
const char *word = NULL;
int len = strlen(needle);
if (!len)
return NULL;
p = haystack;
while (*p) {
word = p;
if (!strncmpa((CHAR8 *)p, (CHAR8 *)needle, len))
break;
p++;
word = NULL;
}
return (char *)word;
}
static inline char *strdup(const char *src)
{
int len;
char *dst;
len = strlen(src);
dst = malloc(len + 1);
if (dst)
memcpy(dst, src, len + 1);
return dst;
}
static inline CHAR16 *strstr_16(CHAR16 *haystack, CHAR16 *needle)
{
CHAR16 *p;
CHAR16 *word = NULL;
UINTN len = StrLen(needle);
if (!len)
return NULL;
p = haystack;
while (*p) {
if (!StrnCmp(p, needle, len)) {
word = p;
break;
}
p++;
}
return (CHAR16*)word;
}
#endif /* __STDLIB_H__ */

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 BSP_CFG_H
#define BSP_CFG_H
#define NR_IOAPICS 1
#define STACK_SIZE 8192
#define LOG_BUF_SIZE 0x100000
#define LOG_DESTINATION 3
#define CPU_UP_TIMEOUT 100
#define CONFIG_SERIAL_PIO_BASE 0x3f8
#define MALLOC_ALIGN 16
#define NUM_ALLOC_PAGES 4096
#define HEAP_SIZE 0x100000
#define CONSOLE_LOGLEVEL_DEFAULT 2
#define MEM_LOGLEVEL_DEFAULT 4
#define CONFIG_LOW_RAM_START 0x00008000
#define CONFIG_LOW_RAM_SIZE 0x000CF000
#define CONFIG_RAM_START 0x20000000
#define CONFIG_RAM_SIZE 0x02000000 /* 32M */
#define CONFIG_DMAR_PARSE_ENABLED 1
#define CONFIG_GPU_SBDF 0x00000010 /* 0000:00:02.0 */
#define CONFIG_EFI_STUB 1
#define CONFIG_RETPOLINE
#endif /* BSP_CFG_H */

160
hypervisor/bsp/uefi/uefi.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <multiboot.h>
#ifdef CONFIG_EFI_STUB
#include <acrn_efi.h>
#endif
#include <hv_debug.h>
/* IOAPIC id */
#define UEFI_IOAPIC_ID 8
/* IOAPIC base address */
#define UEFI_IOAPIC_ADDR 0xfec00000
/* IOAPIC range size */
#define UEFI_IOAPIC_SIZE 0x100000
/* Local APIC base address */
#define UEFI_LAPIC_ADDR 0xfee00000
/* Local APIC range size */
#define UEFI_LAPIC_SIZE 0x100000
/* Number of PCI IRQ assignments */
#define UEFI_PCI_IRQ_ASSIGNMENT_NUM 28
#ifdef CONFIG_EFI_STUB
uint32_t efi_physical_available_ap_bitmap = 0;
uint32_t efi_wake_up_ap_bitmap = 0;
struct efi_ctx* efi_ctx = NULL;
int efi_launch_vector;
extern uint32_t up_count;
extern unsigned long pcpu_sync;
bool in_efi_boot_svc(void)
{
return (efi_wake_up_ap_bitmap != efi_physical_available_ap_bitmap);
}
int efi_spurious_handler(int vector)
{
struct vcpu* vcpu;
if (get_cpu_id() != 0)
return 0;
vcpu = per_cpu(vcpu, 0);
if (vcpu && vcpu->launched) {
int ret = vlapic_set_intr(vcpu, vector, 0);
if (ret && in_efi_boot_svc())
exec_vmwrite(VMX_ENTRY_INT_INFO_FIELD,
VMX_INT_INFO_VALID | vector);
} else
efi_launch_vector = vector;
return 1;
}
int sipi_from_efi_boot_service_exit(uint32_t dest, uint32_t mode, uint32_t vec)
{
if (efi_wake_up_ap_bitmap != efi_physical_available_ap_bitmap) {
if (mode == APIC_DELMODE_STARTUP) {
uint32_t cpu_id = cpu_find_logical_id(dest);
send_startup_ipi(INTR_CPU_STARTUP_USE_DEST,
cpu_id, (paddr_t)(vec<<12));
efi_wake_up_ap_bitmap |= 1 << dest;
}
return 1;
}
return 0;
}
void efi_deferred_wakeup_pcpu(int cpu_id)
{
uint32_t timeout;
uint32_t expected_up;
expected_up = up_count + 1;
send_startup_ipi(INTR_CPU_STARTUP_USE_DEST,
cpu_id, (paddr_t)cpu_secondary_reset);
timeout = CPU_UP_TIMEOUT * 1000;
while ((up_count != expected_up)) {
/* Delay 10us */
udelay(10);
/* Decrement timeout value */
timeout -= 10;
}
bitmap_set(0, &pcpu_sync);
}
int uefi_sw_loader(struct vm *vm, struct vcpu *vcpu)
{
int ret = 0;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
ASSERT(vm != NULL, "Incorrect argument");
pr_dbg("Loading guest to run-time location");
if (!is_vm0(vm))
return load_guest(vm, vcpu);
vcpu->entry_addr = efi_ctx->entry;
cur_context->guest_cpu_regs.regs.rcx = efi_ctx->handle;
cur_context->guest_cpu_regs.regs.rdx = efi_ctx->table;
return ret;
}
#endif
void init_bsp(void)
{
parse_hv_cmdline();
#ifdef CONFIG_EFI_STUB
efi_ctx = (struct efi_ctx*)(uint64_t)boot_regs[2];
ASSERT(efi_ctx != NULL, "");
vm_sw_loader = uefi_sw_loader;
spurious_handler = efi_spurious_handler;
efi_launch_vector = -1;
#endif
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#define NUM_USER_VMS 2
/* Number of CPUs in VM0 */
#define VM0_NUM_CPUS 1
/* Logical CPU IDs assigned to VM0 */
int VM0_CPUS[VM0_NUM_CPUS] = {0};
/* Number of CPUs in VM1 */
#define VM1_NUM_CPUS 2
/* Logical CPU IDs assigned with VM1 */
int VM1_CPUS[VM1_NUM_CPUS] = {3, 1};
const struct vm_description_array vm_desc = {
/* Number of user virtual machines */
.num_vm_desc = NUM_USER_VMS,
/* Virtual Machine descriptions */
.vm_desc_array = {
{
.vm_attr_name = "vm_0",
.vm_hw_num_cores = VM0_NUM_CPUS,
.vm_hw_logical_core_ids = &VM0_CPUS[0],
.vm_state_info_privilege = VM_PRIVILEGE_LEVEL_HIGH,
.vm_created = false,
},
}
};
const struct vm_description_array *get_vm_desc_base(void)
{
return &vm_desc;
}

206
hypervisor/common/hv_main.c Normal file
View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <schedule.h>
#include <hv_debug.h>
bool x2apic_enabled;
static DEFINE_CPU_DATA(uint64_t[64], vmexit_cnt);
static DEFINE_CPU_DATA(uint64_t[64], vmexit_time);
static void run_vcpu_pre_work(struct vcpu *vcpu)
{
unsigned long *pending_pre_work = &vcpu->pending_pre_work;
if (bitmap_test_and_clear(ACRN_VCPU_MMIO_COMPLETE, pending_pre_work))
dm_emulate_mmio_post(vcpu);
}
void vcpu_thread(struct vcpu *vcpu)
{
uint64_t vmexit_begin, vmexit_end;
uint16_t exit_reason;
uint64_t tsc_aux_hyp_cpu = vcpu->pcpu_id;
struct vm_exit_dispatch *vmexit_hdlr;
int ret = 0;
vmexit_begin = vmexit_end = exit_reason = 0;
/* If vcpu is not launched, we need to do init_vmcs first */
if (!vcpu->launched)
init_vmcs(vcpu);
run_vcpu_pre_work(vcpu);
do {
/* handling pending softirq */
CPU_IRQ_ENABLE();
exec_softirq();
CPU_IRQ_DISABLE();
/* Check and process interrupts */
acrn_do_intr_process(vcpu);
if (need_rescheduled(vcpu->pcpu_id)) {
/*
* In extrem case, schedule() could return. Which
* means the vcpu resume happens before schedule()
* triggered by vcpu suspend. In this case, we need
* to do pre work and continue vcpu loop after
* schedule() is return.
*/
schedule();
run_vcpu_pre_work(vcpu);
continue;
}
vmexit_end = rdtsc();
if (vmexit_begin > 0)
per_cpu(vmexit_time, vcpu->pcpu_id)[exit_reason]
+= (vmexit_end - vmexit_begin);
TRACE_2L(TRACE_VM_ENTER, 0, 0);
/* Restore guest TSC_AUX */
if (vcpu->launched) {
CPU_MSR_WRITE(MSR_IA32_TSC_AUX,
vcpu->msr_tsc_aux_guest);
}
ret = start_vcpu(vcpu);
ASSERT(ret == 0, "vcpu resume failed");
vmexit_begin = rdtsc();
vcpu->arch_vcpu.nrexits++;
/* Save guest TSC_AUX */
CPU_MSR_READ(MSR_IA32_TSC_AUX, &vcpu->msr_tsc_aux_guest);
/* Restore native TSC_AUX */
CPU_MSR_WRITE(MSR_IA32_TSC_AUX, tsc_aux_hyp_cpu);
ASSERT((int)get_cpu_id() == vcpu->pcpu_id, "");
/* Dispatch handler */
vmexit_hdlr = vmexit_handler(vcpu);
ASSERT(vmexit_hdlr != 0,
"Unable to dispatch VM exit handler!");
exit_reason = vcpu->arch_vcpu.exit_reason & 0xFFFF;
per_cpu(vmexit_cnt, vcpu->pcpu_id)[exit_reason]++;
TRACE_2L(TRACE_VM_EXIT, exit_reason,
vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context].rip);
if (exit_reason == VMX_EXIT_REASON_EXTERNAL_INTERRUPT) {
/* Handling external_interrupt
* should disable intr
*/
vmexit_hdlr->handler(vcpu);
} else {
CPU_IRQ_ENABLE();
vmexit_hdlr->handler(vcpu);
CPU_IRQ_DISABLE();
}
} while (1);
}
static bool is_vm0_bsp(int pcpu_id)
{
struct vm_description *vm_desc = get_vm_desc(0);
ASSERT(vm_desc, "get vm desc failed");
return pcpu_id == vm_desc->vm_hw_logical_core_ids[0];
}
int hv_main(int cpu_id)
{
int ret = 0;
pr_info("%s, Starting common entry point for CPU %d",
__func__, cpu_id);
ASSERT(cpu_id < phy_cpu_num, "cpu_id out of range");
ASSERT((uint64_t) cpu_id == get_cpu_id(),
"cpu_id/tsc_aux mismatch");
/* Check if virtualization extensions are supported */
ret = check_vmx_support();
ASSERT(ret == 0, "VMX not supported!");
/* Enable virtualization extensions */
ret = exec_vmxon_instr();
ASSERT(ret == 0, "Unable to enable VMX!");
/* X2APIC mode is disabled by default. */
x2apic_enabled = false;
if (is_vm0_bsp(cpu_id))
prepare_vm0();
default_idle();
return ret;
}
int get_vmexit_profile(char *str, int str_max)
{
int cpu, i, len, size = str_max;
len = snprintf(str, size, "\r\nNow(us) = %16lld\r\n",
TICKS_TO_US(rdtsc()));
size -= len;
str += len;
len = snprintf(str, size, "\r\nREASON");
size -= len;
str += len;
for (cpu = 0; cpu < phy_cpu_num; cpu++) {
len = snprintf(str, size, "\t CPU%d\t US", cpu);
size -= len;
str += len;
}
for (i = 0; i < 64; i++) {
len = snprintf(str, size, "\r\n0x%x", i);
size -= len;
str += len;
for (cpu = 0; cpu < phy_cpu_num; cpu++) {
len = snprintf(str, size, "\t%10lld\t%10lld",
per_cpu(vmexit_cnt, cpu)[i],
TICKS_TO_US(per_cpu(vmexit_time, cpu)[i]));
size -= len;
str += len;
}
}
snprintf(str, size, "\r\n");
return 0;
}

View File

@@ -0,0 +1,868 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <schedule.h>
#include <hypercall.h>
#include <acrn_hv_defs.h>
#include <hv_debug.h>
#include <version.h>
#define ACRN_DBG_HYCALL 6
int64_t hcall_get_api_version(struct vm *vm, uint64_t param)
{
struct hc_api_version version;
if (!is_vm0(vm))
return -1;
version.major_version = HV_MAJOR_VERSION;
version.minor_version = HV_MINOR_VERSION;
if (copy_to_vm(vm, &version, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
return 0;
}
static int handle_vpic_irqline(struct vm *vm, int irq, enum irq_mode mode)
{
int ret = -1;
if (!vm)
return ret;
switch (mode) {
case IRQ_ASSERT:
ret = vpic_assert_irq(vm, irq);
break;
case IRQ_DEASSERT:
ret = vpic_deassert_irq(vm, irq);
break;
case IRQ_PULSE:
ret = vpic_pulse_irq(vm, irq);
default:
break;
}
return ret;
}
static int
handle_vioapic_irqline(struct vm *vm, int irq, enum irq_mode mode)
{
int ret = -1;
if (!vm)
return ret;
switch (mode) {
case IRQ_ASSERT:
ret = vioapic_assert_irq(vm, irq);
break;
case IRQ_DEASSERT:
ret = vioapic_deassert_irq(vm, irq);
break;
case IRQ_PULSE:
ret = vioapic_pulse_irq(vm, irq);
break;
default:
break;
}
return ret;
}
static int handle_virt_irqline(struct vm *vm, uint64_t target_vmid,
struct acrn_irqline *param, enum irq_mode mode)
{
int ret = 0;
long intr_type;
struct vm *target_vm = get_vm_from_vmid(target_vmid);
if (!vm || !param)
return -1;
intr_type = param->intr_type;
switch (intr_type) {
case ACRN_INTR_TYPE_ISA:
/* Call vpic for pic injection */
ret = handle_vpic_irqline(target_vm, param->pic_irq, mode);
/* call vioapic for ioapic injection if ioapic_irq != -1*/
if (param->ioapic_irq != -1UL) {
/* handle IOAPIC irqline */
ret = handle_vioapic_irqline(target_vm,
param->ioapic_irq, mode);
}
break;
case ACRN_INTR_TYPE_IOAPIC:
/* handle IOAPIC irqline */
ret = handle_vioapic_irqline(target_vm,
param->ioapic_irq, mode);
break;
default:
dev_dbg(ACRN_DBG_HYCALL, "vINTR inject failed. type=%d",
intr_type);
ret = -1;
}
return ret;
}
int64_t hcall_create_vm(struct vm *vm, uint64_t param)
{
int64_t ret = 0;
struct vm *target_vm = NULL;
/* VM are created from hv_main() directly
* Here we just return the vmid for DM
*/
struct acrn_create_vm cv;
struct vm_description vm_desc;
memset((void *)&cv, 0, sizeof(cv));
if (copy_from_vm(vm, &cv, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
memset(&vm_desc, 0, sizeof(vm_desc));
vm_desc.secure_world_enabled = cv.secure_world_enabled;
memcpy_s(&vm_desc.GUID[0], 16, &cv.GUID[0], 16);
ret = create_vm(&vm_desc, &target_vm);
if (ret != 0) {
dev_dbg(ACRN_DBG_HYCALL, "HCALL: Create VM failed");
cv.vmid = ACRN_INVALID_VMID;
ret = -1;
} else {
cv.vmid = target_vm->attr.id;
ret = 0;
}
if (copy_to_vm(vm, &cv.vmid, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
return ret;
}
int64_t hcall_destroy_vm(uint64_t vmid)
{
int64_t ret = 0;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
ret = shutdown_vm(target_vm);
return ret;
}
int64_t hcall_resume_vm(uint64_t vmid)
{
int64_t ret = 0;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
if (target_vm->sw.req_buf == 0)
ret = -1;
else
ret = start_vm(target_vm);
return ret;
}
int64_t hcall_pause_vm(uint64_t vmid)
{
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
pause_vm(target_vm);
return 0;
}
int64_t hcall_create_vcpu(struct vm *vm, uint64_t vmid, uint64_t param)
{
int ret, pcpu_id;
struct acrn_create_vcpu cv;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (!target_vm || !param)
return -1;
if (copy_from_vm(vm, &cv, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
pcpu_id = allocate_pcpu();
if (-1 == pcpu_id) {
pr_err("%s: No physical available\n", __func__);
return -1;
}
ret = prepare_vcpu(target_vm, pcpu_id);
return ret;
}
int64_t hcall_assert_irqline(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct acrn_irqline irqline;
if (copy_from_vm(vm, &irqline, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
ret = handle_virt_irqline(vm, vmid, &irqline, IRQ_ASSERT);
return ret;
}
int64_t hcall_deassert_irqline(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct acrn_irqline irqline;
if (copy_from_vm(vm, &irqline, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
ret = handle_virt_irqline(vm, vmid, &irqline, IRQ_DEASSERT);
return ret;
}
int64_t hcall_pulse_irqline(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct acrn_irqline irqline;
if (copy_from_vm(vm, &irqline, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
ret = handle_virt_irqline(vm, vmid, &irqline, IRQ_PULSE);
return ret;
}
int64_t hcall_inject_msi(struct vm *vm, uint64_t vmid, uint64_t param)
{
int ret = 0;
struct acrn_msi_entry msi;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&msi, 0, sizeof(msi));
if (copy_from_vm(vm, &msi, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
ret = vlapic_intr_msi(target_vm, msi.msi_addr, msi.msi_data);
return ret;
}
int64_t hcall_set_ioreq_buffer(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct acrn_set_ioreq_buffer iobuf;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&iobuf, 0, sizeof(iobuf));
if (copy_from_vm(vm, &iobuf, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
dev_dbg(ACRN_DBG_HYCALL, "[%d] SET BUFFER=0x%x",
vmid, iobuf.req_buf);
/* store gpa of guest request_buffer */
target_vm->sw.req_buf = gpa2hpa(vm, iobuf.req_buf);
return ret;
}
static void complete_request(struct vcpu *vcpu)
{
/*
* If vcpu is in Zombie state and will be destroyed soon. Just
* mark ioreq done and don't resume vcpu.
*/
if (vcpu->state == VCPU_ZOMBIE) {
struct vhm_request_buffer *req_buf;
req_buf = (struct vhm_request_buffer *)vcpu->vm->sw.req_buf;
req_buf->req_queue[vcpu->vcpu_id].valid = false;
atomic_store_rel_32(&vcpu->ioreq_pending, 0);
return;
}
switch (vcpu->req.type) {
case REQ_MMIO:
request_vcpu_pre_work(vcpu, ACRN_VCPU_MMIO_COMPLETE);
break;
case REQ_PORTIO:
dm_emulate_pio_post(vcpu);
break;
default:
break;
}
resume_vcpu(vcpu);
}
int64_t hcall_notify_req_finish(uint64_t vmid, uint64_t vcpu_id)
{
int64_t ret = 0;
struct vhm_request_buffer *req_buf;
struct vhm_request *req;
struct vcpu *vcpu;
struct vm *target_vm = get_vm_from_vmid(vmid);
/* make sure we have set req_buf */
if (!target_vm || target_vm->sw.req_buf == 0)
return -1;
dev_dbg(ACRN_DBG_HYCALL, "[%d] NOTIFY_FINISH for vcpu %d",
vmid, vcpu_id);
vcpu = vcpu_from_vid(target_vm, vcpu_id);
ASSERT(vcpu != NULL, "Failed to get VCPU context.");
req_buf = (struct vhm_request_buffer *)target_vm->sw.req_buf;
req = req_buf->req_queue + vcpu_id;
if (req->valid &&
((req->processed == REQ_STATE_SUCCESS) ||
(req->processed == REQ_STATE_FAILED)))
complete_request(vcpu);
return ret;
}
int64_t hcall_set_vm_memmap(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
uint64_t hpa;
uint32_t attr, prot;
struct vm_set_memmap memmap;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (!vm || !target_vm)
return -1;
memset((void *)&memmap, 0, sizeof(memmap));
if (copy_from_vm(vm, &memmap, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
if (!is_vm0(vm)) {
pr_err("%s: ERROR! Not coming from service vm", __func__);
return -1;
}
if (is_vm0(target_vm)) {
pr_err("%s: ERROR! Targeting to service vm", __func__);
return -1;
}
if ((memmap.length & 0xFFF) != 0) {
pr_err("%s: ERROR! [vm%d] map size 0x%x is not page aligned",
__func__, vmid, memmap.length);
return -1;
}
hpa = gpa2hpa(vm, memmap.vm0_gpa);
dev_dbg(ACRN_DBG_HYCALL, "[vm%d] gpa=0x%x hpa=0x%x size=0x%x",
vmid, memmap.remote_gpa, hpa, memmap.length);
/* Check prot */
attr = 0;
if (memmap.type != MAP_UNMAP) {
prot = memmap.prot;
if (prot & MEM_ACCESS_READ)
attr |= MMU_MEM_ATTR_READ;
if (prot & MEM_ACCESS_WRITE)
attr |= MMU_MEM_ATTR_WRITE;
if (prot & MEM_ACCESS_EXEC)
attr |= MMU_MEM_ATTR_EXECUTE;
if (prot & MEM_TYPE_WB)
attr |= MMU_MEM_ATTR_WB_CACHE;
else if (prot & MEM_TYPE_WT)
attr |= MMU_MEM_ATTR_WT_CACHE;
else if (prot & MEM_TYPE_UC)
attr |= MMU_MEM_ATTR_UNCACHED;
else if (prot & MEM_TYPE_WC)
attr |= MMU_MEM_ATTR_WC;
else if (prot & MEM_TYPE_WP)
attr |= MMU_MEM_ATTR_WP;
else
attr |= MMU_MEM_ATTR_UNCACHED;
}
/* create gpa to hpa EPT mapping */
ret = ept_mmap(target_vm, hpa,
memmap.remote_gpa, memmap.length, memmap.type, attr);
return ret;
}
int64_t hcall_remap_pci_msix(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct acrn_vm_pci_msix_remap remap;
struct ptdev_msi_info info;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&remap, 0, sizeof(remap));
if (copy_from_vm(vm, &remap, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
if (!is_vm0(vm))
ret = -1;
else {
info.msix = remap.msix;
info.msix_entry_index = remap.msix_entry_index;
info.vmsi_ctl = remap.msi_ctl;
info.vmsi_addr = remap.msi_addr;
info.vmsi_data = remap.msi_data;
ret = ptdev_msix_remap(target_vm,
remap.virt_bdf, &info);
remap.msi_data = info.pmsi_data;
remap.msi_addr = info.pmsi_addr;
if (copy_to_vm(vm, &remap, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
}
return ret;
}
int64_t hcall_gpa_to_hpa(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct vm_gpa2hpa v_gpa2hpa;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&v_gpa2hpa, 0, sizeof(v_gpa2hpa));
if (copy_from_vm(vm, &v_gpa2hpa, param)) {
pr_err("HCALL gpa2hpa: Unable copy param from vm\n");
return -1;
}
v_gpa2hpa.hpa = gpa2hpa(target_vm, v_gpa2hpa.gpa);
if (copy_to_vm(vm, &v_gpa2hpa, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
return ret;
}
int64_t hcall_assign_ptdev(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
uint16_t bdf;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
if (copy_from_vm(vm, &bdf, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
/* create a iommu domain for target VM if not created */
if (!target_vm->iommu_domain) {
ASSERT(target_vm->arch_vm.ept, "EPT of VM not set!");
/* TODO: how to get vm's address width? */
target_vm->iommu_domain = create_iommu_domain(vmid,
target_vm->arch_vm.ept, 48);
ASSERT(target_vm->iommu_domain,
"failed to created iommu domain!");
}
ret = assign_iommu_device(target_vm->iommu_domain,
(uint8_t)(bdf >> 8), (uint8_t)(bdf & 0xff));
return ret;
}
int64_t hcall_deassign_ptdev(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
uint16_t bdf;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
if (copy_from_vm(vm, &bdf, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
ret = unassign_iommu_device(target_vm->iommu_domain,
(uint8_t)(bdf >> 8), (uint8_t)(bdf & 0xff));
return ret;
}
int64_t hcall_set_ptdev_intr_info(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct hc_ptdev_irq irq;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&irq, 0, sizeof(irq));
if (copy_from_vm(vm, &irq, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
if (irq.type == IRQ_INTX)
ptdev_add_intx_remapping(target_vm,
irq.virt_bdf, irq.phys_bdf,
irq.is.intx.virt_pin, irq.is.intx.phys_pin,
irq.is.intx.pic_pin);
else if (irq.type == IRQ_MSI || irq.type == IRQ_MSIX)
ptdev_add_msix_remapping(target_vm,
irq.virt_bdf, irq.phys_bdf,
irq.is.msix.vector_cnt);
return ret;
}
int64_t
hcall_reset_ptdev_intr_info(struct vm *vm, uint64_t vmid, uint64_t param)
{
int64_t ret = 0;
struct hc_ptdev_irq irq;
struct vm *target_vm = get_vm_from_vmid(vmid);
if (target_vm == NULL)
return -1;
memset((void *)&irq, 0, sizeof(irq));
if (copy_from_vm(vm, &irq, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
if (irq.type == IRQ_INTX)
ptdev_remove_intx_remapping(target_vm,
irq.is.intx.virt_pin,
irq.is.intx.pic_pin);
else if (irq.type == IRQ_MSI || irq.type == IRQ_MSIX)
ptdev_remove_msix_remapping(target_vm,
irq.virt_bdf,
irq.is.msix.vector_cnt);
return ret;
}
#ifdef HV_DEBUG
int64_t hcall_setup_sbuf(struct vm *vm, uint64_t param)
{
struct sbuf_setup_param ssp;
uint64_t *hva;
memset((void *)&ssp, 0, sizeof(ssp));
if (copy_from_vm(vm, &ssp, param)) {
pr_err("%s: Unable copy param to vm\n", __func__);
return -1;
}
if (ssp.gpa)
hva = (uint64_t *)GPA2HVA(vm, ssp.gpa);
else
hva = (uint64_t *)NULL;
return sbuf_share_setup(ssp.pcpu_id, ssp.sbuf_id, hva);
}
#else /* HV_DEBUG */
int64_t hcall_setup_sbuf(__unused struct vm *vm,
__unused uint64_t param)
{
return -1;
}
#endif /* HV_DEBUG */
static void fire_vhm_interrupt(void)
{
/*
* use vLAPIC to inject vector to SOS vcpu 0 if vlapic is enabled
* otherwise, send IPI hardcoded to CPU_BOOT_ID
*/
struct vm *vm0;
struct vcpu *vcpu;
vm0 = get_vm_from_vmid(0);
ASSERT(vm0, "VM Pointer is NULL");
vcpu = vcpu_from_vid(vm0, 0);
ASSERT(vcpu, "vcpu_from_vid failed");
vlapic_intr_edge(vcpu, VECTOR_VIRT_IRQ_VHM);
}
#ifdef HV_DEBUG
static void acrn_print_request(int vcpu_id, struct vhm_request *req)
{
switch (req->type) {
case REQ_MMIO:
dev_dbg(ACRN_DBG_HYCALL, "[vcpu_id=%d type=MMIO]", vcpu_id);
dev_dbg(ACRN_DBG_HYCALL,
"gpa=0x%lx, R/W=%d, size=%ld value=0x%lx processed=%lx",
req->reqs.mmio_request.address,
req->reqs.mmio_request.direction,
req->reqs.mmio_request.size,
req->reqs.mmio_request.value,
req->processed);
break;
case REQ_PORTIO:
dev_dbg(ACRN_DBG_HYCALL, "[vcpu_id=%d type=PORTIO]", vcpu_id);
dev_dbg(ACRN_DBG_HYCALL,
"IO=0x%lx, R/W=%d, size=%ld value=0x%lx processed=%lx",
req->reqs.pio_request.address,
req->reqs.pio_request.direction,
req->reqs.pio_request.size,
req->reqs.pio_request.value,
req->processed);
break;
default:
dev_dbg(ACRN_DBG_HYCALL, "[vcpu_id=%d type=%d] NOT support type",
vcpu_id, req->type);
break;
}
}
#else
static void acrn_print_request(__unused int vcpu_id,
__unused struct vhm_request *req)
{
}
#endif
int acrn_insert_request_wait(struct vcpu *vcpu, struct vhm_request *req)
{
struct vhm_request_buffer *req_buf =
(void *)HPA2HVA(vcpu->vm->sw.req_buf);
long cur;
ASSERT(sizeof(*req) == (4096/VHM_REQUEST_MAX),
"vhm_request page broken!");
if (!vcpu || !req || vcpu->vm->sw.req_buf == 0)
return -1;
/* ACRN insert request to VHM and inject upcall */
cur = vcpu->vcpu_id;
req_buf->req_queue[cur] = *req;
/* Must clear the signal before we mark req valid
* Once we mark to valid, VHM may process req and signal us
* before we perform upcall.
* because VHM can work in pulling mode without wait for upcall
*/
req_buf->req_queue[cur].valid = true;
acrn_print_request(vcpu->vcpu_id, req_buf->req_queue + cur);
/* signal VHM */
fire_vhm_interrupt();
/* pause vcpu, wait for VHM to handle the MMIO request */
atomic_store_rel_32(&vcpu->ioreq_pending, 1);
pause_vcpu(vcpu, VCPU_PAUSED);
return 0;
}
int acrn_insert_request_nowait(struct vcpu *vcpu, struct vhm_request *req)
{
struct vhm_request_buffer *req_buf;
long cur;
if (!vcpu || !req || !vcpu->vm->sw.req_buf)
return -1;
req_buf = (void *)gpa2hpa(vcpu->vm, vcpu->vm->sw.req_buf);
/* ACRN insert request to VHM and inject upcall */
cur = vcpu->vcpu_id;
req_buf->req_queue[cur] = *req;
req_buf->req_queue[cur].valid = true;
/* signal VHM and yield CPU */
fire_vhm_interrupt();
return 0;
}
static void _get_req_info_(struct vhm_request *req, int *id, char *type,
char *state, char *dir, long *addr, long *val)
{
strcpy_s(dir, 16, "NONE");
*addr = *val = 0;
*id = req->client;
switch (req->type) {
case REQ_PORTIO:
strcpy_s(type, 16, "PORTIO");
if (req->reqs.pio_request.direction == REQUEST_READ)
strcpy_s(dir, 16, "READ");
else
strcpy_s(dir, 16, "WRITE");
*addr = req->reqs.pio_request.address;
*val = req->reqs.pio_request.value;
break;
case REQ_MMIO:
case REQ_WP:
strcpy_s(type, 16, "MMIO/WP");
if (req->reqs.mmio_request.direction == REQUEST_READ)
strcpy_s(dir, 16, "READ");
else
strcpy_s(dir, 16, "WRITE");
*addr = req->reqs.mmio_request.address;
*val = req->reqs.mmio_request.value;
break;
break;
default:
strcpy_s(type, 16, "UNKNOWN");
}
switch (req->processed) {
case REQ_STATE_SUCCESS:
strcpy_s(state, 16, "SUCCESS");
break;
case REQ_STATE_PENDING:
strcpy_s(state, 16, "PENDING");
break;
case REQ_STATE_PROCESSING:
strcpy_s(state, 16, "PROCESS");
break;
case REQ_STATE_FAILED:
strcpy_s(state, 16, "FAILED");
break;
default:
strcpy_s(state, 16, "UNKNOWN");
}
}
int get_req_info(char *str, int str_max)
{
int i, len, size = str_max, client_id;
struct vhm_request_buffer *req_buf;
struct vhm_request *req;
char type[16], state[16], dir[16];
long addr, val;
struct list_head *pos;
struct vm *vm;
len = snprintf(str, size,
"\r\nVM\tVCPU\tCID\tTYPE\tSTATE\tDIR\tADDR\t\t\tVAL");
size -= len;
str += len;
spinlock_obtain(&vm_list_lock);
list_for_each(pos, &vm_list) {
vm = list_entry(pos, struct vm, list);
req_buf = (struct vhm_request_buffer *)vm->sw.req_buf;
if (req_buf) {
for (i = 0; i < VHM_REQUEST_MAX; i++) {
req = req_buf->req_queue + i;
if (req->valid) {
_get_req_info_(req, &client_id, type,
state, dir, &addr, &val);
len = snprintf(str, size,
"\r\n%d\t%d\t%d\t%s\t%s\t%s",
vm->attr.id, i, client_id, type,
state, dir);
size -= len;
str += len;
len = snprintf(str, size,
"\t0x%016llx\t0x%016llx",
addr, val);
size -= len;
str += len;
}
}
}
}
spinlock_release(&vm_list_lock);
snprintf(str, size, "\r\n");
return 0;
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include <schedule.h>
struct sched_context {
spinlock_t runqueue_lock;
struct list_head runqueue;
unsigned long need_scheduled;
struct vcpu *curr_vcpu;
spinlock_t scheduler_lock;
};
static DEFINE_CPU_DATA(struct sched_context, sched_ctx);
static unsigned long pcpu_used_bitmap;
void init_scheduler(void)
{
int i;
for (i = 0; i < phy_cpu_num; i++) {
spinlock_init(&per_cpu(sched_ctx, i).runqueue_lock);
spinlock_init(&per_cpu(sched_ctx, i).scheduler_lock);
INIT_LIST_HEAD(&per_cpu(sched_ctx, i).runqueue);
per_cpu(sched_ctx, i).need_scheduled = 0;
per_cpu(sched_ctx, i).curr_vcpu = NULL;
}
}
void get_schedule_lock(int pcpu_id)
{
spinlock_obtain(&per_cpu(sched_ctx, pcpu_id).scheduler_lock);
}
void release_schedule_lock(int pcpu_id)
{
spinlock_release(&per_cpu(sched_ctx, pcpu_id).scheduler_lock);
}
int allocate_pcpu(void)
{
int i;
for (i = 0; i < phy_cpu_num; i++) {
if (bitmap_test_and_set(i, &pcpu_used_bitmap) == 0) {
#ifdef CONFIG_EFI_STUB
efi_deferred_wakeup_pcpu(i);
#endif
return i;
}
}
return -1;
}
void set_pcpu_used(int pcpu_id)
{
bitmap_set(pcpu_id, &pcpu_used_bitmap);
}
void free_pcpu(int pcpu_id)
{
bitmap_clr(pcpu_id, &pcpu_used_bitmap);
}
void add_vcpu_to_runqueue(struct vcpu *vcpu)
{
int pcpu_id = vcpu->pcpu_id;
spinlock_obtain(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
if (list_empty(&vcpu->run_list))
list_add_tail(&vcpu->run_list,
&per_cpu(sched_ctx, pcpu_id).runqueue);
spinlock_release(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
}
void remove_vcpu_from_runqueue(struct vcpu *vcpu)
{
int pcpu_id = vcpu->pcpu_id;
spinlock_obtain(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
list_del_init(&vcpu->run_list);
spinlock_release(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
}
static struct vcpu *select_next_vcpu(int pcpu_id)
{
struct vcpu *vcpu = NULL;
spinlock_obtain(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
if (!list_empty(&per_cpu(sched_ctx, pcpu_id).runqueue)) {
vcpu = get_first_item(&per_cpu(sched_ctx, pcpu_id).runqueue,
struct vcpu, run_list);
}
spinlock_release(&per_cpu(sched_ctx, pcpu_id).runqueue_lock);
return vcpu;
}
void make_reschedule_request(struct vcpu *vcpu)
{
bitmap_set(NEED_RESCHEDULED,
&per_cpu(sched_ctx, vcpu->pcpu_id).need_scheduled);
send_single_ipi(vcpu->pcpu_id, VECTOR_NOTIFY_VCPU);
}
int need_rescheduled(int pcpu_id)
{
return bitmap_test_and_clear(NEED_RESCHEDULED,
&per_cpu(sched_ctx, pcpu_id).need_scheduled);
}
static void context_switch_out(struct vcpu *vcpu)
{
/* if it's idle thread, no action for switch out */
if (vcpu == NULL)
return;
atomic_store_rel_32(&vcpu->running, 0);
/* do prev vcpu context switch out */
/* For now, we don't need to invalid ept.
* But if we have more than one vcpu on one pcpu,
* we need add ept invalid operation here.
*/
}
static void context_switch_in(struct vcpu *vcpu)
{
/* update current_vcpu */
get_cpu_var(sched_ctx).curr_vcpu = vcpu;
/* if it's idle thread, no action for switch out */
if (vcpu == NULL)
return;
atomic_store_rel_32(&vcpu->running, 1);
/* FIXME:
* Now, we don't need to load new vcpu VMCS because
* we only do switch between vcpu loop and idle loop.
* If we have more than one vcpu on on pcpu, need to
* add VMCS load operation here.
*/
}
void default_idle(void)
{
int pcpu_id = get_cpu_id();
while (1) {
if (need_rescheduled(pcpu_id))
schedule();
else
__asm __volatile("pause" ::: "memory");
}
}
static void switch_to(struct vcpu *curr)
{
/*
* reset stack pointer here. Otherwise, schedule
* is recursive call and stack will overflow finally.
*/
uint64_t cur_sp = (uint64_t)&get_cpu_var(stack)[STACK_SIZE];
if (curr == NULL) {
asm volatile ("movq %1, %%rsp\n"
"movq $0, %%rdi\n"
"jmp *%0\n"
:
: "a"(default_idle), "r"(cur_sp)
: "memory");
} else {
asm volatile ("movq %2, %%rsp\n"
"movq %0, %%rdi\n"
"jmp *%1\n"
:
: "c"(curr), "a"(vcpu_thread), "r"(cur_sp)
: "memory");
}
}
void schedule(void)
{
int pcpu_id = get_cpu_id();
struct vcpu *next = NULL;
struct vcpu *prev = per_cpu(sched_ctx, pcpu_id).curr_vcpu;
get_schedule_lock(pcpu_id);
next = select_next_vcpu(pcpu_id);
if (prev == next) {
release_schedule_lock(pcpu_id);
return;
}
context_switch_out(prev);
context_switch_in(next);
release_schedule_lock(pcpu_id);
switch_to(next);
ASSERT(false, "Shouldn't go here");
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
void __stack_chk_fail(void)
{
ASSERT(0, "stack check fails in HV\n");
}

254
hypervisor/common/vm_load.c Normal file
View File

@@ -0,0 +1,254 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <bsp_extern.h>
#include <hv_debug.h>
struct zero_page {
uint8_t pad1[0x1e8]; /* 0x000 */
uint8_t e820_nentries; /* 0x1e8 */
uint8_t pad2[0x8]; /* 0x1e9 */
struct {
uint8_t setup_sects; /* 0x1f1 */
uint8_t hdr_pad1[0x1e]; /* 0x1f2 */
uint8_t loader_type; /* 0x210 */
uint8_t load_flags; /* 0x211 */
uint8_t hdr_pad2[0x6]; /* 0x212 */
uint32_t ramdisk_addr; /* 0x218 */
uint32_t ramdisk_size; /* 0x21c */
uint8_t hdr_pad3[0x8]; /* 0x220 */
uint32_t bootargs_addr; /* 0x228 */
uint8_t hdr_pad4[0x1c]; /* 0x22c */
uint32_t payload_offset;/* 0x248 */
uint32_t payload_length;/* 0x24c */
uint8_t hdr_pad5[0x18]; /* 0x250 */
} __packed hdr;
uint8_t pad3[0x68]; /* 0x268 */
struct e820_entry e820[0x80]; /* 0x2d0 */
uint8_t pad4[0x330]; /* 0xcd0 */
} __packed;
static uint32_t create_e820_table(struct e820_entry *_e820)
{
uint32_t i;
ASSERT(e820_entries > 0,
"e820 should be inited");
for (i = 0; i < e820_entries; i++) {
_e820[i].baseaddr = e820[i].baseaddr;
_e820[i].length = e820[i].length;
_e820[i].type = e820[i].type;
}
return e820_entries;
}
static uint64_t create_zero_page(struct vm *vm)
{
struct zero_page *zeropage;
struct sw_linux *sw_linux = &(vm->sw.linux_info);
struct zero_page *hva;
uint64_t gpa;
/* Set zeropage in Linux Guest RAM region just past boot args */
hva = GPA2HVA(vm, (uint64_t)sw_linux->bootargs_load_addr);
zeropage = (struct zero_page *)((char *)hva + MEM_4K);
/* clear the zeropage */
memset(zeropage, 0, MEM_2K);
/* copy part of the header into the zero page */
hva = GPA2HVA(vm, (uint64_t)vm->sw.kernel_info.kernel_load_addr);
memcpy_s(&(zeropage->hdr), sizeof(zeropage->hdr),
&(hva->hdr), sizeof(hva->hdr));
/* See if kernel has a RAM disk */
if (sw_linux->ramdisk_src_addr) {
/* Copy ramdisk load_addr and size in zeropage header structure
*/
zeropage->hdr.ramdisk_addr =
(uint32_t)(uint64_t)sw_linux->ramdisk_load_addr;
zeropage->hdr.ramdisk_size = (uint32_t)sw_linux->ramdisk_size;
}
/* Copy bootargs load_addr in zeropage header structure */
zeropage->hdr.bootargs_addr =
(uint32_t)(uint64_t)sw_linux->bootargs_load_addr;
/* set constant arguments in zero page */
zeropage->hdr.loader_type = 0xff;
zeropage->hdr.load_flags |= (1 << 5); /* quiet */
/* Create/add e820 table entries in zeropage */
zeropage->e820_nentries = create_e820_table(zeropage->e820);
/* Get the host physical address of the zeropage */
gpa = hpa2gpa(vm, HVA2HPA((uint64_t)zeropage));
/* Return Physical Base Address of zeropage */
return gpa;
}
int load_guest(struct vm *vm, struct vcpu *vcpu)
{
int ret = 0;
void *hva;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
uint64_t lowmem_gpa_top;
hva = GPA2HVA(vm, GUEST_CFG_OFFSET);
lowmem_gpa_top = *(uint64_t *)hva;
/* hardcode vcpu entry addr(kernel entry) & rsi (zeropage)*/
memset(cur_context->guest_cpu_regs.longs,
0, sizeof(uint64_t)*NUM_GPRS);
hva = GPA2HVA(vm, lowmem_gpa_top -
MEM_4K - MEM_2K);
vcpu->entry_addr = (void *)(*((uint64_t *)hva));
cur_context->guest_cpu_regs.regs.rsi =
lowmem_gpa_top - MEM_4K;
pr_info("%s, Set config according to predefined offset:",
__func__);
pr_info("VCPU%d Entry: 0x%llx, RSI: 0x%016llx, cr3: 0x%016llx",
vcpu->vcpu_id, vcpu->entry_addr,
cur_context->guest_cpu_regs.regs.rsi,
vm->arch_vm.guest_pml4);
return ret;
}
int general_sw_loader(struct vm *vm, struct vcpu *vcpu)
{
int ret = 0;
void *hva;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
char dyn_bootargs[100] = {0};
uint32_t kernel_entry_offset;
struct zero_page *zeropage;
ASSERT(vm != NULL, "Incorrect argument");
pr_dbg("Loading guest to run-time location");
/* FIXME: set config according to predefined offset */
if (!is_vm0(vm))
return load_guest(vm, vcpu);
/* calculate the kernel entry point */
zeropage = (struct zero_page *)
vm->sw.kernel_info.kernel_src_addr;
kernel_entry_offset = (zeropage->hdr.setup_sects + 1) * 512;
/* 64bit entry is the 512bytes after the start */
kernel_entry_offset += 512;
vm->sw.kernel_info.kernel_entry_addr =
(void *)((unsigned long)vm->sw.kernel_info.kernel_load_addr
+ kernel_entry_offset);
if (is_vcpu_bsp(vcpu)) {
/* Set VCPU entry point to kernel entry */
vcpu->entry_addr = vm->sw.kernel_info.kernel_entry_addr;
pr_info("%s, VM *d VCPU %d Entry: 0x%016llx ",
__func__, vm->attr.id, vcpu->vcpu_id, vcpu->entry_addr);
}
/* Calculate the host-physical address where the guest will be loaded */
hva = GPA2HVA(vm, (uint64_t)vm->sw.kernel_info.kernel_load_addr);
/* Copy the guest kernel image to its run-time location */
memcpy_s((void *)hva, vm->sw.kernel_info.kernel_size,
vm->sw.kernel_info.kernel_src_addr,
vm->sw.kernel_info.kernel_size);
/* See if guest is a Linux guest */
if (vm->sw.kernel_type == VM_LINUX_GUEST) {
/* Documentation states: ebx=0, edi=0, ebp=0, esi=ptr to
* zeropage
*/
memset(cur_context->guest_cpu_regs.longs,
0, sizeof(uint64_t) * NUM_GPRS);
/* Get host-physical address for guest bootargs */
hva = GPA2HVA(vm,
(uint64_t)vm->sw.linux_info.bootargs_load_addr);
/* Copy Guest OS bootargs to its load location */
strcpy_s((char *)hva, MEM_2K,
vm->sw.linux_info.bootargs_src_addr);
/* add "cma=XXXXM@0xXXXXXXXX" to cmdline*/
if (is_vm0(vm) && (e820_mem.max_ram_blk_size > 0)) {
snprintf(dyn_bootargs, 100, " cma=%dM@0x%llx\n",
(e820_mem.max_ram_blk_size >> 20),
e820_mem.max_ram_blk_base);
/* Delete '\n' at the end of cmdline */
strcpy_s((char *)hva
+vm->sw.linux_info.bootargs_size - 1,
100, dyn_bootargs);
}
/* Check if a RAM disk is present with Linux guest */
if (vm->sw.linux_info.ramdisk_src_addr) {
/* Get host-physical address for guest RAM disk */
hva = GPA2HVA(vm,
(uint64_t)vm->sw.linux_info.ramdisk_load_addr);
/* Copy RAM disk to its load location */
memcpy_s((void *)hva, vm->sw.linux_info.ramdisk_size,
vm->sw.linux_info.ramdisk_src_addr,
vm->sw.linux_info.ramdisk_size);
}
/* Create Zeropage and copy Physical Base Address of Zeropage
* in RSI
*/
cur_context->guest_cpu_regs.regs.rsi = create_zero_page(vm);
pr_info("%s, RSI pointing to zero page for VM %d at GPA %X",
__func__, vm->attr.id,
cur_context->guest_cpu_regs.regs.rsi);
} else {
pr_err("%s, Loading VM SW failed", __func__);
ret = -EINVAL;
}
return ret;
}

236
hypervisor/debug/console.c Normal file
View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "serial_internal.h"
static spinlock_t lock;
static uint32_t serial_handle = SERIAL_INVALID_HANDLE;
#define CONSOLE_KICK_TIMER_TIMEOUT 40 /* timeout is 40ms*/
uint32_t get_serial_handle(void)
{
return serial_handle;
}
static int print_char(char x)
{
serial_puts(serial_handle, &x, 1);
if (x == '\n')
serial_puts(serial_handle, "\r", 1);
return 0;
}
int console_init(void)
{
spinlock_init(&lock);
serial_handle = serial_open("STDIO");
return 0;
}
int console_putc(int ch)
{
int res = -1;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE)
res = print_char(ch);
spinlock_release(&lock);
return res;
}
int console_puts(const char *s)
{
int res = -1;
const char *p;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
res = 0;
while (*s) {
/* start output at the beginning of the string search
* for end of string or '\n'
*/
p = s;
while (*p && *p != '\n')
++p;
/* write all characters up to p */
serial_puts(serial_handle, s, p - s);
res += p - s;
if (*p == '\n') {
print_char('\n');
++p;
res += 2;
}
/* continue at position p */
s = p;
}
}
spinlock_release(&lock);
return res;
}
int console_write(const char *s, size_t len)
{
int res = -1;
const char *e;
const char *p;
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
/* calculate pointer to the end of the string */
e = s + len;
res = 0;
/* process all characters */
while (s != e) {
/* search for '\n' or the end of the string */
p = s;
while ((p != e) && (*p != '\n'))
++p;
/* write all characters processed so far */
serial_puts(serial_handle, s, p - s);
res += p - s;
/* write '\n' if end of string is not reached */
if (p != e) {
print_char('\n');
++p;
res += 2;
}
/* continue at next position */
s = p;
}
}
spinlock_release(&lock);
return res;
}
void console_dump_bytes(const void *p, unsigned int len)
{
const unsigned char *x = p;
const unsigned char *e = x + len;
int i;
/* dump all bytes */
while (x < e) {
/* write the address of the first byte in the row */
printf("%08x: ", (vaddr_t) x);
/* print one row (16 bytes) as hexadecimal values */
for (i = 0; i < 16; i++)
printf("%02x ", x[i]);
/* print one row as ASCII characters (if possible) */
for (i = 0; i < 16; i++) {
if ((x[i] < ' ') || (x[i] >= 127))
console_putc('.');
else
console_putc(x[i]);
}
/* continue with next row */
console_putc('\n');
/* set pointer one row ahead */
x += 16;
}
}
static void console_read(void)
{
spinlock_obtain(&lock);
if (serial_handle != SERIAL_INVALID_HANDLE) {
/* Get all the data available in the RX FIFO */
serial_get_rx_data(serial_handle);
}
spinlock_release(&lock);
}
static void console_handler(void)
{
/* Dump the RX FIFO to a circular buffer */
console_read();
/* serial Console Rx operation */
vuart_console_rx_chars(serial_handle);
/* serial Console Tx operation */
vuart_console_tx_chars();
shell_kick_session();
}
static int console_timer_callback(__unused uint64_t data)
{
/* Kick HV-Shell and Uart-Console tasks */
console_handler();
/* Restart the timer */
console_setup_timer();
return 0;
}
void console_setup_timer(void)
{
/* Start an one-shot timer */
if (add_timer(console_timer_callback, 0,
rdtsc() + TIME_MS_DELTA * CONSOLE_KICK_TIMER_TIMEOUT) < 0)
pr_err("Failed to add console kick timer");
}

368
hypervisor/debug/dump.c Normal file
View File

@@ -0,0 +1,368 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
/*
* readable exception descriptors.
*/
static const char *const excp_names[] = {
[0] = "Divide Error",
[1] = "RESERVED",
[2] = "NMI",
[3] = "Breakpoint",
[4] = "Overflow",
[5] = "BOUND range exceeded",
[6] = "Invalid Opcode",
[7] = "Device Not Available",
[8] = "Double Fault",
[9] = "Coprocessor Segment Overrun",
[10] = "Invalid TSS",
[11] = "Segment Not Present",
[12] = "Stack Segment Fault",
[13] = "General Protection",
[14] = "Page Fault",
[15] = "Intel Reserved",
[16] = "x87 FPU Floating Point Error",
[17] = "Alignment Check",
[18] = "Machine Check",
[19] = "SIMD Floating Point Exception",
[20] = "Virtualization Exception",
[21] = "Intel Reserved",
[22] = "Intel Reserved",
[23] = "Intel Reserved",
[24] = "Intel Reserved",
[25] = "Intel Reserved",
[26] = "Intel Reserved",
[27] = "Intel Reserved",
[28] = "Intel Reserved",
[29] = "Intel Reserved",
[30] = "Intel Reserved",
[31] = "Intel Reserved"
};
static void dump_guest_reg(struct vcpu *vcpu)
{
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
printf("\n\n================================================");
printf("================================\n\n");
printf("Guest Registers:\r\n");
printf("= VM ID %d ==== vCPU ID %d === pCPU ID %d ===="
"world %d =============\r\n",
vcpu->vm->attr.id, vcpu->vcpu_id, vcpu->pcpu_id,
vcpu->arch_vcpu.cur_context);
printf("= RIP=0x%016llx RSP=0x%016llx "
"RFLAGS=0x%016llx\r\n",
cur_context->rip,
cur_context->rsp,
cur_context->rflags);
printf("= CR0=0x%016llx CR2=0x%016llx "
" CR3=0x%016llx\r\n",
cur_context->cr0,
cur_context->cr2,
cur_context->cr3);
printf("= RAX=0x%016llx RBX=0x%016llx "
"RCX=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rax,
cur_context->guest_cpu_regs.regs.rbx,
cur_context->guest_cpu_regs.regs.rcx);
printf("= RDX=0x%016llx RDI=0x%016llx "
"RSI=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rdx,
cur_context->guest_cpu_regs.regs.rdi,
cur_context->guest_cpu_regs.regs.rsi);
printf("= RBP=0x%016llx R8=0x%016llx "
"R9=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.rbp,
cur_context->guest_cpu_regs.regs.r8,
cur_context->guest_cpu_regs.regs.r9);
printf("= R10=0x%016llx R11=0x%016llx "
"R12=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.r10,
cur_context->guest_cpu_regs.regs.r11,
cur_context->guest_cpu_regs.regs.r12);
printf("= R13=0x%016llx R14=0x%016llx "
"R15=0x%016llx\r\n",
cur_context->guest_cpu_regs.regs.r13,
cur_context->guest_cpu_regs.regs.r14,
cur_context->guest_cpu_regs.regs.r15);
printf("\r\n");
}
static void dump_guest_stack(struct vcpu *vcpu)
{
uint64_t gpa;
uint64_t hpa;
uint32_t i;
uint64_t *tmp;
uint64_t page1_size;
uint64_t page2_size;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
gpa = gva2gpa(vcpu->vm, cur_context->cr3, cur_context->rsp);
hpa = gpa2hpa(vcpu->vm, gpa);
printf("\r\nGuest Stack:\r\n");
printf("Dump stack for vcpu %d, from gva 0x%016llx ->"
"gpa 0x%016llx -> hpa 0x%016llx \r\n",
vcpu->vcpu_id, cur_context->rsp, gpa, hpa);
/* Need check if cross 2 pages*/
if (((cur_context->rsp % CPU_PAGE_SIZE) + DUMP_STACK_SIZE)
<= CPU_PAGE_SIZE) {
tmp = HPA2HVA(hpa);
for (i = 0; i < DUMP_STACK_SIZE/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx "
"0x%016llx 0x%016llx\r\n", (hpa+i*32),
tmp[i*4], tmp[i*4+1],
tmp[i*4+2], tmp[i*4+3]);
}
} else {
tmp = HPA2HVA(hpa);
page1_size = CPU_PAGE_SIZE
- (cur_context->rsp % CPU_PAGE_SIZE);
for (i = 0; i < page1_size/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (hpa+i*32), tmp[i*4],
tmp[i*4+1], tmp[i*4+2], tmp[i*4+3]);
}
gpa = gva2gpa(vcpu->vm, cur_context->cr3,
cur_context->rsp + page1_size);
hpa = gpa2hpa(vcpu->vm, gpa);
printf("Dump stack for vcpu %d, from gva 0x%016llx ->"
"gpa 0x%016llx -> hpa 0x%016llx \r\n",
vcpu->vcpu_id, cur_context->rsp + page1_size,
gpa, hpa);
tmp = HPA2HVA(hpa);
page2_size = DUMP_STACK_SIZE - page1_size;
for (i = 0; i < page2_size/32; i++) {
printf("addr(0x%llx): 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (hpa+i*32), tmp[i*4],
tmp[i*4+1], tmp[i*4+2], tmp[i*4+3]);
}
}
printf("\r\n");
}
static void show_guest_call_trace(struct vcpu *vcpu)
{
uint64_t gpa;
uint64_t hpa;
uint64_t *hva;
uint64_t bp;
uint64_t count = 0;
struct run_context *cur_context =
&vcpu->arch_vcpu.contexts[vcpu->arch_vcpu.cur_context];
bp = cur_context->guest_cpu_regs.regs.rbp;
printf("Guest Call Trace: **************************************\r\n");
printf("Maybe the call trace is not accurate, pls check stack!!\r\n");
/* if enable compiler option(no-omit-frame-pointer) the stack layout
* should be like this when call a function for x86_64
*
* | |
* rbp+8 | return address |
* rbp | rbp | push rbp
* | | mov rsp rbp
*
* rsp | |
*
* try to print out call trace,here can not check if the rbp is valid
* if the address is invalid, it will cause hv page fault
* then halt system */
while ((count++ < CALL_TRACE_HIERARCHY_MAX) && (bp != 0)) {
gpa = gva2gpa(vcpu->vm, cur_context->cr3, bp);
hpa = gpa2hpa(vcpu->vm, gpa);
hva = HPA2HVA(hpa);
printf("BP_GVA(0x%016llx)->BP_GPA(0x%016llx)"
"->BP_HPA(0x%016llx) RIP=0x%016llx\r\n", bp, gpa, hpa,
*(uint64_t *)((uint64_t)hva + sizeof(uint64_t)));
/* Get previous rbp*/
bp = *hva;
}
printf("\r\n");
}
static void dump_guest_context(uint32_t cpu_id)
{
struct vcpu *vcpu;
vcpu = per_cpu(vcpu, cpu_id);
if (vcpu != NULL) {
dump_guest_reg(vcpu);
dump_guest_stack(vcpu);
show_guest_call_trace(vcpu);
}
}
static void show_host_call_trace(uint64_t rsp, uint64_t rbp, uint32_t cpu_id)
{
int i = 0;
int cb_hierarchy = 0;
uint64_t *sp = (uint64_t *)rsp;
printf("\r\nHost Stack: \r\n");
for (i = 0; i < DUMP_STACK_SIZE/32; i++) {
printf("addr(0x%llx) 0x%016llx 0x%016llx 0x%016llx "
"0x%016llx\r\n", (rsp+i*32), sp[i*4], sp[i*4+1],
sp[i*4+2], sp[i*4+3]);
}
printf("\r\n");
printf("Host Call Trace:\r\n");
if (rsp >
(uint64_t)&per_cpu(stack, cpu_id)[STACK_SIZE - 1]
|| rsp < (uint64_t)&per_cpu(stack, cpu_id)[0]) {
return;
}
/* if enable compiler option(no-omit-frame-pointer) the stack layout
* should be like this when call a function for x86_64
*
* | |
* rbp+8 | return address |
* rbp | rbp | push rbp
* | | mov rsp rbp
*
* rsp | |
*
*
* if the address is invalid, it will cause hv page fault
* then halt system */
while ((rbp <=
(uint64_t)&per_cpu(stack, cpu_id)[STACK_SIZE - 1])
&& (rbp >= (uint64_t)&per_cpu(stack, cpu_id)[0])
&& (cb_hierarchy++ < CALL_TRACE_HIERARCHY_MAX)) {
printf("----> 0x%016llx\r\n",
*(uint64_t *)(rbp + sizeof(uint64_t)));
if (*(uint64_t *)(rbp + 2*sizeof(uint64_t))
== SP_BOTTOM_MAGIC) {
break;
}
rbp = *(uint64_t *)rbp;
}
printf("\r\n");
}
void __assert(uint32_t line, const char *file, char *txt)
{
uint32_t cpu_id = get_cpu_id();
uint64_t rsp = cpu_rsp_get();
uint64_t rbp = cpu_rbp_get();
pr_fatal("Assertion failed in file %s,line %u : %s",
file, line, txt);
show_host_call_trace(rsp, rbp, cpu_id);
dump_guest_context(cpu_id);
do {
asm volatile ("pause" ::: "memory");
} while (1);
}
void dump_exception(struct intr_ctx *ctx, uint32_t cpu_id)
{
const char *name = "Not defined";
static int nested = 1;
/* avoid endless loop, only dump the first exception */
if (nested++ > 1)
return;
if (ctx->vector < 0x20)
name = excp_names[ctx->vector];
printf("\n\n================================================");
printf("================================\n=\n");
printf("= Unhandled exception: %d (%s)\n", ctx->vector, name);
printf("= CPU ID = %d", cpu_id);
/* Dump host register*/
printf("\r\nHost Registers:\r\n");
printf("= Vector=0x%016llX RIP=0x%016llX\n",
ctx->vector, ctx->rip);
printf("= RAX=0x%016llX RBX=0x%016llX RCX=0x%016llX\n",
ctx->rax, ctx->rbx, ctx->rcx);
printf("= RDX=0x%016llX RDI=0x%016llX RSI=0x%016llX\n",
ctx->rdx, ctx->rdi, ctx->rsi);
printf("= RSP=0x%016llX RBP=0x%016llX RBX=0x%016llX\n",
ctx->rsp, ctx->rbp, ctx->rbx);
printf("= R8=0x%016llX R9=0x%016llX R10=0x%016llX\n",
ctx->r8, ctx->r9, ctx->r10);
printf("= R11=0x%016llX R12=0x%016llX R13=0x%016llX\n",
ctx->r11, ctx->r12, ctx->r13);
printf("= RFLAGS=0x%016llX R14=0x%016llX R15=0x%016llX\n",
ctx->rflags, ctx->r14, ctx->r15);
printf("= ERRCODE=0x%016llX CS=0x%016llX SS=0x%016llX\n",
ctx->error_code, ctx->cs, ctx->ss);
printf("\r\n");
/* Dump host stack */
show_host_call_trace(ctx->rsp, ctx->rbp, cpu_id);
/* Dump guest context */
dump_guest_context(cpu_id);
printf("= System halted\n");
printf("=====================================================");
printf("===========================\n");
}
void dump_interrupt(struct intr_ctx *ctx)
{
printf("\n\n==========================================");
printf("======================================\n=\n");
printf("\n=\n");
printf("= Vector=0x%016llX RIP=0x%016llX\n",
ctx->vector, ctx->rip);
printf("= RAX=0x%016llX RBX=0x%016llX RCX=0x%016llX\n",
ctx->rax, ctx->rbx, ctx->rcx);
printf("= RDX=0x%016llX RDI=0x%016llX RSI=0x%016llX\n",
ctx->rdx, ctx->rdi, ctx->rsi);
printf("= RSP=0x%016llX RBP=0x%016llX RBX=0x%016llX\n",
ctx->rsp, ctx->rbp, ctx->rbx);
printf("= R8=0x%016llX R9=0x%016llX R10=0x%016llX\n",
ctx->r8, ctx->r9, ctx->r10);
printf("= R11=0x%016llX R12=0x%016llX R13=0x%016llX\n",
ctx->r11, ctx->r12, ctx->r13);
printf("= RFLAGS=0x%016llX R14=0x%016llX R15=0x%016llX\n",
ctx->rflags, ctx->r14, ctx->r15);
printf("= ERRCODE=0x%016llX CS=0x%016llX SS=0x%016llX\n",
ctx->error_code, ctx->cs, ctx->ss);
printf("=\n");
printf("= system halted\n");
printf("===============================================");
printf("=================================\n");
}

161
hypervisor/debug/logmsg.c Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#define LOG_ENTRY_SIZE 80
/* Size of buffer used to store a message being logged,
* should align to LOG_ENTRY_SIZE.
*/
#define LOG_MESSAGE_MAX_SIZE (4 * LOG_ENTRY_SIZE)
DEFINE_CPU_DATA(char [LOG_MESSAGE_MAX_SIZE], logbuf);
struct logmsg {
uint32_t flags;
unsigned int seq;
spinlock_t lock;
};
static struct logmsg logmsg;
void init_logmsg(__unused uint32_t mem_size, uint32_t flags)
{
logmsg.flags = flags;
logmsg.seq = 0;
}
void do_logmsg(uint32_t severity, const char *fmt, ...)
{
va_list args;
uint64_t timestamp;
uint32_t cpu_id;
bool do_console_log;
bool do_mem_log;
char *buffer;
spinlock_rflags;
do_console_log = ((logmsg.flags & LOG_FLAG_STDOUT) &&
(severity <= console_loglevel));
do_mem_log = ((logmsg.flags & LOG_FLAG_MEMORY) &&
(severity <= mem_loglevel));
if (!do_console_log && !do_mem_log)
return;
/* Get time-stamp value */
timestamp = rdtsc();
/* Scale time-stamp appropriately */
timestamp = TICKS_TO_US(timestamp);
/* Get CPU ID */
cpu_id = get_cpu_id();
buffer = per_cpu(logbuf, cpu_id);
memset(buffer, 0, LOG_MESSAGE_MAX_SIZE);
/* Put time-stamp, CPU ID and severity into buffer */
snprintf(buffer, LOG_MESSAGE_MAX_SIZE,
"[%lluus][cpu=%u][sev=%u][seq=%u]:",
timestamp, cpu_id, severity,
atomic_inc_return(&logmsg.seq));
/* Put message into remaining portion of local buffer */
va_start(args, fmt);
vsnprintf(buffer + strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE),
LOG_MESSAGE_MAX_SIZE
- strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE), fmt, args);
va_end(args);
/* Check if flags specify to output to stdout */
if (do_console_log) {
spinlock_irqsave_obtain(&(logmsg.lock));
/* Send buffer to stdout */
printf("%s\n\r", buffer);
spinlock_irqrestore_release(&(logmsg.lock));
}
/* Check if flags specify to output to memory */
if (do_mem_log) {
int i, msg_len;
struct shared_buf *sbuf = (struct shared_buf *)
per_cpu(sbuf, cpu_id)[ACRN_HVLOG];
if (sbuf != NULL) {
msg_len = strnlen_s(buffer, LOG_MESSAGE_MAX_SIZE);
for (i = 0; i < (msg_len - 1) / LOG_ENTRY_SIZE + 1;
i++) {
sbuf_put(sbuf, (uint8_t *)buffer +
i * LOG_ENTRY_SIZE);
}
}
}
}
void print_logmsg_buffer(uint32_t cpu_id)
{
spinlock_rflags;
char buffer[LOG_ENTRY_SIZE + 1];
int read_cnt;
struct shared_buf *sbuf;
if (cpu_id >= (uint32_t)phy_cpu_num)
return;
sbuf = (struct shared_buf *)per_cpu(sbuf, cpu_id)[ACRN_HVLOG];
if (sbuf != NULL) {
spinlock_irqsave_obtain(&(logmsg.lock));
printf("CPU%d: head: 0x%x, tail: 0x%x\n\r",
cpu_id, sbuf->head, sbuf->tail);
spinlock_irqrestore_release(&(logmsg.lock));
do {
memset(buffer, 0, LOG_ENTRY_SIZE + 1);
read_cnt = sbuf_get(sbuf, (uint8_t *)buffer);
if (read_cnt > 0) {
uint32_t idx;
idx = (read_cnt < LOG_ENTRY_SIZE) ?
read_cnt : LOG_ENTRY_SIZE;
buffer[idx] = '\0';
spinlock_irqsave_obtain(&(logmsg.lock));
printf("%s\n\r", buffer);
spinlock_irqrestore_release(&(logmsg.lock));
}
} while (read_cnt > 0);
}
}

824
hypervisor/debug/printf.c Normal file
View File

@@ -0,0 +1,824 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#ifndef NULL
#define NULL ((void *) 0)
#endif
#define PRINT_STRING_MAX_LEN 4096
/** Command for the emit function: copy string to output. */
#define PRINT_CMD_COPY 0x00000000
/** Command for the emit function: fill output with first character. */
#define PRINT_CMD_FILL 0x00000001
/** Use upper case letters for hexadecimal format. */
#define PRINT_FLAG_UPPER 0x00000001
/** Use alternate form. */
#define PRINT_FLAG_ALTERNATE_FORM 0x00000002
/** Use '0' instead of ' ' for padding. */
#define PRINT_FLAG_PAD_ZERO 0x00000004
/** Use left instead of right justification. */
#define PRINT_FLAG_LEFT_JUSTIFY 0x00000008
/** Always use the sign as prefix. */
#define PRINT_FLAG_SIGN 0x00000010
/** Use ' ' as prefix if no sign is used. */
#define PRINT_FLAG_SPACE 0x00000020
/** The original value was a (unsigned) char. */
#define PRINT_FLAG_CHAR 0x00000040
/** The original value was a (unsigned) short. */
#define PRINT_FLAG_SHORT 0x00000080
/** The original value was a (unsigned) long. */
#define PRINT_FLAG_LONG 0x00000100
/** The original value was a (unsigned) long long. */
#define PRINT_FLAG_LONG_LONG 0x00000200
/** The value is interpreted as unsigned. */
#define PRINT_FLAG_UINT32 0x00000400
/** Structure used to parse parameters and variables to subroutines. */
struct print_param {
/** A pointer to the function that is used to emit characters. */
int (*emit)(int, const char *, int, void *);
/** An opaque pointer that is passed as third argument to the emit
* function.
*/
void *data;
/** Contains variables which are recalculated for each argument. */
struct {
/** A bitfield with the parsed format flags. */
int flags;
/** The parsed format width. */
int width;
/** The parsed format precision. */
int precision;
/** The bitmask for unsigned values. */
unsigned long long mask;
/** A pointer to the preformated value. */
const char *value;
/* The number of characters in the preformated value buffer. */
uint32_t valuelen;
/** A pointer to the values prefix. */
const char *prefix;
/** The number of characters in the prefix buffer. */
uint32_t prefixlen;
} vars;
};
/** Structure used to save (v)snprintf() specific values */
struct snprint_param {
/** The destination buffer. */
char *dst;
/** The size of the destination buffer. */
int sz;
/** Counter for written chars. */
int wrtn;
};
/** The characters to use for upper case hexadecimal conversion.
*
* Note that this array is 17 bytes long. The first 16 characters
* are used to convert a 4 bit number to a printable character.
* The last character is used to determine the prefix for the
* alternate form.
*/
static const char upper_hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'X'
};
/** The characters to use for lower case hexadecimal conversion.
*
* Note that this array is 17 bytes long. The first 16 characters
* are used to convert a 4 bit number to a printable character.
* The last character is used to determine the prefix for the
* alternate form.
*/
static const char lower_hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'x'
};
static const char *get_int(const char *s, int *x)
{
int negative = 0;
*x = 0;
/* evaluate leading '-' for negative numbers */
if (*s == '-') {
negative = 1;
++s;
}
/* parse uint32_teger */
while ((*s >= '0') && (*s <= '9'))
*x = *x * 10 + (*s++ - '0');
/* apply sign to result */
if (negative)
*x = -*x;
return s;
}
static const char *get_flags(const char *s, int *flags)
{
/* contains the flag characters */
static const char flagchars[] = "#0- +";
/* contains the numeric flags for the characters above */
static const int fl[sizeof(flagchars)] = {
PRINT_FLAG_ALTERNATE_FORM, /* # */
PRINT_FLAG_PAD_ZERO, /* 0 */
PRINT_FLAG_LEFT_JUSTIFY, /* - */
PRINT_FLAG_SIGN, /* + */
PRINT_FLAG_SPACE /* ' ' */
};
const char *pos;
/* parse multiple flags */
while (*s) {
/* get index of flag. Terminate loop if no flag character was
* found
*/
pos = strchr(flagchars, *s);
if (pos == 0)
break;
/* apply matching flags and continue with the next character */
++s;
*flags |= fl[pos - flagchars];
}
/* Spec says that '-' has a higher priority than '0' */
if (*flags & PRINT_FLAG_LEFT_JUSTIFY)
*flags &= ~PRINT_FLAG_PAD_ZERO;
/* Spec says that '+' has a higher priority than ' ' */
if (*flags & PRINT_FLAG_SIGN)
*flags &= ~PRINT_FLAG_SPACE;
return s;
}
static const char *get_length_modifier(const char *s,
int *flags, unsigned long long *mask)
{
/* check for h[h] (char/short) */
if (*s == 'h') {
if (*++s == 'h') {
*flags |= PRINT_FLAG_CHAR;
*mask = 0x000000FF;
++s;
} else {
*flags |= PRINT_FLAG_SHORT;
*mask = 0x0000FFFF;
}
}
/* check for l[l] (long/long long) */
else if (*s == 'l') {
if (*++s == 'l') {
*flags |= PRINT_FLAG_LONG_LONG;
++s;
} else
*flags |= PRINT_FLAG_LONG;
}
return s;
}
static int format_number(struct print_param *param)
{
/* contains the character used for padding */
char pad;
/* effective width of the result */
uint32_t width;
/* number of characters to insert for width (w) and precision (p) */
uint32_t p, w;
/* the result */
int res;
/* initialize variables */
p = w = 0;
res = 0;
width = param->vars.valuelen + param->vars.prefixlen;
/* calculate additional characters for precision */
if ((uint32_t)(param->vars.precision) > width)
p = param->vars.precision - width;
/* calculate additional characters for width */
if ((uint32_t)(param->vars.width) > (width + p))
w = param->vars.width - (width + p);
/* handle case of right justification */
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) == 0) {
/* assume ' ' as padding character */
pad = ' ';
/*
* if padding with 0 is used, we have to emit the prefix (if any
* ) first to achieve the expected result. However, if a blank is
* used for padding, the prefix is emitted after the padding.
*/
if (param->vars.flags & PRINT_FLAG_PAD_ZERO) {
/* use '0' for padding */
pad = '0';
/* emit prefix, return early if an error occurred */
res = param->emit(PRINT_CMD_COPY, param->vars.prefix,
param->vars.prefixlen, param->data);
if (param->vars.prefix && (res < 0))
return res;
/* invalidate prefix */
param->vars.prefix = 0;
param->vars.prefixlen = 0;
}
/* fill the width with the padding character, return early if
* an error occurred
*/
res = param->emit(PRINT_CMD_FILL, &pad, w, param->data);
if (res < 0)
return res;
}
/* emit prefix (if any), return early in case of an error */
res = param->emit(PRINT_CMD_COPY, param->vars.prefix,
param->vars.prefixlen, param->data);
if (param->vars.prefix && (res < 0))
return res;
/* insert additional 0's for precision, return early if an error
* occurred
*/
res = param->emit(PRINT_CMD_FILL, "0", p, param->data);
if (res < 0)
return res;
/* emit the pre-calculated result, return early in case of an error */
res = param->emit(PRINT_CMD_COPY, param->vars.value,
param->vars.valuelen, param->data);
if (res < 0)
return res;
/* handle left justification */
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) != 0) {
/* emit trailing blanks, return early in case of an error */
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
/* done, return the last result */
return res;
}
static int print_pow2(struct print_param *param,
unsigned long long v, uint32_t shift)
{
/* max buffer required for octal representation of unsigned long long */
char digitbuff[22];
/* Insert position for the next character+1 */
char *pos = digitbuff + sizeof(digitbuff);
/* buffer for the 0/0x/0X prefix */
char prefix[2];
/* pointer to the digits translation table */
const char *digits;
/* mask to extract next character */
unsigned long long mask;
int ret;
/* calculate mask */
mask = (1ULL << shift) - 1;
/* determine digit translation table */
digits = (param->vars.flags & PRINT_FLAG_UPPER) ?
upper_hex_digits : lower_hex_digits;
/* apply mask for short/char */
v &= param->vars.mask;
/* determine prefix for alternate form */
if ((v == 0) && (param->vars.flags & PRINT_FLAG_ALTERNATE_FORM)) {
prefix[0] = '0';
param->vars.prefix = prefix;
param->vars.prefixlen = 1;
if (shift == 4) {
param->vars.prefixlen = 2;
prefix[1] = digits[16];
}
}
/* determine digits from right to left */
do {
*--pos = digits[(v & mask)];
} while (v >>= shift);
/* assign parameter and apply width and precision */
param->vars.value = pos;
param->vars.valuelen = digitbuff + sizeof(digitbuff) - pos;
ret = format_number(param);
param->vars.value = NULL;
param->vars.valuelen = 0;
return ret;
}
static int print_decimal(struct print_param *param, long long value)
{
/* max. required buffer for unsigned long long in decimal format */
char digitbuff[20];
/* pointer to the next character position (+1) */
char *pos = digitbuff + sizeof(digitbuff);
/* current value in 32/64 bit */
union u_qword v;
/* next value in 32/64 bit */
union u_qword nv;
/* helper union for division result */
struct udiv_result d;
int ret;
/* assume an unsigned 64 bit value */
v.qword = ((unsigned long long)value) & param->vars.mask;
/*
* assign sign and correct value if value is negative and
* value must be interpreted as signed
*/
if (((param->vars.flags & PRINT_FLAG_UINT32) == 0) && (value < 0)) {
v.qword = (unsigned long long)-value;
param->vars.prefix = "-";
param->vars.prefixlen = 1;
}
/* determine sign if explicit requested in the format string */
if (!param->vars.prefix) {
if (param->vars.flags & PRINT_FLAG_SIGN) {
param->vars.prefix = "+";
param->vars.prefixlen = 1;
} else if (param->vars.flags & PRINT_FLAG_SPACE) {
param->vars.prefix = " ";
param->vars.prefixlen = 1;
}
}
/* process 64 bit value as long as needed */
while (v.dwords.high != 0) {
/* determine digits from right to left */
udiv64(v.qword, 10, &d);
*--pos = d.r.dwords.low + '0';
v.qword = d.q.qword;
}
/* process 32 bit (or reduced 64 bit) value */
do {
/* determine digits from right to left. The compiler should be
* able to handle a division and multiplication by the constant
* 10.
*/
nv.dwords.low = v.dwords.low / 10;
*--pos = (v.dwords.low - (10 * nv.dwords.low)) + '0';
} while ((v.dwords.low = nv.dwords.low) != 0);
/* assign parameter and apply width and precision */
param->vars.value = pos;
param->vars.valuelen = digitbuff + sizeof(digitbuff) - pos;
ret = format_number(param);
param->vars.value = NULL;
param->vars.valuelen = 0;
return ret;
}
static int print_string(struct print_param *param, const char *s)
{
/* the length of the string (-1) if unknown */
int len;
/* the number of additional characters to insert to reach the required
* width
*/
uint32_t w;
/* the last result of the emit function */
int res;
w = 0;
len = -1;
/* we need the length of the string if either width or precision is
* given
*/
if (param->vars.precision || param->vars.width)
len = strnlen_s(s, PRINT_STRING_MAX_LEN);
/* precision gives the max. number of characters to emit. */
if (param->vars.precision && (len > param->vars.precision))
len = param->vars.precision;
/* calculate the number of additional characters to get the required
* width
*/
if (param->vars.width > 0 && param->vars.width > len)
w = param->vars.width - len;
/* emit additional characters for width, return early if an error
* occurred
*/
if ((param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) == 0) {
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
/* emit the string, return early if an error occurred */
res = param->emit(PRINT_CMD_COPY, s, len, param->data);
if (res < 0)
return res;
/* emit additional characters on the right, return early if an error
* occurred
*/
if (param->vars.flags & PRINT_FLAG_LEFT_JUSTIFY) {
res = param->emit(PRINT_CMD_FILL, " ", w, param->data);
if (res < 0)
return res;
}
return res;
}
static int do_print(const char *fmt, struct print_param *param,
__builtin_va_list args)
{
/* the result of this function */
int res = 0;
/* temp. storage for the next character */
char ch;
/* temp. pointer to the start of an analysed character sequence */
const char *start;
/* main loop: analyse until there are no more characters */
while (*fmt) {
/* mark the current position and search the next '%' */
start = fmt;
while (*fmt && (*fmt != '%'))
fmt++;
/*
* pass all characters until the next '%' to the emit function.
* Return early if the function fails
*/
res = param->emit(PRINT_CMD_COPY, start, fmt - start,
param->data);
if (res < 0)
return res;
/* continue only if the '%' character was found */
if (*fmt == '%') {
/* mark current position in the format string */
start = fmt++;
/* initialize the variables for the next argument */
memset(&(param->vars), 0, sizeof(param->vars));
param->vars.mask = 0xFFFFFFFFFFFFFFFFULL;
/*
* analyze the format specification:
* - get the flags
* - get the width
* - get the precision
* - get the length modifier
*/
fmt = get_flags(fmt, &(param->vars.flags));
fmt = get_int(fmt, &(param->vars.width));
if (*fmt == '.') {
fmt++;
fmt = get_int(fmt, &(param->vars.precision));
if (param->vars.precision < 0)
param->vars.precision = 0;
}
fmt = get_length_modifier(fmt, &(param->vars.flags),
&(param->vars.mask));
ch = *fmt++;
/* a single '%'? => print out a single '%' */
if (ch == '%') {
res = param->emit(PRINT_CMD_COPY, &ch, 1,
param->data);
}
/* decimal number */
else if ((ch == 'd') || (ch == 'i')) {
res = print_decimal(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
long long)
: (long long)
__builtin_va_arg(args,
int));
}
/* unsigned decimal number */
else if (ch == 'u') {
param->vars.flags |= PRINT_FLAG_UINT32;
res = print_decimal(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
unsigned int));
}
/* octal number */
else if (ch == 'o') {
res = print_pow2(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
uint32_t),
3);
}
/* hexadecimal number */
else if ((ch == 'X') || (ch == 'x')) {
if (ch == 'X')
param->vars.flags |= PRINT_FLAG_UPPER;
res = print_pow2(param,
(param->vars.flags &
PRINT_FLAG_LONG_LONG) ?
__builtin_va_arg(args,
unsigned long long)
: (unsigned long long)
__builtin_va_arg(args,
uint32_t),
4);
}
/* string argument */
else if (ch == 's') {
const char *s = __builtin_va_arg(args, char *);
if (s == NULL)
s = "(null)";
res = print_string(param, s);
}
/* pointer argument */
else if (ch == 'p') {
param->vars.flags |= PRINT_FLAG_ALTERNATE_FORM;
/* XXXCRG res=print_pow2(param,
* (uint32_t) __builtin_va_arg(args,
* void *),4);
*/
res = print_pow2(param, (unsigned long long)
__builtin_va_arg(args, void *), 4);
}
/* single character argument */
else if (ch == 'c') {
char c[2];
c[0] = __builtin_va_arg(args, int);
c[1] = 0;
res = print_string(param, c);
}
/* default: print the format specifier as it is */
else {
res = param->emit(PRINT_CMD_COPY, start,
fmt - start, param->data);
}
}
/* return if an error occurred */
if (res < 0)
return res;
}
/* done. Return the result of the last emit function call */
return res;
}
static int charout(int cmd, const char *s, int sz, void *hnd)
{
/* pointer to an integer to store the number of characters */
int *nchars = (int *)hnd;
/* working pointer */
const char *p = s;
/* copy mode ? */
if (cmd == PRINT_CMD_COPY) {
/* copy all characters until NUL is found */
if (sz < 0)
s += console_puts(s);
/* copy 'sz' characters */
else
s += console_write(s, sz);
return (*nchars += (s - p));
}
/* fill mode */
else {
*nchars += sz;
while (sz--)
console_putc(*s);
}
return *nchars;
}
int vprintf(const char *fmt, va_list args)
{
/* struct to store all necessary parameters */
struct print_param param;
/* the result of this function */
int res = 0;
/* argument fo charout() */
int nchars = 0;
/* initialize parameters */
memset(&param, 0, sizeof(param));
param.emit = charout;
param.data = &nchars;
/* execute the printf() */
res = do_print(fmt, &param, args);
/* done */
return res;
}
int printf(const char *fmt, ...)
{
/* variable argument list needed for do_print() */
va_list args;
/* the result of this function */
int res;
va_start(args, fmt);
/* execute the printf() */
res = vprintf(fmt, args);
/* destroy parameter list */
va_end(args);
/* done */
return res;
}
static int charmem(int cmd, const char *s, int sz, void *hnd)
{
/* pointer to the snprint parameter list */
struct snprint_param *param = (struct snprint_param *) hnd;
/* pointer to the destination */
char *p = param->dst + param->wrtn;
/* characters actually written */
int n = 0;
/* copy mode ? */
if (cmd == PRINT_CMD_COPY) {
if (sz < 0) {
while (*s) {
if (n < param->sz - param->wrtn)
*p = *s;
p++;
s++;
n++;
}
} else {
while (*s && n < sz) {
if (n < param->sz - param->wrtn)
*p = *s;
p++;
s++;
n++;
}
}
param->wrtn += n;
return n;
}
/* fill mode */
else {
n = (sz < param->sz - param->wrtn) ? sz : 0;
param->wrtn += sz;
memset(p, *s, n);
}
return n;
}
int vsnprintf(char *dst, int sz, const char *fmt, va_list args)
{
char c[1];
/* the result of this function */
int res = 0;
if (sz <= 0 || !dst) {
dst = c;
sz = 1;
}
/* struct to store all necessary parameters */
struct print_param param;
/* struct to store snprintf specific parameters */
struct snprint_param snparam;
/* initialize parameters */
memset(&snparam, 0, sizeof(snparam));
snparam.dst = dst;
snparam.sz = sz;
memset(&param, 0, sizeof(param));
param.emit = charmem;
param.data = &snparam;
/* execute the printf() */
if (do_print(fmt, &param, args) < 0)
return -1;
/* ensure the written string is NULL terminated */
if (snparam.wrtn < sz)
snparam.dst[snparam.wrtn] = '\0';
else
snparam.dst[sz - 1] = '\0';
/* return the number of chars which would be written */
res = snparam.wrtn;
/* done */
return res;
}
int snprintf(char *dest, int sz, const char *fmt, ...)
{
/* variable argument list needed for do_print() */
va_list args;
/* the result of this function */
int res;
va_start(args, fmt);
/* execute the printf() */
res = vsnprintf(dest, sz, fmt, args);
/* destroy parameter list */
va_end(args);
/* done */
return res;
}

194
hypervisor/debug/sbuf.c Normal file
View File

@@ -0,0 +1,194 @@
/*
* SHARED BUFFER
*
* Copyright (C) 2017 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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.
*
* Li Fei <fei1.li@intel.com>
*
*/
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
DEFINE_CPU_DATA(uint64_t * [ACRN_SBUF_ID_MAX], sbuf);
static inline bool sbuf_is_empty(struct shared_buf *sbuf)
{
return (sbuf->head == sbuf->tail);
}
static inline uint32_t sbuf_next_ptr(uint32_t pos,
uint32_t span, uint32_t scope)
{
pos += span;
pos = (pos >= scope) ? (pos - scope) : pos;
return pos;
}
static inline uint32_t sbuf_calculate_allocate_size(uint32_t ele_num,
uint32_t ele_size)
{
uint64_t sbuf_allocate_size;
sbuf_allocate_size = ele_num * ele_size;
sbuf_allocate_size += SBUF_HEAD_SIZE;
if (sbuf_allocate_size > SBUF_MAX_SIZE) {
pr_err("%s, num=0x%x, size=0x%x exceed 0x%x",
__func__, ele_num, ele_size, SBUF_MAX_SIZE);
return 0;
}
return sbuf_allocate_size;
}
struct shared_buf *sbuf_allocate(uint32_t ele_num, uint32_t ele_size)
{
struct shared_buf *sbuf;
uint32_t sbuf_allocate_size;
if (!ele_num || !ele_size) {
pr_err("%s invalid parameter!", __func__);
return NULL;
}
sbuf_allocate_size = sbuf_calculate_allocate_size(ele_num, ele_size);
if (!sbuf_allocate_size)
return NULL;
sbuf = malloc(sbuf_allocate_size);
if (sbuf == NULL) {
pr_err("%s no memory!", __func__);
return NULL;
}
memset(sbuf, 0, SBUF_HEAD_SIZE);
sbuf->ele_num = ele_num;
sbuf->ele_size = ele_size;
sbuf->size = ele_num * ele_size;
sbuf->magic = SBUF_MAGIC;
pr_info("%s ele_num=0x%x, ele_size=0x%x allocated",
__func__, ele_num, ele_size);
return sbuf;
}
void sbuf_free(struct shared_buf *sbuf)
{
if ((sbuf == NULL) || sbuf->magic != SBUF_MAGIC) {
pr_err("%s invalid parameter!", __func__);
return;
}
sbuf->magic = 0;
free(sbuf);
}
int sbuf_get(struct shared_buf *sbuf, uint8_t *data)
{
const void *from;
if ((sbuf == NULL) || (data == NULL))
return -EINVAL;
if (sbuf_is_empty(sbuf)) {
/* no data available */
return 0;
}
from = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head;
memcpy_s((void *)data, sbuf->ele_size, from, sbuf->ele_size);
sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size);
return sbuf->ele_size;
}
/**
* The high caller should guarantee each time there must have
* sbuf->ele_size data can be write form data and this function
* should guarantee execution atomically.
*
* flag:
* If OVERWRITE_EN set, buf can store (ele_num - 1) elements at most.
* Should use lock to guarantee that only one read or write at
* the same time.
* if OVERWRITE_EN not set, buf can store (ele_num - 1) elements
* at most. Shouldn't modify the sbuf->head.
*
* return:
* ele_size: write succeeded.
* 0: no write, buf is full
* negative: failed.
*/
int sbuf_put(struct shared_buf *sbuf, uint8_t *data)
{
void *to;
uint32_t next_tail;
bool trigger_overwrite = false;
if ((sbuf == NULL) || (data == NULL))
return -EINVAL;
next_tail = sbuf_next_ptr(sbuf->tail, sbuf->ele_size, sbuf->size);
/* if this write would trigger overrun */
if (next_tail == sbuf->head) {
/* accumulate overrun count if necessary */
sbuf->overrun_cnt += sbuf->flags & OVERRUN_CNT_EN;
if (!(sbuf->flags & OVERWRITE_EN)) {
/* if not enable over write, return here. */
return 0;
}
trigger_overwrite = true;
}
to = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->tail;
memcpy_s(to, sbuf->ele_size, data, sbuf->ele_size);
if (trigger_overwrite) {
sbuf->head = sbuf_next_ptr(sbuf->head,
sbuf->ele_size, sbuf->size);
}
sbuf->tail = next_tail;
return sbuf->ele_size;
}
int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, uint64_t *hva)
{
if (pcpu_id >= (uint32_t) phy_cpu_num ||
sbuf_id >= ACRN_SBUF_ID_MAX)
return -EINVAL;
per_cpu(sbuf, pcpu_id)[sbuf_id] = hva;
return 0;
}

365
hypervisor/debug/serial.c Normal file
View File

@@ -0,0 +1,365 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "serial_internal.h"
static struct uart *sio_ports[SERIAL_MAX_DEVS];
static uint8_t sio_initialized[SERIAL_MAX_DEVS];
static struct uart *get_uart_by_id(char *uart_id, uint32_t *index)
{
/* Initialize the index to the start of array. */
*index = 0;
while (sio_ports[*index] != NULL) {
if (strncmp(sio_ports[*index]->tgt_uart->uart_id, uart_id,
strnlen_s(sio_ports[*index]->tgt_uart->uart_id,
SERIAL_ID_MAX_LENGTH)) == 0)
break;
/* No device is found if index reaches end of array. */
if (++(*index) == SERIAL_MAX_DEVS)
return NULL;
}
return sio_ports[*index];
}
int serial_init(void)
{
uint32_t index = 0;
int status = 0;
while (index < SERIAL_MAX_DEVS) {
/* Allocate memory for generic control block of enabled UART */
sio_ports[index] = calloc(1, sizeof(struct uart));
if (!sio_ports[index]) {
status = -ENOMEM;
break;
}
sio_ports[index]->tgt_uart = &(Tgt_Uarts[index]);
/*
* Set the open flag to false to indicate that UART port is
* not opened yet.
*/
sio_ports[index]->open_flag = false;
/* Reset the tx lock */
spinlock_init(&sio_ports[index]->tx_lock);
sio_ports[index]->rx_sio_queue = sbuf_allocate(
sio_ports[index]->tgt_uart->buffer_size,
sizeof(uint8_t));
if (sio_ports[index]->rx_sio_queue != NULL) {
sbuf_set_flags(sio_ports[index]->rx_sio_queue,
OVERWRITE_EN);
/* Call target specific initialization function */
status = sio_ports[index]->tgt_uart->
init(sio_ports[index]->tgt_uart);
if (status == 0)
sio_initialized[index] = true;
} else {
status = -ENOMEM;
break;
}
index++;
}
return status;
}
uint32_t serial_open(char *uart_id)
{
int status = SERIAL_DEV_NOT_FOUND;
struct uart *uart;
uint32_t index;
/* Get UART control block from given character ID */
uart = get_uart_by_id(uart_id, &index);
if (uart != NULL && index < SERIAL_MAX_DEVS &&
sio_initialized[index] &&
(uart->open_flag == false)) {
/* Reset the buffer lock */
spinlock_init(&uart->buffer_lock);
/* Configure the UART port to default settings. */
uart->config.data_bits = DATA_8;
uart->config.stop_bits = STOP_1;
uart->config.parity_bits = PARITY_NONE;
uart->config.baud_rate = BAUD_115200;
uart->config.flow_control = FLOW_NONE;
uart->config.read_mode = SUSPEND;
/* Open the UART hardware with default configuration. */
status = uart->tgt_uart->open(uart->tgt_uart, &(uart->config));
if (status == 0)
uart->open_flag = true;
}
/* Already open serial device */
else if (uart != NULL && uart->open_flag == true) {
/* Reset the buffer lock */
spinlock_init(&uart->buffer_lock);
status = 0;
}
return (status == 0) ?
SERIAL_ENCODE_INDEX(index) :
SERIAL_INVALID_HANDLE;
}
int serial_get_rx_data(uint32_t uart_handle)
{
uint32_t index;
struct uart *uart;
int data_avail, rx_byte_status;
uint32_t lsr_reg, bytes_read;
uint8_t ch;
int total_bytes_read = 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
uart = sio_ports[index];
if (uart == NULL)
return 0;
/* Place all the data available in RX FIFO, in circular buffer */
while ((data_avail = uart->tgt_uart->rx_data_is_avail(
uart->tgt_uart, &lsr_reg))) {
/* Read the byte */
uart->tgt_uart->read(uart->tgt_uart, (void *)&ch, &bytes_read);
/* Get RX status for this byte */
rx_byte_status = uart->tgt_uart->get_rx_err(lsr_reg);
/*
* Check if discard errors in RX character
* (parity / framing errors)
*/
if (rx_byte_status >= SD_RX_PARITY_ERROR) {
/* Increase error status if bad data */
uart->rx_error.parity_errors +=
(rx_byte_status == SD_RX_PARITY_ERROR);
uart->rx_error.frame_errors +=
(rx_byte_status == SD_RX_FRAME_ERROR);
} else {
/* Update the overrun errors */
uart->rx_error.overrun_errors +=
(rx_byte_status == SD_RX_OVERRUN_ERROR);
/* Enter Critical Section */
spinlock_obtain(&uart->buffer_lock);
/* Put the item on circular buffer */
sbuf_put(uart->rx_sio_queue, &ch);
/* Exit Critical Section */
spinlock_release(&uart->buffer_lock);
}
/* Update the total bytes read */
total_bytes_read += bytes_read;
}
return total_bytes_read;
}
int serial_getc(uint32_t uart_handle)
{
uint8_t ch;
struct uart *port;
uint32_t index;
int status = SERIAL_DEV_NOT_FOUND;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
goto exit;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
goto exit;
port = sio_ports[index];
if (port == NULL)
goto exit;
/* First read a character from the circular buffer regardless of the
* read mode of UART port. If status is not CBUFFER_EMPTY, character
* read from UART port is returned to the caller. Otherwise, if read
* mode is not NO_SUSPEND, thread is blocked until a character is read
* from the port. Serial target specific HISR unblocks the thread when
* a character is received and character is then read from the circular
* buffer.
*/
/* Disable interrupts for critical section */
spinlock_obtain(&port->buffer_lock);
status = sbuf_get(port->rx_sio_queue, &ch);
/* Restore interrupts to original level. */
spinlock_release(&port->buffer_lock);
exit:
/* Return the character read, otherwise return the error status */
return ((status > 0) ? (int)(ch) : SERIAL_EOF);
}
int serial_gets(uint32_t uart_handle, char *buffer, uint32_t length)
{
char *data_read = buffer;
int c;
struct uart *port;
uint32_t index;
int status = 0;
if ((buffer == NULL) || (length == 0))
return 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
port = sio_ports[index];
if ((port != NULL) && (port->open_flag == true)) {
for (; length > 0; data_read++, length--) {
/* Disable interrupts for critical section */
spinlock_obtain(&port->buffer_lock);
status = sbuf_get(port->rx_sio_queue, (uint8_t *)&c);
/* Restore interrupts to original level. */
spinlock_release(&port->buffer_lock);
if (status <= 0)
break;
/* Save character in buffer */
*data_read = (char) c;
}
}
/* Return actual number of bytes read */
return (int)(data_read - buffer);
}
static int serial_putc(uint32_t uart_handle, int c)
{
uint32_t index, bytes_written = 0;
struct uart *uart;
int busy;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return SERIAL_EOF;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return SERIAL_EOF;
uart = sio_ports[index];
if (uart == NULL)
return SERIAL_EOF;
/* Wait for TX hardware to be ready */
do {
busy = uart->tgt_uart->tx_is_busy(uart->tgt_uart);
} while (busy);
/* Transmit character */
uart->tgt_uart->write(uart->tgt_uart, &(c), &bytes_written);
/* Return character written or EOF for error */
return ((bytes_written > 0) ? c : (SERIAL_EOF));
}
int serial_puts(uint32_t uart_handle, const char *s, uint32_t length)
{
const char *old_data = s;
uint32_t index;
struct uart *port;
int retval = 0;
if ((s == NULL) || (length == 0))
return 0;
if (!SERIAL_VALIDATE_HANDLE(uart_handle))
return 0;
index = SERIAL_DECODE_INDEX(uart_handle);
if (index >= SERIAL_MAX_DEVS)
return 0;
port = sio_ports[index];
if (port == NULL)
return 0;
/*
* Grab the semaphore so that strings between threads do not
* get mixed.
*/
spinlock_obtain(&port->tx_lock);
/*
* Loop through the string until desired length of bytes have
* been written or SERIAL_EOF is returned.
*/
for (; length > 0 && retval != SERIAL_EOF; s++, length--)
retval = serial_putc(uart_handle, (int) *s);
/* Allow other threads to use this service. */
spinlock_release(&port->tx_lock);
/* Return actual number of bytes written */
return (int)(s - old_data);
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 SERIAL_INTER_H
#define SERIAL_INTER_H
struct shared_buf;
/* Maximum serial devices supported by the platform. */
#define SERIAL_MAX_DEVS 1
/* Maximum length of unique id of each UART port enabled in platform. */
#define SERIAL_ID_MAX_LENGTH 8
/* SERIAL error values */
#define SERIAL_SUCCESS 0
#define SERIAL_EOF -1
#define SERIAL_ERROR -2
#define SERIAL_DEV_NOT_FOUND -3
#define INVALID_COM_PORT -4
#define SERIAL_NO_char_AVAIL -5
#define SERIAL_INVALID_HANDLE 0xFFFFFFFF
/* Pending interrupt defines */
#define SD_NO_INTERRUPT 0
#define SD_RX_INTERRUPT 1
/* RX error defines */
#define SD_RX_NO_ERROR 0
#define SD_RX_OVERRUN_ERROR 1
#define SD_RX_PARITY_ERROR 2
#define SD_RX_FRAME_ERROR 3
/* Defines for encoding/decoding the unique UART handle of each port. */
#define SERIAL_MAGIC_NUM 0x005500AA
#define SERIAL_VALIDATE_HANDLE(handle) \
((handle & 0xFFFF00FF) == (SERIAL_MAGIC_NUM))
#define SERIAL_ENCODE_INDEX(index) ((SERIAL_MAGIC_NUM) | (index << 8))
#define SERIAL_DECODE_INDEX(handle) ((handle & 0x0000FF00) >> 8)
#define NO_SUSPEND 0
#define SUSPEND 0xFFFFFFFFUL
/* Enumeration values to set UART Configuration */
typedef enum _baudenum_ {
/* Baud Rate Options */
BAUD_110 = 110, /* not supported on OMAP5 */
BAUD_300 = 300,
BAUD_600 = 600,
BAUD_1200 = 1200,
BAUD_2400 = 2400,
BAUD_4800 = 4800,
BAUD_9600 = 9600,
BAUD_14400 = 14400,
BAUD_19200 = 19200,
BAUD_28800 = 28800,
BAUD_38400 = 38400,
BAUD_57600 = 57600,
BAUD_115200 = 115200,
BAUD_230400 = 230400,
BAUD_460800 = 460800,
BAUD_921600 = 921600,
BAUD_1843000 = 1843000,
BAUD_36884000 = 36884000
} BAUD_ENUM;
typedef enum _flowenum_ {
/* Flow Control Bits */
FLOW_NONE = 0,
FLOW_HARD = 1,
FLOW_X = 2
} FLOW_ENUM;
typedef enum _parityenum_ {
/* Parity Bits */
PARITY_NONE = 0,
PARITY_ODD = 1,
PARITY_EVEN = 2,
PARITY_MARK = 3,
PARITY_SPACE = 4
} PARITY_ENUM;
typedef enum _stopenum_ {
/* Stop Bits */
STOP_1 = 1,
STOP_2 = 2
} STOP_ENUM;
typedef enum _dataenum_ {
/* Data bits */
DATA_7 = 7,
DATA_8 = 8
} DATA_ENUM;
/* Control Block definition about error in Rx data */
struct rx_error {
uint32_t parity_errors;
uint32_t frame_errors;
uint32_t overrun_errors;
uint32_t general_errors;
};
/* Control Block definition for configuration specific
* parameters of UART
*/
struct uart_config {
uint32_t data_bits;
uint32_t stop_bits;
uint32_t parity_bits;
uint32_t baud_rate;
uint32_t flow_control;
/* Read mode of UART port in interrupt mode. It can be NO_SUSPEND or
* SUSPEND or (1-4,294,967,293). SUSPEND means unlimited blocking,
* NO_SUSPEND means non-blocking and some integer value means timeout
* blocking support. By default, it is set to SUSPEND.
*/
uint32_t read_mode;
};
/* Control Block definition for target specific driver
* of UART
*/
struct tgt_uart {
char uart_id[SERIAL_ID_MAX_LENGTH];
mmio_addr_t base_address;
uint32_t clock_frequency;
uint32_t buffer_size;
unsigned int open_count;
/* Target specific function pointers. */
int (*init)(struct tgt_uart *tgt_uart);
int (*open)(struct tgt_uart *tgt_uart, struct uart_config *config);
void (*close)(struct tgt_uart *tgt_uart);
void (*read)(struct tgt_uart *tgt_uart,
void *buffer, uint32_t *bytes_read);
void (*write)(struct tgt_uart *tgt_uart,
const void *buffer, uint32_t *bytes_written);
bool (*tx_is_busy)(struct tgt_uart *tgt_uart);
bool (*rx_data_is_avail)(struct tgt_uart *tgt_uart, uint32_t *lsr_reg);
int (*get_rx_err)(uint32_t rx_data);
};
/* Control Block definition of light-weight serial driver */
struct uart {
/* Pointer to target specific Control Block of UART */
struct tgt_uart *tgt_uart;
/* Configuration of UART */
struct uart_config config;
/* Errors in data received from UART port */
struct rx_error rx_error;
/* Pointer to receive circular buffer */
struct shared_buf *rx_sio_queue;
/* Lock to provide mutual exclusion for transmitting data to UART port*/
spinlock_t tx_lock;
/* Lock to provide mutual exclusion for accessing shared buffer */
spinlock_t buffer_lock;
/* Flag to indicate whether UART port is opened or not */
uint8_t open_flag;
};
/* Null terminated array of target specific UART control blocks */
extern struct tgt_uart Tgt_Uarts[SERIAL_MAX_DEVS];
uint32_t serial_open(char *uart_id);
int serial_getc(uint32_t uart_handle);
int serial_gets(uint32_t uart_handle, char *buffer, uint32_t length);
int serial_puts(uint32_t uart_handle, const char *s, uint32_t length);
int serial_get_rx_data(uint32_t uart_handle);
#endif /* !SERIAL_INTER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 SHELL_INTER_H
#define SHELL_INTER_H
#include <spinlock.h>
struct shell;
/* Structure to hold the details about shell input and output */
struct shell_io {
void *io_session_info;
int (*io_init)(struct shell *);
int (*io_deinit)(struct shell *);
void (*io_puts)(struct shell *, char *);
uint8_t (*io_getc)(struct shell *);
void (*io_special)(struct shell *, uint8_t);
bool io_echo_on;
};
#define SHELL_CMD_MAX_LEN 100
#define SHELL_NAME_MAX_LEN 50
#define SHELL_PARA_MAX_LEN 64
#define SHELL_HELP_MAX_LEN 256
#define SHELL_STRING_MAX_LEN (CPU_PAGE_SIZE << 2)
/* Shell Control Block */
struct shell {
struct shell_io session_io; /* Session I/O information */
char input_line[2][SHELL_CMD_MAX_LEN + 1]; /* current & last */
char name[SHELL_NAME_MAX_LEN]; /* Session name */
uint32_t input_line_len; /* Length of current input line */
uint32_t input_line_active; /* Active input line index */
struct list_head cmd_list; /* List of registered commands */
uint32_t cmd_count; /* Count of added commands */
};
/* Shell Command Function */
typedef int (*shell_cmd_fn_t)(struct shell *, int, char **);
/* Shell Command */
struct shell_cmd {
struct list_head node; /* Linked list node */
char *str; /* Command string */
char *cmd_param; /* Command parameter string */
char *help_str; /* Help text associated with the command */
shell_cmd_fn_t fcn; /* Command call-back function */
};
/* Shell Command list with parameters and help description */
#define SHELL_CMD_HELP "help"
#define SHELL_CMD_HELP_PARAM NULL
#define SHELL_CMD_HELP_HELP "Display info about the supported shell commands."
#define SHELL_CMD_VM_LIST "vm_list"
#define SHELL_CMD_VM_LIST_PARAM NULL
#define SHELL_CMD_VM_LIST_HELP "Lists all VMs (VM Name, VM ID, VM State)"
#define SHELL_CMD_VCPU_LIST "vcpu_list"
#define SHELL_CMD_VCPU_LIST_PARAM NULL
#define SHELL_CMD_VCPU_LIST_HELP "Lists all VCPU in all VMs"
#define SHELL_CMD_VCPU_PAUSE "vcpu_pause"
#define SHELL_CMD_VCPU_PAUSE_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_PAUSE_HELP "Pause a specific vcpu"
#define SHELL_CMD_VCPU_RESUME "vcpu_resume"
#define SHELL_CMD_VCPU_RESUME_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_RESUME_HELP "Resume a specific vcpu"
#define SHELL_CMD_VCPU_DUMPREG "vcpu_dumpreg"
#define SHELL_CMD_VCPU_DUMPREG_PARAM "<vm id, vcpu id>"
#define SHELL_CMD_VCPU_DUMPREG_HELP "Dump registers for a specific vcpu"
#define SHELL_CMD_VCPU_DUMPMEM "vcpu_dumpmem"
#define SHELL_CMD_VCPU_DUMPMEM_PARAM "<vcpu id, gva, length>"
#define SHELL_CMD_VCPU_DUMPMEM_HELP "Dump memory for a specific vcpu"
#define SHELL_CMD_VM_CONSOLE "vm_console"
#define SHELL_CMD_VM_CONSOLE_PARAM NULL
#define SHELL_CMD_VM_CONSOLE_HELP "Switch to SOS's console"
#define SHELL_CMD_INTERRUPT "int"
#define SHELL_CMD_INTERRUPT_PARAM NULL
#define SHELL_CMD_INTERRUPT_HELP "show interrupt info per CPU"
#define SHELL_CMD_PTDEV "pt"
#define SHELL_CMD_PTDEV_PARAM NULL
#define SHELL_CMD_PTDEV_HELP "show pass-through device info"
#define SHELL_CMD_REQ "lsreq"
#define SHELL_CMD_REQ_PARAM NULL
#define SHELL_CMD_REQ_HELP "show ioreq info"
#define SHELL_CMD_IOAPIC "dump_ioapic"
#define SHELL_CMD_IOAPIC_PARAM NULL
#define SHELL_CMD_IOAPIC_HELP "show native ioapic info"
#define SHELL_CMD_VIOAPIC "vioapic"
#define SHELL_CMD_VIOAPIC_PARAM "<vm id>"
#define SHELL_CMD_VIOAPIC_HELP "show vioapic info"
#define SHELL_CMD_VMEXIT "vmexit"
#define SHELL_CMD_VMEXIT_PARAM NULL
#define SHELL_CMD_VMEXIT_HELP "show vmexit profiling"
#define SHELL_CMD_LOGDUMP "logdump"
#define SHELL_CMD_LOGDUMP_PARAM "<pcpu id>"
#define SHELL_CMD_LOGDUMP_HELP "log buffer dump"
#define SHELL_CMD_trace "trace"
#define SHELL_CMD_trace_PARAM "<cpumask> <ms>"
#define SHELL_CMD_trace_HELP "Dump cpus recent events within <ms> millisecond"
#define SHELL_CMD_GET_LOG_LVL "get_loglevel"
#define SHELL_CMD_GET_LOG_LVL_PARAM NULL
#define SHELL_CMD_GET_LOG_LVL_HELP "Get the loglevel"
#define SHELL_CMD_SET_LOG_LVL "set_loglevel"
#define SHELL_CMD_SET_LOG_LVL_PARAM "<console_loglevel> [mem_loglevel]"
#define SHELL_CMD_SET_LOG_LVL_HELP "Set loglevel [0-6]"
/* Global function prototypes */
int shell_show_req_info(struct shell *p_shell, int argc, char **argv);
int shell_construct(struct shell **p_shell);
int shell_cmd_help(struct shell *p_shell, int argc, char **argv);
int shell_reset_cmd(struct shell *p_shell, int argc, char **argv);
int shell_list_vm(struct shell *p_shell, int argc, char **argv);
int shell_list_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_pause_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_resume_vcpu(struct shell *p_shell, int argc, char **argv);
int shell_vcpu_dumpreg(struct shell *p_shell, int argc, char **argv);
int shell_vcpu_dumpmem(struct shell *p_shell, int argc, char **argv);
int shell_boot_vm(struct shell *p_shell, int argc, char **argv);
int shell_trace_cmd(struct shell *p_shell, int argc, char **argv);
int shell_to_sos_console(struct shell *p_shell, int argc, char **argv);
int shell_show_cpu_int(struct shell *p_shell, int argc, char **argv);
int shell_show_ptdev_info(struct shell *p_shell, int argc, char **argv);
int shell_show_vioapic_info(struct shell *p_shell, int argc, char **argv);
int shell_show_ioapic_info(struct shell *p_shell, int argc, char **argv);
int shell_show_vmexit_profile(struct shell *p_shell, int argc, char **argv);
int shell_dump_logbuf(struct shell *p_shell, int argc, char **argv);
int shell_get_loglevel(struct shell *p_shell, int argc, char **argv);
int shell_set_loglevel(struct shell *p_shell, int argc, char **argv);
struct shell_cmd *shell_find_cmd(struct shell *p_shell, const char *cmd);
int shell_process_cmd(struct shell *p_shell, char *p_input_line);
int shell_terminate_serial(struct shell *p_shell);
int shell_init_serial(struct shell *p_shell);
void shell_puts_serial(struct shell *p_shell, char *string_ptr);
uint8_t shell_getc_serial(struct shell *p_shell);
void shell_special_serial(struct shell *p_shell, uint8_t ch);
void kick_shell(struct shell *p_shell);
int shell_puts(struct shell *p_shell, char *str_ptr);
int shell_set_name(struct shell *p_shell, char *name);
#endif /* SHELL_INTER_H */

View File

@@ -0,0 +1,421 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "shell_internal.h"
/* Shell that uses serial I/O */
static struct shell *serial_session;
static int shell_register_cmd(struct shell *p_shell,
const char *cmd,
const char *cmd_param,
const char *cmd_help_str,
int (*cmd_fcn)(struct shell *, int, char **))
{
int status = 0;
struct shell_cmd *p_cmd;
uint32_t cmd_mem_size;
if ((p_shell == NULL) || (cmd == NULL) ||
(cmd_help_str == NULL) || (cmd_fcn == NULL)) {
return -EINVAL;
}
/* Check if a duplicate command exists */
p_cmd = shell_find_cmd(p_shell, cmd);
if (p_cmd != NULL) {
/* Requested command is already registered */
pr_err("Error: Command %s is already registered.", cmd);
status = -EINVAL;
goto exit;
}
/* Requested command is not already registered. So allocate enough
* memory for the command structure and the command, parameter and the
* help text strings along with the corresponding null terminating
* character/s.
*/
cmd_mem_size = sizeof(struct shell_cmd)
+ (strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
/* If command takes any parameters, need to allocate memory for storing
* parameter string.
*/
if (cmd_param)
cmd_mem_size += strnlen_s(cmd_param, SHELL_PARA_MAX_LEN) + 1;
/* If help text is provided for command, need to allocate memory for
* storing help string.
*/
if (cmd_help_str)
cmd_mem_size += strnlen_s(cmd_help_str, SHELL_HELP_MAX_LEN) + 1;
p_cmd = (struct shell_cmd *) calloc(1, cmd_mem_size);
if (p_cmd == NULL) {
status = -ENOMEM;
goto exit;
}
/* The command structure, command string, it's parameter string and
* the associated help string are all stored in contiguous memory
* locations. So the cmd string immediately follows the command
* structure..
*/
p_cmd->str = (char *)p_cmd + sizeof(struct shell_cmd);
strncpy_s(p_cmd->str, SHELL_CMD_MAX_LEN, cmd, SHELL_CMD_MAX_LEN);
/* Check if this command does take any parameters... */
if (cmd_param) {
/* The command parameter string immediately follows the command
* string in memory.
*/
p_cmd->cmd_param = p_cmd->str
+ (strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
strcpy_s(p_cmd->cmd_param, SHELL_PARA_MAX_LEN, cmd_param);
}
/* Check if help string is provided for the command.. */
if (cmd_help_str) {
if (cmd_param) {
/* The command help string immediately follows the
* parameter string in memory | cmd_structure |
* cmd_str | param_str | help_str |
*/
p_cmd->help_str = p_cmd->cmd_param +
(strnlen_s(cmd_param, SHELL_PARA_MAX_LEN) + 1);
strcpy_s(p_cmd->help_str,
SHELL_HELP_MAX_LEN, cmd_help_str);
} else {
/* No command parameter/s. Help string immediately
* follows the cmd string | cmd_structure | cmd_str |
* help_str |
*/
p_cmd->help_str = p_cmd->str +
(strnlen_s(cmd, SHELL_CMD_MAX_LEN) + 1);
strcpy_s(p_cmd->help_str,
SHELL_HELP_MAX_LEN, cmd_help_str);
}
}
/* Set the command function. */
p_cmd->fcn = cmd_fcn;
INIT_LIST_HEAD(&p_cmd->node);
list_add(&p_cmd->node, &p_shell->cmd_list);
/* Update command count. */
p_shell->cmd_count++;
status = 0;
exit:
return status;
}
int shell_init(void)
{
int status;
status = shell_construct(&serial_session);
if (status != 0)
return status;
/* Set the function pointers for the shell i/p and o/p functions */
serial_session->session_io.io_init = shell_init_serial;
serial_session->session_io.io_deinit = shell_terminate_serial;
serial_session->session_io.io_puts = shell_puts_serial;
serial_session->session_io.io_getc = shell_getc_serial;
serial_session->session_io.io_special = shell_special_serial;
serial_session->session_io.io_echo_on = (bool)true;
/* Initialize the handler for the serial port that will be used
* for shell i/p and o/p
*/
status = serial_session->session_io.io_init(serial_session);
/* Register command handlers for the shell commands that are available
* by default
*/
if (status == 0) {
status = shell_register_cmd(serial_session,
SHELL_CMD_HELP,
SHELL_CMD_HELP_PARAM,
SHELL_CMD_HELP_HELP,
shell_cmd_help);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_HELP);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VM_LIST,
SHELL_CMD_VM_LIST_PARAM,
SHELL_CMD_VM_LIST_HELP,
shell_list_vm);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VM_LIST);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_LIST,
SHELL_CMD_VCPU_LIST_PARAM,
SHELL_CMD_VCPU_LIST_HELP,
shell_list_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_LIST);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_PAUSE,
SHELL_CMD_VCPU_PAUSE_PARAM,
SHELL_CMD_VCPU_PAUSE_HELP,
shell_pause_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_PAUSE);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_RESUME,
SHELL_CMD_VCPU_RESUME_PARAM,
SHELL_CMD_VCPU_RESUME_HELP,
shell_resume_vcpu);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_RESUME);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_DUMPREG,
SHELL_CMD_VCPU_DUMPREG_PARAM,
SHELL_CMD_VCPU_DUMPREG_HELP,
shell_vcpu_dumpreg);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_DUMPREG);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VCPU_DUMPMEM,
SHELL_CMD_VCPU_DUMPMEM_PARAM,
SHELL_CMD_VCPU_DUMPMEM_HELP,
shell_vcpu_dumpmem);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VCPU_DUMPMEM);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VM_CONSOLE,
SHELL_CMD_VM_CONSOLE_PARAM,
SHELL_CMD_VM_CONSOLE_HELP,
shell_to_sos_console);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VM_CONSOLE);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_INTERRUPT,
SHELL_CMD_INTERRUPT_PARAM,
SHELL_CMD_INTERRUPT_HELP,
shell_show_cpu_int);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_INTERRUPT);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_PTDEV,
SHELL_CMD_PTDEV_PARAM,
SHELL_CMD_PTDEV_HELP,
shell_show_ptdev_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_PTDEV);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_REQ,
SHELL_CMD_REQ_PARAM,
SHELL_CMD_REQ_HELP,
shell_show_req_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_REQ);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VIOAPIC,
SHELL_CMD_VIOAPIC_PARAM,
SHELL_CMD_VIOAPIC_HELP,
shell_show_vioapic_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VIOAPIC);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_IOAPIC,
SHELL_CMD_IOAPIC_PARAM,
SHELL_CMD_IOAPIC_HELP,
shell_show_ioapic_info);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_IOAPIC);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_VMEXIT,
SHELL_CMD_VMEXIT_PARAM,
SHELL_CMD_VMEXIT_HELP,
shell_show_vmexit_profile);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_VMEXIT);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_LOGDUMP,
SHELL_CMD_LOGDUMP_PARAM,
SHELL_CMD_LOGDUMP_HELP,
shell_dump_logbuf);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_LOGDUMP);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_GET_LOG_LVL,
SHELL_CMD_GET_LOG_LVL_PARAM,
SHELL_CMD_GET_LOG_LVL_HELP,
shell_get_loglevel);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_GET_LOG_LVL);
}
status = shell_register_cmd(serial_session,
SHELL_CMD_SET_LOG_LVL,
SHELL_CMD_SET_LOG_LVL_PARAM,
SHELL_CMD_SET_LOG_LVL_HELP,
shell_set_loglevel);
if (status != 0) {
pr_err("Error: Command \"%s\" registration failed.",
SHELL_CMD_SET_LOG_LVL);
}
}
return status;
}
int shell_puts(struct shell *p_shell, char *str_ptr)
{
int status;
if ((p_shell != NULL) && (p_shell->session_io.io_puts != NULL) &&
(str_ptr != NULL)) {
/* Transmit data using this shell session's 'puts' function */
p_shell->session_io.io_puts(p_shell, str_ptr);
status = 0;
} else {
/* Error: Invalid request */
status = -EINVAL;
}
return status;
}
int shell_set_name(struct shell *p_shell, char *name)
{
int status;
if ((p_shell != NULL) && (name != NULL)) {
strncpy_s((void *) p_shell->name, SHELL_NAME_MAX_LEN,
(void *) name, SHELL_NAME_MAX_LEN - 1);
/* Ensure null terminated string */
p_shell->name[SHELL_NAME_MAX_LEN - 1] = 0;
status = 0;
} else {
status = -EINVAL;
}
return status;
}
void shell_kick_session(void)
{
/* Kick the shell */
kick_shell(serial_session);
}
int shell_switch_console(void)
{
struct vuart *vuart;
vuart = vuart_console_active();
if (vuart == NULL)
return -EINVAL;
vuart->active = false;
/* Output that switching to ACRN shell */
shell_puts(serial_session,
"\r\n\r\n----- Entering ACRN Shell -----\r\n");
return 0;
}

View File

@@ -0,0 +1,347 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include "uart16550.h"
#include "serial_internal.h"
/* Mapping of 16c550 write-only registers to appropriate structure members */
#define THR_IDX RBR_IDX
#define IIR_IDX FCR_IDX
#define DLL_IDX RBR_IDX
#define DLM_IDX IER_IDX
#if defined(CONFIG_SERIAL_PIO_BASE)
static int serial_port_mapped = 1;
static int uart_enabled = 1;
#define UART_BASE_ADDRESS CONFIG_SERIAL_PIO_BASE
#elif defined(CONFIG_SERIAL_MMIO_BASE)
static int serial_port_mapped;
static int uart_enabled = 1;
#define UART_BASE_ADDRESS CONFIG_SERIAL_MMIO_BASE
#else
#warning "no uart base configure, please check!"
static int serial_port_mapped;
static int uart_enabled;
#define UART_BASE_ADDRESS 0
#endif
typedef uint32_t uart_reg_t;
enum UART_REG_IDX{
RBR_IDX, /* 0 */
IER_IDX, /* 1 */
FCR_IDX, /* 2 */
LCR_IDX, /* 3 */
MCR_IDX, /* 4 */
ISR_IDX, /* 5 */
MSR_IDX, /* 6 */
SPR_IDX, /* 7 */
MDR1_IDX, /* 8 */
REG9_IDX, /* 9 */
REGA_IDX, /* A */
REGB_IDX, /* B */
REGC_IDX, /* C */
REGD_IDX, /* D */
REGE_IDX, /* E */
UASR_IDX, /* F */
SCR_IDX, /* 10*/
SSR_IDX, /* 11*/
REG12_IDX, /* 12*/
OSC_12M_SEL_IDX, /* 13*/
};
/* CPU oscillator clock */
#define CPU_OSC_CLOCK 1843200 /* 1.8432 MHz */
/* UART hardware definitions */
#define UART_CLOCK_RATE CPU_OSC_CLOCK
#define UART_BUFFER_SIZE 2048
static inline uint32_t uart16550_read_reg(uint32_t base, uint32_t reg_idx)
{
if (serial_port_mapped) {
return io_read_byte((ioport_t)
((uint8_t *)(uint64_t)base + reg_idx));
} else {
return mmio_read_long((mmio_addr_t)
((uint32_t *)(uint64_t)base + reg_idx));
}
}
static inline void uart16550_write_reg(uint32_t base,
uint32_t val, uint32_t reg_idx)
{
if (serial_port_mapped) {
io_write_byte(val, (ioport_t)
((uint8_t *)(uint64_t)base + reg_idx));
} else {
mmio_write_long(val, (mmio_addr_t)
((uint32_t *)(uint64_t)base + reg_idx));
}
}
static void uart16550_enable(__unused struct tgt_uart *tgt_uart)
{
}
static int uart16550_calc_baud_div(__unused struct tgt_uart *tgt_uart,
uint32_t ref_freq, uint32_t *baud_div_ptr, uint32_t baud_rate)
{
uint32_t baud_multiplier = baud_rate < BAUD_460800 ? 16 : 13;
*baud_div_ptr = ref_freq / (baud_multiplier * baud_rate);
return 0;
}
static int uart16550_set_baud_rate(struct tgt_uart *tgt_uart,
uint32_t baud_rate)
{
int status;
uint32_t baud_div, duart_clock = CPU_OSC_CLOCK;
uart_reg_t temp_reg;
/* Calculate baud divisor */
status = uart16550_calc_baud_div(
tgt_uart, duart_clock, &baud_div, baud_rate);
if (status == 0) {
/* Enable DLL and DLM registers for setting the Divisor */
temp_reg = uart16550_read_reg(tgt_uart->base_address, LCR_IDX);
temp_reg |= LCR_DLAB;
uart16550_write_reg(tgt_uart->base_address, temp_reg, LCR_IDX);
/* Write the appropriate divisor value */
uart16550_write_reg(tgt_uart->base_address,
((baud_div >> 8) & 0xFF), DLM_IDX);
uart16550_write_reg(tgt_uart->base_address,
(baud_div & 0xFF), DLL_IDX);
/* Disable DLL and DLM registers */
temp_reg &= ~LCR_DLAB;
uart16550_write_reg(tgt_uart->base_address, temp_reg, LCR_IDX);
}
return status;
}
static int uart16550_init(struct tgt_uart *tgt_uart)
{
int status = 0;
if (!uart_enabled) {
/*uart will not be used */
status = -ENODEV;
} else {
if (strcmp(tgt_uart->uart_id, "STDIO") == 0) {
atomic_set_int(&tgt_uart->open_count, 0);
} else {
/* set open count to 1 to prevent open */
atomic_set_int(&tgt_uart->open_count, 1);
status = -EINVAL;
}
}
return status;
}
static int uart16550_open(struct tgt_uart *tgt_uart,
struct uart_config *config)
{
uint32_t temp32;
int status = 0;
if (strcmp(tgt_uart->uart_id, "STDIO") == 0) {
if (atomic_cmpxchg_int(&tgt_uart->open_count, 0, 1) != 0)
return -EBUSY;
/* Call UART setup function */
/* Enable TX and RX FIFOs */
uart16550_write_reg(tgt_uart->base_address,
FCR_FIFOE | FCR_RFR | FCR_TFR, FCR_IDX);
/* Set parity value */
if (config->parity_bits == PARITY_ODD) {
/* Odd parity */
temp32 = LCR_PARITY_ODD;
} else if (config->parity_bits == PARITY_EVEN) {
/* Even parity */
temp32 = LCR_PARITY_EVEN;
} else {
/* No parity */
temp32 = LCR_PARITY_NONE;
}
/* Set Data length */
if (config->data_bits == DATA_7) {
/* Set bits for 7 data bits */
temp32 |= LCR_WL7;
} else {
/* Set bits for 8 data bits */
temp32 |= LCR_WL8;
}
/* Check for 1 stop bit */
if (config->stop_bits == STOP_1) {
/* Set bits for 1 stop bit */
temp32 |= LCR_NB_STOP_BITS_1;
} else {
/* Set bits for 2 stop bits */
temp32 |= LCR_NB_STOP_BITS_2;
}
/* Set-up data bits / parity / stop bits. */
uart16550_write_reg(tgt_uart->base_address,
temp32, LCR_IDX);
/* Disable interrupts (we use polling) */
uart16550_write_reg(tgt_uart->base_address,
UART_IER_DISABLE_ALL, IER_IDX);
/* Set baud rate */
uart16550_set_baud_rate(tgt_uart, config->baud_rate);
/* Data terminal ready + Request to send */
uart16550_write_reg(tgt_uart->base_address,
MCR_RTS | MCR_DTR, MCR_IDX);
/* Enable the UART hardware */
uart16550_enable(tgt_uart);
} else {
status = -ENODEV;
}
return status;
}
static int uart16550_get_rx_err(uint32_t rx_data)
{
int rx_status = SD_RX_NO_ERROR;
/* Check for RX overrun error */
if ((rx_data & LSR_OE))
rx_status |= SD_RX_OVERRUN_ERROR;
/* Check for RX parity error */
if ((rx_data & LSR_PE))
rx_status |= SD_RX_PARITY_ERROR;
/* Check for RX frame error */
if ((rx_data & LSR_FE))
rx_status |= SD_RX_FRAME_ERROR;
/* Return the rx status */
return rx_status;
}
static void uart16550_close(struct tgt_uart *tgt_uart)
{
if (tgt_uart != NULL) {
if (atomic_cmpxchg_int(&tgt_uart->open_count, 1, 0) == 1) {
/* TODO: Add logic to disable the UART */
}
}
}
static void uart16550_read(struct tgt_uart *tgt_uart, void *buffer,
uint32_t *bytes_read)
{
/* If a character has been received, read it */
if ((uart16550_read_reg(tgt_uart->base_address, ISR_IDX) & LSR_DR)
== LSR_DR) {
/* Read a character */
*(uint8_t *)buffer =
uart16550_read_reg(tgt_uart->base_address, RBR_IDX);
/* Read 1 byte */
*bytes_read = 1;
} else {
*bytes_read = 0;
}
}
static void uart16550_write(struct tgt_uart *tgt_uart,
const void *buffer, uint32_t *bytes_written)
{
/* Ensure there are no further Transmit buffer write requests */
do {
} while (!(uart16550_read_reg(tgt_uart->base_address,
ISR_IDX) & LSR_THRE));
/* Transmit the character. */
uart16550_write_reg(tgt_uart->base_address,
*(uint8_t *)buffer, THR_IDX);
if (bytes_written != NULL)
*bytes_written = 1;
}
static bool uart16550_tx_is_busy(struct tgt_uart *tgt_uart)
{
return ((uart16550_read_reg(tgt_uart->base_address, ISR_IDX) &
(LSR_TEMT)) == 0) ? true : false;
}
static bool uart16550_rx_data_is_avail(struct tgt_uart *tgt_uart,
uint32_t *lsr_reg)
{
*(uart_reg_t *)lsr_reg =
uart16550_read_reg(tgt_uart->base_address, ISR_IDX);
return ((*(uart_reg_t *)lsr_reg & LSR_DR) == LSR_DR) ? true : false;
}
struct tgt_uart Tgt_Uarts[SERIAL_MAX_DEVS] = {
{
.uart_id = "STDIO",
.base_address = UART_BASE_ADDRESS,
.clock_frequency = UART_CLOCK_RATE,
.buffer_size = UART_BUFFER_SIZE,
.init = uart16550_init,
.open = uart16550_open,
.close = uart16550_close,
.read = uart16550_read,
.write = uart16550_write,
.tx_is_busy = uart16550_tx_is_busy,
.rx_data_is_avail = uart16550_rx_data_is_avail,
.get_rx_err = uart16550_get_rx_err,
}
};
void uart16550_set_property(int enabled, int port_mapped, uint64_t base_addr)
{
uart_enabled = enabled;
serial_port_mapped = port_mapped;
Tgt_Uarts[0].base_address = (uint32_t) base_addr;
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 UART16550_H
#define UART16550_H
/* Register / bit definitions for 16c550 uart */
#define UART16550_RBR 0x00
/*receive buffer register | base+00h, dlab=0b r*/
#define UART16550_THR 0x00
/*transmit holding register | base+00h, dlab=0b w*/
#define UART16550_DLL 0x00
/*divisor least significant byte | base+00h, dlab=1b rw*/
#define UART16550_IER 0x01
/*interrupt enable register | base+01h, dlab=0b rw*/
#define UART16550_DLM 0x01
/*divisor most significant byte | base+01h, dlab=1b rw*/
#define UART16550_IIR 0x02
/*interrupt identification register | base+02h, dlab=0b r*/
#define UART16550_FCR 0x02
/*fifo control register | base+02h, dlab=0b w*/
#define UART16550_LCR 0x03
/*line control register | base+03h, dlab=xb rw*/
#define UART16550_MCR 0x04
/*modem control register, only uart0 | base+04h, dlab=xb rw*/
#define UART16550_LSR 0x05
/*line status register | base+05h, dlab=xb r*/
#define UART16550_MSR 0x06
/*modem status register, only uart0 | base+06h, dlab=xb r*/
#define UART16550_SCR 0x07
/*scratch pad register | base+07h, dlab=xb rw*/
#define UART16550_MDR1 0x08
#define UARTML7213_BRCSR 0x0e
/*baud rate reference clock select register dlab xb*/
#define UARTML7213_SRST 0x0f /*Soft Reset Register dlab xb*/
/* value definitions for IIR */
#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
#define IIR_RXTOUT 0x0c
#define IIR_RLS 0x06
#define IIR_RXRDY 0x04
#define IIR_TXRDY 0x02
#define IIR_NOPEND 0x01
#define IIR_MLSC 0x00
#define IER_EDSSI (0x0008)
/*enable/disable modem status interrupt*/
#define IER_ELSI (0x0004)
/*enable/disable receive data error interrupt*/
#define IER_ETBEI (0x0002)
/*enable/disable transmit data write request interrupt*/
#define IER_ERBFI (0x0001)
/*enable/disable receive data read request interrupt*/
/* definition for LCR */
#define LCR_DLAB (1 << 7) /*DLAB THR/RBR&IER or DLL&DLM= Bit 7*/
#define LCR_SB (1 << 6) /*break control on/off= Bit 6*/
#define LCR_SP (1 << 5) /*Specifies the operation of parity bit*/
#define LCR_EPS (1 << 4) /*Specifies the logic of a parity bit*/
#define LCR_PEN (1 << 3) /*Specifies whether to add a parity bit*/
#define LCR_STB (1 << 2) /*stop bit length*/
#define LCR_WL8 (0x03) /*number of bits of serial data*/
#define LCR_WL7 (0x02) /*number of bits of serial data*/
#define LCR_WL6 (0x01) /*number of bits of serial data*/
#define LCR_WL5 (0x00) /*number of bits of serial data*/
#define LCR_PARITY_ODD (LCR_PEN)
#define LCR_PARITY_NONE 0x0
#define LCR_PARITY_EVEN (LCR_PEN | LCR_EPS)
#define LCR_NB_STOP_BITS_1 0x0
#define LCR_NB_STOP_BITS_2 (LCR_STB)
/* bit definitions for LSR */
/* at least one error in data within fifo */
#define LSR_ERR (1 << 7)
/* Transmit data Present */
#define LSR_TEMT (1 << 6)
/* Transmit data write request present */
#define LSR_THRE (1 << 5)
/* Break interrupt data Present */
#define LSR_BI (1 << 4)
/* Framing Error Occurred */
#define LSR_FE (1 << 3)
/* Parity Error Occurred */
#define LSR_PE (1 << 2)
/* Overrun error */
#define LSR_OE (1 << 1)
/* Readable received data is present */
#define LSR_DR (1 << 0)
/* definition for MCR */
#define MCR_RTS (1 << 1) /* Request to Send */
#define MCR_DTR (1 << 0) /* Data Terminal Ready */
/* definition for FCR */
#define FCR_RX_MASK 0xc0
#define FCR_DMA (1 << 3)
#define FCR_TFR (1 << 2) /* Reset Transmit Fifo */
#define FCR_RFR (1 << 1) /* Reset Receive Fifo */
#define FCR_FIFOE (1 << 0) /* Fifo Enable */
#define UART_IER_DISABLE_ALL 0x00000000
#endif /* !UART16550_H */

398
hypervisor/debug/vuart.c Normal file
View File

@@ -0,0 +1,398 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2018 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#include <hypervisor.h>
#include <hv_lib.h>
#include <acrn_common.h>
#include <hv_arch.h>
#include <hv_debug.h>
#include "uart16550.h"
#include "serial_internal.h"
#define COM1_BASE 0x3F8
#define COM1_IRQ 4
#define DEFAULT_RCLK 1843200
#define DEFAULT_BAUD 9600
#define RX_SIZE 256
#define TX_SIZE 65536
#define vuart_lock_init(vu) spinlock_init(&((vu)->lock))
#define vuart_lock(vu) spinlock_obtain(&((vu)->lock))
#define vuart_unlock(vu) spinlock_release(&((vu)->lock))
#define vm_vuart(vm) (vm->vuart)
static void fifo_reset(struct fifo *fifo)
{
fifo->rindex = 0;
fifo->windex = 0;
fifo->num = 0;
}
static void fifo_init(struct fifo *fifo, int sz)
{
fifo->buf = calloc(1, sz);
ASSERT(fifo->buf != NULL, "");
fifo->size = sz;
fifo_reset(fifo);
}
static char fifo_putchar(struct fifo *fifo, char ch)
{
fifo->buf[fifo->windex] = ch;
if (fifo->num < fifo->size) {
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
} else {
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->windex = (fifo->windex + 1) % fifo->size;
}
return 0;
}
static char fifo_getchar(struct fifo *fifo)
{
char c;
if (fifo->num > 0) {
c = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
return c;
} else
return -1;
}
static int fifo_numchars(struct fifo *fifo)
{
return fifo->num;
}
/*
* The IIR returns a prioritized interrupt reason:
* - receive data available
* - transmit holding register empty
*
* Return an interrupt reason if one is available.
*/
static int uart_intr_reason(struct vuart *vu)
{
if ((vu->lsr & LSR_OE) != 0 && (vu->ier & IER_ELSI) != 0)
return IIR_RLS;
else if (fifo_numchars(&vu->rxfifo) > 0 && (vu->ier & IER_ERBFI) != 0)
return IIR_RXTOUT;
else if (vu->thre_int_pending && (vu->ier & IER_ETBEI) != 0)
return IIR_TXRDY;
else
return IIR_NOPEND;
}
static void uart_init(struct vuart *vu)
{
uint16_t divisor;
divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
vu->dll = divisor;
vu->dlh = divisor >> 16;
vu->active = false;
vu->base = COM1_BASE;
fifo_init(&vu->rxfifo, RX_SIZE);
fifo_init(&vu->txfifo, TX_SIZE);
vuart_lock_init(vu);
}
/*
* Toggle the COM port's intr pin depending on whether or not we have an
* interrupt condition to report to the processor.
*/
static void uart_toggle_intr(struct vuart *vu)
{
char intr_reason;
intr_reason = uart_intr_reason(vu);
if (intr_reason != IIR_NOPEND) {
if (vu->vm->vpic)
vpic_assert_irq(vu->vm, COM1_IRQ);
vioapic_assert_irq(vu->vm, COM1_IRQ);
if (vu->vm->vpic)
vpic_deassert_irq(vu->vm, COM1_IRQ);
vioapic_deassert_irq(vu->vm, COM1_IRQ);
}
}
static void uart_write(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t offset,
__unused size_t width, uint32_t value)
{
struct vuart *vu = vm_vuart(vm);
offset -= vu->base;
vuart_lock(vu);
/*
* Take care of the special case DLAB accesses first
*/
if ((vu->lcr & LCR_DLAB) != 0) {
if (offset == UART16550_DLL) {
vu->dll = value;
goto done;
}
if (offset == UART16550_DLM) {
vu->dlh = value;
goto done;
}
}
switch (offset) {
case UART16550_THR:
fifo_putchar(&vu->txfifo, value);
vu->thre_int_pending = true;
break;
case UART16550_IER:
/*
* Apply mask so that bits 4-7 are 0
* Also enables bits 0-3 only if they're 1
*/
vu->ier = value & 0x0F;
break;
case UART16550_FCR:
/*
* The FCR_ENABLE bit must be '1' for the programming
* of other FCR bits to be effective.
*/
if ((value & FCR_FIFOE) == 0) {
vu->fcr = 0;
} else {
if ((value & FCR_RFR) != 0)
fifo_reset(&vu->rxfifo);
vu->fcr = value &
(FCR_FIFOE | FCR_DMA | FCR_RX_MASK);
}
break;
case UART16550_LCR:
vu->lcr = value;
break;
case UART16550_MCR:
/* ignore modem */
break;
case UART16550_LSR:
/*
* Line status register is not meant to be written to
* during normal operation.
*/
break;
case UART16550_MSR:
/*
* As far as I can tell MSR is a read-only register.
*/
break;
case UART16550_SCR:
vu->scr = value;
break;
default:
break;
}
done:
uart_toggle_intr(vu);
vuart_unlock(vu);
}
static uint32_t uart_read(__unused struct vm_io_handler *hdlr,
struct vm *vm, ioport_t offset,
__unused size_t width)
{
char iir, intr_reason, reg;
struct vuart *vu = vm_vuart(vm);
offset -= vu->base;
vuart_lock(vu);
/*
* Take care of the special case DLAB accesses first
*/
if ((vu->lcr & LCR_DLAB) != 0) {
if (offset == UART16550_DLL) {
reg = vu->dll;
goto done;
}
if (offset == UART16550_DLM) {
reg = vu->dlh;
goto done;
}
}
switch (offset) {
case UART16550_RBR:
vu->lsr &= ~LSR_OE;
reg = fifo_getchar(&vu->rxfifo);
break;
case UART16550_IER:
reg = vu->ier;
break;
case UART16550_IIR:
iir = (vu->fcr & FCR_FIFOE) ? IIR_FIFO_MASK : 0;
intr_reason = uart_intr_reason(vu);
/*
* Deal with side effects of reading the IIR register
*/
if (intr_reason == IIR_TXRDY)
vu->thre_int_pending = false;
iir |= intr_reason;
reg = iir;
break;
case UART16550_LCR:
reg = vu->lcr;
break;
case UART16550_MCR:
reg = vu->mcr;
break;
case UART16550_LSR:
/* Transmitter is always ready for more data */
vu->lsr |= LSR_TEMT | LSR_THRE;
/* Check for new receive data */
if (fifo_numchars(&vu->rxfifo) > 0)
vu->lsr |= LSR_DR;
else
vu->lsr &= ~LSR_DR;
reg = vu->lsr;
/* The LSR_OE bit is cleared on LSR read */
vu->lsr &= ~LSR_OE;
break;
case UART16550_MSR:
/* ignore modem I*/
reg = 0;
break;
case UART16550_SCR:
reg = vu->scr;
break;
default:
reg = 0xFF;
break;
}
done:
uart_toggle_intr(vu);
vuart_unlock(vu);
return reg;
}
void vuart_register_io_handler(struct vm *vm)
{
struct vm_io_range range = {
.flags = IO_ATTR_RW,
.base = 0x3f8,
.len = 8
};
register_io_emulation_handler(vm, &range, uart_read, uart_write);
}
void vuart_console_tx_chars(void)
{
struct vuart *vu;
vu = vuart_console_active();
if (vu == NULL)
return;
vuart_lock(vu);
while (fifo_numchars(&vu->txfifo) > 0)
printf("%c", fifo_getchar(&vu->txfifo));
vuart_unlock(vu);
}
void vuart_console_rx_chars(uint32_t serial_handle)
{
struct vuart *vu;
uint32_t vbuf_len;
char buffer[100];
uint32_t buf_idx = 0;
if (serial_handle == SERIAL_INVALID_HANDLE) {
pr_err("%s: invalid serial handle 0x%llx\n",
__func__, serial_handle);
return;
}
vu = vuart_console_active();
if (vu == NULL)
return;
vuart_lock(vu);
/* Get data from serial */
vbuf_len = serial_gets(serial_handle, buffer, 100);
if (vbuf_len) {
while (buf_idx < vbuf_len) {
if (buffer[buf_idx] == GUEST_CONSOLE_TO_HV_SWITCH_KEY) {
/* Switch the console */
shell_switch_console();
break;
}
buf_idx++;
}
if (vu->active != false) {
buf_idx = 0;
while (buf_idx < vbuf_len)
fifo_putchar(&vu->rxfifo, buffer[buf_idx++]);
uart_toggle_intr(vu);
}
}
vuart_unlock(vu);
}
struct vuart *vuart_console_active(void)
{
struct vm *vm = get_vm_from_vmid(0);
if (vm && vm->vuart) {
struct vuart *vu = vm->vuart;
if (vu->active)
return vm->vuart;
}
return NULL;
}
void *vuart_init(struct vm *vm)
{
struct vuart *vu;
vu = calloc(1, sizeof(struct vuart));
ASSERT(vu != NULL, "");
uart_init(vu);
vu->vm = vm;
vuart_register_io_handler(vm);
return vu;
}

View File

@@ -0,0 +1,524 @@
/*-
* Copyright (c) 1996, by Peter Wemm and Steve Passe
* Copyright (c) 2017 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. The name of the developer may NOT be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _APICREG_H_
#define _APICREG_H_
/*
* Local && I/O APIC definitions.
*/
/*
* Pentium P54C+ Built-in APIC
* (Advanced programmable Interrupt Controller)
*
* Base Address of Built-in APIC in memory location
* is 0xfee00000.
*
* Map of APIC Registers:
*
* Offset (hex) Description Read/Write state
* 000 Reserved
* 010 Reserved
* 020 ID Local APIC ID R/W
* 030 VER Local APIC Version R
* 040 Reserved
* 050 Reserved
* 060 Reserved
* 070 Reserved
* 080 Task Priority Register R/W
* 090 Arbitration Priority Register R
* 0A0 Processor Priority Register R
* 0B0 EOI Register W
* 0C0 RRR Remote read R
* 0D0 Logical Destination R/W
* 0E0 Destination Format Register 0..27 R; 28..31 R/W
* 0F0 SVR Spurious Interrupt Vector Reg. 0..3 R; 4..9 R/W
* 100 ISR 000-031 R
* 110 ISR 032-063 R
* 120 ISR 064-095 R
* 130 ISR 095-128 R
* 140 ISR 128-159 R
* 150 ISR 160-191 R
* 160 ISR 192-223 R
* 170 ISR 224-255 R
* 180 TMR 000-031 R
* 190 TMR 032-063 R
* 1A0 TMR 064-095 R
* 1B0 TMR 095-128 R
* 1C0 TMR 128-159 R
* 1D0 TMR 160-191 R
* 1E0 TMR 192-223 R
* 1F0 TMR 224-255 R
* 200 IRR 000-031 R
* 210 IRR 032-063 R
* 220 IRR 064-095 R
* 230 IRR 095-128 R
* 240 IRR 128-159 R
* 250 IRR 160-191 R
* 260 IRR 192-223 R
* 270 IRR 224-255 R
* 280 Error Status Register R
* 290 Reserved
* 2A0 Reserved
* 2B0 Reserved
* 2C0 Reserved
* 2D0 Reserved
* 2E0 Reserved
* 2F0 Local Vector Table (CMCI) R/W
* 300 ICR_LOW Interrupt Command Reg. (0-31) R/W
* 310 ICR_HI Interrupt Command Reg. (32-63) R/W
* 320 Local Vector Table (Timer) R/W
* 330 Local Vector Table (Thermal) R/W (PIV+)
* 340 Local Vector Table (Performance) R/W (P6+)
* 350 LVT1 Local Vector Table (LINT0) R/W
* 360 LVT2 Local Vector Table (LINT1) R/W
* 370 LVT3 Local Vector Table (ERROR) R/W
* 380 Initial Count Reg. for Timer R/W
* 390 Current Count of Timer R
* 3A0 Reserved
* 3B0 Reserved
* 3C0 Reserved
* 3D0 Reserved
* 3E0 Timer Divide Configuration Reg. R/W
* 3F0 Reserved
*/
/******************************************************************************
* global defines, etc.
*/
/******************************************************************************
* LOCAL APIC structure
*/
#ifndef LOCORE
#define PAD3 int: 32; int: 32; int: 32
#define PAD4 int: 32; int: 32; int: 32; int: 32
struct lapic_reg {
uint32_t val; PAD3;
};
struct lapic {
/* reserved */ PAD4;
/* reserved */ PAD4;
uint32_t id; PAD3;
uint32_t version; PAD3;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
uint32_t tpr; PAD3;
uint32_t apr; PAD3;
uint32_t ppr; PAD3;
uint32_t eoi; PAD3;
/* reserved */ PAD4;
uint32_t ldr; PAD3;
uint32_t dfr; PAD3;
uint32_t svr; PAD3;
struct lapic_reg isr[8];
struct lapic_reg tmr[8];
struct lapic_reg irr[8];
uint32_t esr; PAD3;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
uint32_t lvt_cmci; PAD3;
uint32_t icr_lo; PAD3;
uint32_t icr_hi; PAD3;
uint32_t lvt_timer; PAD3;
uint32_t lvt_thermal; PAD3;
uint32_t lvt_pcint; PAD3;
uint32_t lvt_lint0; PAD3;
uint32_t lvt_lint1; PAD3;
uint32_t lvt_error; PAD3;
uint32_t icr_timer; PAD3;
uint32_t ccr_timer; PAD3;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
/* reserved */ PAD4;
uint32_t dcr_timer; PAD3;
/* reserved */ PAD4;
};
enum LAPIC_REGISTERS {
LAPIC_ID = 0x2,
LAPIC_VERSION = 0x3,
LAPIC_TPR = 0x8,
LAPIC_APR = 0x9,
LAPIC_PPR = 0xa,
LAPIC_EOI = 0xb,
LAPIC_LDR = 0xd,
LAPIC_DFR = 0xe, /* Not in x2APIC */
LAPIC_SVR = 0xf,
LAPIC_ISR0 = 0x10,
LAPIC_ISR1 = 0x11,
LAPIC_ISR2 = 0x12,
LAPIC_ISR3 = 0x13,
LAPIC_ISR4 = 0x14,
LAPIC_ISR5 = 0x15,
LAPIC_ISR6 = 0x16,
LAPIC_ISR7 = 0x17,
LAPIC_TMR0 = 0x18,
LAPIC_TMR1 = 0x19,
LAPIC_TMR2 = 0x1a,
LAPIC_TMR3 = 0x1b,
LAPIC_TMR4 = 0x1c,
LAPIC_TMR5 = 0x1d,
LAPIC_TMR6 = 0x1e,
LAPIC_TMR7 = 0x1f,
LAPIC_IRR0 = 0x20,
LAPIC_IRR1 = 0x21,
LAPIC_IRR2 = 0x22,
LAPIC_IRR3 = 0x23,
LAPIC_IRR4 = 0x24,
LAPIC_IRR5 = 0x25,
LAPIC_IRR6 = 0x26,
LAPIC_IRR7 = 0x27,
LAPIC_ESR = 0x28,
LAPIC_LVT_CMCI = 0x2f,
LAPIC_ICR_LO = 0x30,
LAPIC_ICR_HI = 0x31, /* Not in x2APIC */
LAPIC_LVT_TIMER = 0x32,
LAPIC_LVT_THERMAL = 0x33,
LAPIC_LVT_PCINT = 0x34,
LAPIC_LVT_LINT0 = 0x35,
LAPIC_LVT_LINT1 = 0x36,
LAPIC_LVT_ERROR = 0x37,
LAPIC_ICR_TIMER = 0x38,
LAPIC_CCR_TIMER = 0x39,
LAPIC_DCR_TIMER = 0x3e,
LAPIC_SELF_IPI = 0x3f, /* Only in x2APIC */
LAPIC_EXT_FEATURES = 0x40, /* AMD */
LAPIC_EXT_CTRL = 0x41, /* AMD */
LAPIC_EXT_SEOI = 0x42, /* AMD */
LAPIC_EXT_IER0 = 0x48, /* AMD */
LAPIC_EXT_IER1 = 0x49, /* AMD */
LAPIC_EXT_IER2 = 0x4a, /* AMD */
LAPIC_EXT_IER3 = 0x4b, /* AMD */
LAPIC_EXT_IER4 = 0x4c, /* AMD */
LAPIC_EXT_IER5 = 0x4d, /* AMD */
LAPIC_EXT_IER6 = 0x4e, /* AMD */
LAPIC_EXT_IER7 = 0x4f, /* AMD */
LAPIC_EXT_LVT0 = 0x50, /* AMD */
LAPIC_EXT_LVT1 = 0x51, /* AMD */
LAPIC_EXT_LVT2 = 0x52, /* AMD */
LAPIC_EXT_LVT3 = 0x53, /* AMD */
};
#define LAPIC_MEM_MUL 0x10
/*
* Although some registers are available on AMD processors only,
* it's not a big waste to reserve them on all platforms.
* However, we need to watch out for this space being assigned for
* non-APIC purposes in the future processor models.
*/
#define LAPIC_MEM_REGION ((LAPIC_EXT_LVT3 + 1) * LAPIC_MEM_MUL)
/******************************************************************************
* I/O APIC structure
*/
struct ioapic {
uint32_t ioregsel; PAD3;
uint32_t iowin; PAD3;
};
#undef PAD4
#undef PAD3
#endif /* !LOCORE */
/******************************************************************************
* various code 'logical' values
*/
/******************************************************************************
* LOCAL APIC defines
*/
/* default physical locations of LOCAL (CPU) APICs */
#define DEFAULT_APIC_BASE 0xfee00000
/* constants relating to APIC ID registers */
#define APIC_ID_MASK 0xff000000
#define APIC_ID_SHIFT 24
#define APIC_ID_CLUSTER 0xf0
#define APIC_ID_CLUSTER_ID 0x0f
#define APIC_MAX_CLUSTER 0xe
#define APIC_MAX_INTRACLUSTER_ID 3
#define APIC_ID_CLUSTER_SHIFT 4
/* fields in VER */
#define APIC_VER_VERSION 0x000000ff
#define APIC_VER_MAXLVT 0x00ff0000
#define MAXLVTSHIFT 16
#define APIC_VER_EOI_SUPPRESSION 0x01000000
#define APIC_VER_AMD_EXT_SPACE 0x80000000
/* fields in LDR */
#define APIC_LDR_RESERVED 0x00ffffff
/* fields in DFR */
#define APIC_DFR_RESERVED 0x0fffffff
#define APIC_DFR_MODEL_MASK 0xf0000000
#define APIC_DFR_MODEL_FLAT 0xf0000000
#define APIC_DFR_MODEL_CLUSTER 0x00000000
/* fields in SVR */
#define APIC_SVR_VECTOR 0x000000ff
#define APIC_SVR_VEC_PROG 0x000000f0
#define APIC_SVR_VEC_FIX 0x0000000f
#define APIC_SVR_ENABLE 0x00000100
#define APIC_SVR_SWDIS 0x00000000
#define APIC_SVR_SWEN 0x00000100
#define APIC_SVR_FOCUS 0x00000200
#define APIC_SVR_FEN 0x00000000
#define APIC_SVR_FDIS 0x00000200
#define APIC_SVR_EOI_SUPPRESSION 0x00001000
/* fields in TPR */
#define APIC_TPR_PRIO 0x000000ff
#define APIC_TPR_INT 0x000000f0
#define APIC_TPR_SUB 0x0000000f
/* fields in ESR */
#define APIC_ESR_SEND_CS_ERROR 0x00000001
#define APIC_ESR_RECEIVE_CS_ERROR 0x00000002
#define APIC_ESR_SEND_ACCEPT 0x00000004
#define APIC_ESR_RECEIVE_ACCEPT 0x00000008
#define APIC_ESR_SEND_ILLEGAL_VECTOR 0x00000020
#define APIC_ESR_RECEIVE_ILLEGAL_VECTOR 0x00000040
#define APIC_ESR_ILLEGAL_REGISTER 0x00000080
/* fields in ICR_LOW */
#define APIC_VECTOR_MASK 0x000000ff
#define APIC_DELMODE_MASK 0x00000700
#define APIC_DELMODE_FIXED 0x00000000
#define APIC_DELMODE_LOWPRIO 0x00000100
#define APIC_DELMODE_SMI 0x00000200
#define APIC_DELMODE_RR 0x00000300
#define APIC_DELMODE_NMI 0x00000400
#define APIC_DELMODE_INIT 0x00000500
#define APIC_DELMODE_STARTUP 0x00000600
#define APIC_DELMODE_RESV 0x00000700
#define APIC_DESTMODE_MASK 0x00000800
#define APIC_DESTMODE_PHY 0x00000000
#define APIC_DESTMODE_LOG 0x00000800
#define APIC_DELSTAT_MASK 0x00001000
#define APIC_DELSTAT_IDLE 0x00000000
#define APIC_DELSTAT_PEND 0x00001000
#define APIC_RESV1_MASK 0x00002000
#define APIC_LEVEL_MASK 0x00004000
#define APIC_LEVEL_DEASSERT 0x00000000
#define APIC_LEVEL_ASSERT 0x00004000
#define APIC_TRIGMOD_MASK 0x00008000
#define APIC_TRIGMOD_EDGE 0x00000000
#define APIC_TRIGMOD_LEVEL 0x00008000
#define APIC_RRSTAT_MASK 0x00030000
#define APIC_RRSTAT_INVALID 0x00000000
#define APIC_RRSTAT_INPROG 0x00010000
#define APIC_RRSTAT_VALID 0x00020000
#define APIC_RRSTAT_RESV 0x00030000
#define APIC_DEST_MASK 0x000c0000
#define APIC_DEST_DESTFLD 0x00000000
#define APIC_DEST_SELF 0x00040000
#define APIC_DEST_ALLISELF 0x00080000
#define APIC_DEST_ALLESELF 0x000c0000
#define APIC_RESV2_MASK 0xfff00000
#define APIC_ICRLO_RESV_MASK (APIC_RESV1_MASK | APIC_RESV2_MASK)
/* fields in LVT1/2 */
#define APIC_LVT_VECTOR 0x000000ff
#define APIC_LVT_DM 0x00000700
#define APIC_LVT_DM_FIXED 0x00000000
#define APIC_LVT_DM_SMI 0x00000200
#define APIC_LVT_DM_NMI 0x00000400
#define APIC_LVT_DM_INIT 0x00000500
#define APIC_LVT_DM_EXTINT 0x00000700
#define APIC_LVT_DS 0x00001000
#define APIC_LVT_IIPP 0x00002000
#define APIC_LVT_IIPP_INTALO 0x00002000
#define APIC_LVT_IIPP_INTAHI 0x00000000
#define APIC_LVT_RIRR 0x00004000
#define APIC_LVT_TM 0x00008000
#define APIC_LVT_M 0x00010000
/* fields in LVT Timer */
#define APIC_LVTT_VECTOR 0x000000ff
#define APIC_LVTT_DS 0x00001000
#define APIC_LVTT_M 0x00010000
#define APIC_LVTT_TM 0x00060000
#define APIC_LVTT_TM_ONE_SHOT 0x00000000
#define APIC_LVTT_TM_PERIODIC 0x00020000
#define APIC_LVTT_TM_TSCDLT 0x00040000
#define APIC_LVTT_TM_RSRV 0x00060000
/* APIC timer current count */
#define APIC_TIMER_MAX_COUNT 0xffffffff
/* fields in TDCR */
#define APIC_TDCR_2 0x00
#define APIC_TDCR_4 0x01
#define APIC_TDCR_8 0x02
#define APIC_TDCR_16 0x03
#define APIC_TDCR_32 0x08
#define APIC_TDCR_64 0x09
#define APIC_TDCR_128 0x0a
#define APIC_TDCR_1 0x0b
/* Constants related to AMD Extended APIC Features Register */
#define APIC_EXTF_ELVT_MASK 0x00ff0000
#define APIC_EXTF_ELVT_SHIFT 16
#define APIC_EXTF_EXTID_CAP 0x00000004
#define APIC_EXTF_SEIO_CAP 0x00000002
#define APIC_EXTF_IER_CAP 0x00000001
/* LVT table indices */
#define APIC_LVT_LINT0 0
#define APIC_LVT_LINT1 1
#define APIC_LVT_TIMER 2
#define APIC_LVT_ERROR 3
#define APIC_LVT_PMC 4
#define APIC_LVT_THERMAL 5
#define APIC_LVT_CMCI 6
#define APIC_LVT_MAX APIC_LVT_CMCI
/* AMD extended LVT constants, seem to be assigned by fiat */
#define APIC_ELVT_IBS 0 /* Instruction based sampling */
#define APIC_ELVT_MCA 1 /* MCE thresholding */
#define APIC_ELVT_DEI 2 /* Deferred error interrupt */
#define APIC_ELVT_SBI 3 /* Sideband interface */
#define APIC_ELVT_MAX APIC_ELVT_SBI
/******************************************************************************
* I/O APIC defines
*/
/* default physical locations of an IO APIC */
#define DEFAULT_IO_APIC_BASE 0xfec00000
/* window register offset */
#define IOAPIC_WINDOW 0x10
#define IOAPIC_EOIR 0x40
/* indexes into IO APIC */
#define IOAPIC_ID 0x00
#define IOAPIC_VER 0x01
#define IOAPIC_ARB 0x02
#define IOAPIC_REDTBL 0x10
#define IOAPIC_REDTBL0 IOAPIC_REDTBL
#define IOAPIC_REDTBL1 (IOAPIC_REDTBL+0x02)
#define IOAPIC_REDTBL2 (IOAPIC_REDTBL+0x04)
#define IOAPIC_REDTBL3 (IOAPIC_REDTBL+0x06)
#define IOAPIC_REDTBL4 (IOAPIC_REDTBL+0x08)
#define IOAPIC_REDTBL5 (IOAPIC_REDTBL+0x0a)
#define IOAPIC_REDTBL6 (IOAPIC_REDTBL+0x0c)
#define IOAPIC_REDTBL7 (IOAPIC_REDTBL+0x0e)
#define IOAPIC_REDTBL8 (IOAPIC_REDTBL+0x10)
#define IOAPIC_REDTBL9 (IOAPIC_REDTBL+0x12)
#define IOAPIC_REDTBL10 (IOAPIC_REDTBL+0x14)
#define IOAPIC_REDTBL11 (IOAPIC_REDTBL+0x16)
#define IOAPIC_REDTBL12 (IOAPIC_REDTBL+0x18)
#define IOAPIC_REDTBL13 (IOAPIC_REDTBL+0x1a)
#define IOAPIC_REDTBL14 (IOAPIC_REDTBL+0x1c)
#define IOAPIC_REDTBL15 (IOAPIC_REDTBL+0x1e)
#define IOAPIC_REDTBL16 (IOAPIC_REDTBL+0x20)
#define IOAPIC_REDTBL17 (IOAPIC_REDTBL+0x22)
#define IOAPIC_REDTBL18 (IOAPIC_REDTBL+0x24)
#define IOAPIC_REDTBL19 (IOAPIC_REDTBL+0x26)
#define IOAPIC_REDTBL20 (IOAPIC_REDTBL+0x28)
#define IOAPIC_REDTBL21 (IOAPIC_REDTBL+0x2a)
#define IOAPIC_REDTBL22 (IOAPIC_REDTBL+0x2c)
#define IOAPIC_REDTBL23 (IOAPIC_REDTBL+0x2e)
/* fields in VER, for redirection entry */
#define IOAPIC_MAX_RTE_MASK 0x00ff0000
#define MAX_RTE_SHIFT 16
/*
* fields in the IO APIC's redirection table entries
*/
#define IOAPIC_RTE_DEST APIC_ID_MASK /* broadcast addr: all APICs */
#define IOAPIC_RTE_RESV 0x00fe0000 /* reserved */
#define IOAPIC_RTE_INTMASK 0x00010000 /* R/W: INTerrupt mask */
#define IOAPIC_RTE_INTMCLR 0x00000000 /* clear, allow INTs */
#define IOAPIC_RTE_INTMSET 0x00010000 /* set, inhibit INTs */
#define IOAPIC_RTE_TRGRMOD 0x00008000 /* R/W: trigger mode */
#define IOAPIC_RTE_TRGREDG 0x00000000 /* edge */
#define IOAPIC_RTE_TRGRLVL 0x00008000 /* level */
#define IOAPIC_RTE_REM_IRR 0x00004000 /* RO: remote IRR */
#define IOAPIC_RTE_INTPOL 0x00002000 /*R/W:INT input pin polarity*/
#define IOAPIC_RTE_INTAHI 0x00000000 /* active high */
#define IOAPIC_RTE_INTALO 0x00002000 /* active low */
#define IOAPIC_RTE_DELIVS 0x00001000 /* RO: delivery status */
#define IOAPIC_RTE_DESTMOD 0x00000800 /*R/W:destination mode*/
#define IOAPIC_RTE_DESTPHY 0x00000000 /* physical */
#define IOAPIC_RTE_DESTLOG 0x00000800 /* logical */
#define IOAPIC_RTE_DELMOD 0x00000700 /* R/W: delivery mode */
#define IOAPIC_RTE_DELFIXED 0x00000000 /* fixed */
#define IOAPIC_RTE_DELLOPRI 0x00000100 /* lowest priority */
#define IOAPIC_RTE_DELSMI 0x00000200 /*System Management INT*/
#define IOAPIC_RTE_DELRSV1 0x00000300 /* reserved */
#define IOAPIC_RTE_DELNMI 0x00000400 /* NMI signal */
#define IOAPIC_RTE_DELINIT 0x00000500 /* INIT signal */
#define IOAPIC_RTE_DELRSV2 0x00000600 /* reserved */
#define IOAPIC_RTE_DELEXINT 0x00000700 /* External INTerrupt */
#define IOAPIC_RTE_INTVEC 0x000000ff /*R/W: INT vector field*/
#endif /* _APICREG_H_ */

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 ASSIGN_H
#define ASSIGN_H
enum ptdev_intr_type {
PTDEV_INTR_MSI,
PTDEV_INTR_INTX
};
enum ptdev_vpin_source {
PTDEV_VPIN_IOAPIC,
PTDEV_VPIN_PIC,
};
/* entry per guest virt vector */
struct ptdev_msi_info {
uint32_t vmsi_addr; /* virt msi_addr */
uint32_t vmsi_data; /* virt msi_data */
uint16_t vmsi_ctl; /* virt msi_ctl */
uint32_t pmsi_addr; /* phys msi_addr */
uint32_t pmsi_data; /* phys msi_data */
int msix; /* 0-MSI, 1-MSIX */
int msix_entry_index; /* MSI: 0, MSIX: index of vector table*/
int virt_vector;
int phys_vector;
};
/* entry per guest vioapic pin */
struct ptdev_intx_info {
enum ptdev_vpin_source vpin_src;
uint8_t virt_pin;
uint8_t phys_pin;
};
/* entry per each allocated irq/vector */
struct ptdev_remapping_info {
struct vm *vm;
uint16_t virt_bdf; /* PCI bus:slot.func*/
uint16_t phys_bdf; /* PCI bus:slot.func*/
uint32_t active; /* 1=active, 0=inactive and to free*/
enum ptdev_intr_type type;
struct dev_handler_node *node;
struct list_head softirq_node;
struct list_head entry_node;
union {
struct ptdev_msi_info msi;
struct ptdev_intx_info intx;
};
};
void ptdev_intx_ack(struct vm *vm, int virt_pin,
enum ptdev_vpin_source vpin_src);
int ptdev_msix_remap(struct vm *vm, uint16_t virt_bdf,
struct ptdev_msi_info *info);
int ptdev_intx_pin_remap(struct vm *vm, struct ptdev_intx_info *info);
void ptdev_softirq(int cpu);
void ptdev_init(void);
void ptdev_vm_init(struct vm *vm);
void ptdev_vm_deinit(struct vm *vm);
void ptdev_add_intx_remapping(struct vm *vm, uint16_t virt_bdf,
uint16_t phys_bdf, uint8_t virt_pin, uint8_t phys_pin, bool pic_pin);
void ptdev_remove_intx_remapping(struct vm *vm, uint8_t virt_pin, bool pic_pin);
void ptdev_add_msix_remapping(struct vm *vm, uint16_t virt_bdf,
uint16_t phys_bdf, int vector_count);
void ptdev_remove_msix_remapping(struct vm *vm, uint16_t virt_bdf,
int vector_count);
int get_ptdev_info(char *str, int str_max);
#endif /* ASSIGN_H */

View File

@@ -0,0 +1,412 @@
/*-
* Copyright (c) 1989, 1990 William F. Jolitz
* Copyright (c) 1990 The Regents of the University of California.
* Copyright (c) 2017 Intel Corporation
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* 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 University 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 REGENTS 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 REGENTS 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.
*
* from: @(#)segments.h 7.1 (Berkeley) 5/9/91
* $FreeBSD$
*/
#ifndef CPU_H
#define CPU_H
/* Define page size */
#define CPU_PAGE_SHIFT 12
#define CPU_PAGE_SIZE 0x1000
/* Define CPU stack alignment */
#define CPU_STACK_ALIGN 16
/* CR0 register definitions */
#define CR0_PG (1<<31) /* paging enable */
#define CR0_CD (1<<30) /* cache disable */
#define CR0_NW (1<<29) /* not write through */
#define CR0_AM (1<<18) /* alignment mask */
#define CR0_WP (1<<16) /* write protect */
#define CR0_NE (1<<5) /* numeric error */
#define CR0_ET (1<<4) /* extension type */
#define CR0_TS (1<<3) /* task switched */
#define CR0_EM (1<<2) /* emulation */
#define CR0_MP (1<<1) /* monitor coprocessor */
#define CR0_PE (1<<0) /* protected mode enabled */
/* CR3 register definitions */
#define CR3_PWT (1<<3) /* page-level write through */
#define CR3_PCD (1<<4) /* page-level cache disable */
/* CR4 register definitions */
#define CR4_VME (1<<0) /* virtual 8086 mode extensions */
#define CR4_PVI (1<<1) /* protected mode virtual interrupts */
#define CR4_TSD (1<<2) /* time stamp disable */
#define CR4_DE (1<<3) /* debugging extensions */
#define CR4_PSE (1<<4) /* page size extensions */
#define CR4_PAE (1<<5) /* physical address extensions */
#define CR4_MCE (1<<6) /* machine check enable */
#define CR4_PGE (1<<7) /* page global enable */
#define CR4_PCE (1<<8)
/* performance monitoring counter enable */
#define CR4_OSFXSR (1<<9) /* OS support for FXSAVE/FXRSTOR */
#define CR4_OSXMMEXCPT (1<<10)
/* OS support for unmasked SIMD floating point exceptions */
#define CR4_VMXE (1<<13) /* VMX enable */
#define CR4_SMXE (1<<14) /* SMX enable */
#define CR4_PCIDE (1<<17) /* PCID enable */
#define CR4_OSXSAVE (1<<18)
/* XSAVE and Processor Extended States enable bit */
/*
* Entries in the Interrupt Descriptor Table (IDT)
*/
#define IDT_DE 0 /* #DE: Divide Error */
#define IDT_DB 1 /* #DB: Debug */
#define IDT_NMI 2 /* Nonmaskable External Interrupt */
#define IDT_BP 3 /* #BP: Breakpoint */
#define IDT_OF 4 /* #OF: Overflow */
#define IDT_BR 5 /* #BR: Bound Range Exceeded */
#define IDT_UD 6 /* #UD: Undefined/Invalid Opcode */
#define IDT_NM 7 /* #NM: No Math Coprocessor */
#define IDT_DF 8 /* #DF: Double Fault */
#define IDT_FPUGP 9 /* Coprocessor Segment Overrun */
#define IDT_TS 10 /* #TS: Invalid TSS */
#define IDT_NP 11 /* #NP: Segment Not Present */
#define IDT_SS 12 /* #SS: Stack Segment Fault */
#define IDT_GP 13 /* #GP: General Protection Fault */
#define IDT_PF 14 /* #PF: Page Fault */
#define IDT_MF 16 /* #MF: FPU Floating-Point Error */
#define IDT_AC 17 /* #AC: Alignment Check */
#define IDT_MC 18 /* #MC: Machine Check */
#define IDT_XF 19 /* #XF: SIMD Floating-Point Exception */
/*Bits in EFER special registers */
#define EFER_LMA 0x000000400 /* Long mode active (R) */
/* CPU clock frequencies (FSB) */
#define CPU_FSB_83KHZ 83200
#define CPU_FSB_100KHZ 99840
#define CPU_FSB_133KHZ 133200
#define CPU_FSB_166KHZ 166400
/* Time conversions */
#define CPU_GHZ_TO_HZ 1000000000
#define CPU_GHZ_TO_KHZ 1000000
#define CPU_GHZ_TO_MHZ 1000
#define CPU_MHZ_TO_HZ 1000000
#define CPU_MHZ_TO_KHZ 1000
/* Boot CPU ID */
#define CPU_BOOT_ID 0
/* CPU states defined */
#define CPU_STATE_RESET 0
#define CPU_STATE_INITIALIZING 1
#define CPU_STATE_RUNNING 2
#define CPU_STATE_HALTED 3
#define CPU_STATE_DEAD 4
/* hypervisor stack bottom magic('intl') */
#define SP_BOTTOM_MAGIC 0x696e746c
/* type of speculation control
* 0 - no speculation control support
* 1 - raw IBRS + IPBP support
* 2 - with STIBP optimization support
*/
#define IBRS_NONE 0
#define IBRS_RAW 1
#define IBRS_OPT 2
#ifndef ASSEMBLER
/**********************************/
/* EXTERNAL VARIABLES */
/**********************************/
extern const uint8_t _ld_cpu_secondary_reset_load[];
extern uint8_t _ld_cpu_secondary_reset_start[];
extern const uint64_t _ld_cpu_secondary_reset_size;
extern uint8_t _ld_bss_start[];
extern uint8_t _ld_bss_end[];
extern uint8_t _ld_cpu_data_start[];
extern uint8_t _ld_cpu_data_end[];
extern int ibrs_type;
/*
* To support per_cpu access, we use a special section ".cpu_data" to define
* the pattern of per CPU data. And we allocate memory for per CPU data
* according to multiple this section size and pcpu number.
*
* +------------------+------------------+---+------------------+
* | percpu for pcpu0 | percpu for pcpu1 |...| percpu for pcpuX |
* +------------------+------------------+---+------------------+
* ^ ^
* | |
* --.cpu_data size--
*
* To access per cpu data, we use:
* per_cpu_data_base_ptr + curr_pcpu_id * cpu_data_section_size +
* offset_of_symbol_in_cpu_data_section
* to locate the per cpu data.
*/
/* declare per cpu data */
#define EXTERN_CPU_DATA(type, name) \
extern __typeof__(type) cpu_data_##name
EXTERN_CPU_DATA(uint8_t, lapic_id);
EXTERN_CPU_DATA(void *, vcpu);
EXTERN_CPU_DATA(uint8_t[STACK_SIZE], stack) __aligned(16);
/* define per cpu data */
#define DEFINE_CPU_DATA(type, name) \
__typeof__(type) cpu_data_##name \
__attribute__((__section__(".cpu_data")))
extern void *per_cpu_data_base_ptr;
extern int phy_cpu_num;
#define PER_CPU_DATA_OFFSET(sym_addr) \
((uint64_t)(sym_addr) - (uint64_t)(_ld_cpu_data_start))
#define PER_CPU_DATA_SIZE \
((uint64_t)_ld_cpu_data_end - (uint64_t)(_ld_cpu_data_start))
/*
* get percpu data for pcpu_id.
*
* It returns:
* per_cpu_data_##name[pcpu_id];
*/
#define per_cpu(name, pcpu_id) \
(*({ uint64_t base = (uint64_t)per_cpu_data_base_ptr; \
uint64_t off = PER_CPU_DATA_OFFSET(&cpu_data_##name); \
((typeof(&cpu_data_##name))(base + \
(pcpu_id) * PER_CPU_DATA_SIZE + off)); \
}))
/* get percpu data for current pcpu */
#define get_cpu_var(name) per_cpu(name, get_cpu_id())
/* Function prototypes */
void cpu_halt(uint32_t logical_id);
uint64_t cpu_cycles_per_second(void);
uint64_t tsc_cycles_in_period(uint16_t timer_period_in_us);
void cpu_secondary_reset(void);
int hv_main(int cpu_id);
bool check_tsc_adjust_support(void);
bool check_ibrs_ibpb_support(void);
bool check_stibp_support(void);
bool is_apicv_enabled(void);
/* Read control register */
#define CPU_CR_READ(cr, result_ptr) \
{ \
asm volatile ("mov %%" __CPP_STRING(cr) ", %0" \
: "=r"(*result_ptr)); \
}
/* Write control register */
#define CPU_CR_WRITE(cr, value) \
{ \
asm volatile ("mov %0, %%" __CPP_STRING(cr) \
: /* No output */ \
: "r"(value)); \
}
/* Read MSR */
#define CPU_MSR_READ(reg, msr_val_ptr) \
{ \
uint32_t msrl, msrh; \
asm volatile (" rdmsr ":"=a"(msrl), \
"=d"(msrh) : "c" (reg)); \
*msr_val_ptr = ((uint64_t)msrh<<32) | msrl; \
}
/* Write MSR */
#define CPU_MSR_WRITE(reg, msr_val) \
{ \
uint32_t msrl, msrh; \
msrl = (uint32_t)msr_val; \
msrh = (uint32_t)(msr_val >> 32); \
asm volatile (" wrmsr " : : "c" (reg), \
"a" (msrl), "d" (msrh)); \
}
/* Disables interrupts on the current CPU */
#define CPU_IRQ_DISABLE() \
{ \
asm volatile ("cli\n" : : : "cc"); \
}
/* Enables interrupts on the current CPU */
#define CPU_IRQ_ENABLE() \
{ \
asm volatile ("sti\n" : : : "cc"); \
}
/* This macro writes the stack pointer. */
#define CPU_SP_WRITE(stack_ptr) \
{ \
uint64_t rsp = (uint64_t)stack_ptr & ~(CPU_STACK_ALIGN - 1); \
asm volatile ("movq %0, %%rsp" : : "r"(rsp)); \
}
/* Synchronizes all read accesses from memory */
#define CPU_MEMORY_READ_BARRIER() \
{ \
asm volatile ("lfence\n" : : : "memory"); \
}
/* Synchronizes all write accesses to memory */
#define CPU_MEMORY_WRITE_BARRIER() \
{ \
asm volatile ("sfence\n" : : : "memory"); \
}
/* Synchronizes all read and write accesses to/from memory */
#define CPU_MEMORY_BARRIER() \
{ \
asm volatile ("mfence\n" : : : "memory"); \
}
/* Write the task register */
#define CPU_LTR_EXECUTE(ltr_ptr) \
{ \
asm volatile ("ltr %%ax\n" : : "a"(ltr_ptr)); \
}
/* Read time-stamp counter / processor ID */
#define CPU_RDTSCP_EXECUTE(timestamp_ptr, cpu_id_ptr) \
{ \
uint32_t tsl, tsh; \
asm volatile ("rdtscp":"=a"(tsl), "=d"(tsh), \
"=c"(*cpu_id_ptr)); \
*timestamp_ptr = ((uint64_t)tsh << 32) | tsl; \
}
/* Define variable(s) required to save / restore architecture interrupt state.
* These variable(s) are used in conjunction with the ESAL_AR_INT_ALL_DISABLE()
* and ESAL_AR_INT_ALL_RESTORE() macros to hold any data that must be preserved
* in order to allow these macros to function correctly.
*/
#define CPU_INT_CONTROL_VARS uint64_t cpu_int_value
/* Macro to save rflags register */
#define CPU_RFLAGS_SAVE(rflags_ptr) \
{ \
asm volatile (" pushf"); \
asm volatile (" pop %0" \
: "=r" (*(rflags_ptr)) \
: /* No inputs */); \
}
/* Macro to restore rflags register */
#define CPU_RFLAGS_RESTORE(rflags) \
{ \
asm volatile (" push %0" : : "r" (rflags)); \
asm volatile (" popf"); \
}
/* This macro locks out interrupts and saves the current architecture status
* register / state register to the specified address. This function does not
* attempt to mask any bits in the return register value and can be used as a
* quick method to guard a critical section.
* NOTE: This macro is used in conjunction with CPU_INT_ALL_RESTORE
* defined below and CPU_INT_CONTROL_VARS defined above.
*/
#define CPU_INT_ALL_DISABLE() \
{ \
CPU_RFLAGS_SAVE(&cpu_int_value); \
CPU_IRQ_DISABLE(); \
}
/* This macro restores the architecture status / state register used to lockout
* interrupts to the value provided. The intent of this function is to be a
* fast mechanism to restore the interrupt level at the end of a critical
* section to its original level.
* NOTE: This macro is used in conjunction with CPU_INT_ALL_DISABLE
* and CPU_INT_CONTROL_VARS defined above.
*/
#define CPU_INT_ALL_RESTORE() \
{ \
CPU_RFLAGS_RESTORE(cpu_int_value); \
}
/* Macro to get CPU ID */
static inline uint32_t get_cpu_id(void)
{
uint32_t tsl, tsh, cpu_id;
asm volatile ("rdtscp":"=a" (tsl), "=d"(tsh), "=c"(cpu_id)::);
return cpu_id;
}
static inline uint64_t cpu_rsp_get(void)
{
uint64_t ret;
asm volatile("movq %%rsp, %0"
: "=r"(ret));
return ret;
}
static inline uint64_t cpu_rbp_get(void)
{
uint64_t ret;
asm volatile("movq %%rbp, %0"
: "=r"(ret));
return ret;
}
static inline uint64_t
msr_read(uint32_t reg_num)
{
uint64_t msr_val;
CPU_MSR_READ(reg_num, &msr_val);
return msr_val;
}
static inline void
msr_write(uint32_t reg_num, uint64_t value64)
{
CPU_MSR_WRITE(reg_num, value64);
}
#else /* ASSEMBLER defined */
#endif /* ASSEMBLER defined */
#endif /* CPU_H */

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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.
*/
/*
* cpuid.h
*
* Created on: Jan 4, 2018
* Author: don
*/
#ifndef CPUID_H_
#define CPUID_H_
/* CPUID bit definitions */
#define CPUID_ECX_SSE3 (1<<0)
#define CPUID_ECX_PCLMUL (1<<1)
#define CPUID_ECX_DTES64 (1<<2)
#define CPUID_ECX_MONITOR (1<<3)
#define CPUID_ECX_DS_CPL (1<<4)
#define CPUID_ECX_VMX (1<<5)
#define CPUID_ECX_SMX (1<<6)
#define CPUID_ECX_EST (1<<7)
#define CPUID_ECX_TM2 (1<<8)
#define CPUID_ECX_SSSE3 (1<<9)
#define CPUID_ECX_CID (1<<10)
#define CPUID_ECX_FMA (1<<12)
#define CPUID_ECX_CX16 (1<<13)
#define CPUID_ECX_ETPRD (1<<14)
#define CPUID_ECX_PDCM (1<<15)
#define CPUID_ECX_DCA (1<<18)
#define CPUID_ECX_SSE4_1 (1<<19)
#define CPUID_ECX_SSE4_2 (1<<20)
#define CPUID_ECX_x2APIC (1<<21)
#define CPUID_ECX_MOVBE (1<<22)
#define CPUID_ECX_POPCNT (1<<23)
#define CPUID_ECX_AES (1<<25)
#define CPUID_ECX_XSAVE (1<<26)
#define CPUID_ECX_OSXSAVE (1<<27)
#define CPUID_ECX_AVX (1<<28)
#define CPUID_EDX_FPU (1<<0)
#define CPUID_EDX_VME (1<<1)
#define CPUID_EDX_DE (1<<2)
#define CPUID_EDX_PSE (1<<3)
#define CPUID_EDX_TSC (1<<4)
#define CPUID_EDX_MSR (1<<5)
#define CPUID_EDX_PAE (1<<6)
#define CPUID_EDX_MCE (1<<7)
#define CPUID_EDX_CX8 (1<<8)
#define CPUID_EDX_APIC (1<<9)
#define CPUID_EDX_SEP (1<<11)
#define CPUID_EDX_MTRR (1<<12)
#define CPUID_EDX_PGE (1<<13)
#define CPUID_EDX_MCA (1<<14)
#define CPUID_EDX_CMOV (1<<15)
#define CPUID_EDX_PAT (1<<16)
#define CPUID_EDX_PSE36 (1<<17)
#define CPUID_EDX_PSN (1<<18)
#define CPUID_EDX_CLF (1<<19)
#define CPUID_EDX_DTES (1<<21)
#define CPUID_EDX_ACPI (1<<22)
#define CPUID_EDX_MMX (1<<23)
#define CPUID_EDX_FXSR (1<<24)
#define CPUID_EDX_SSE (1<<25)
#define CPUID_EDX_SSE2 (1<<26)
#define CPUID_EDX_SS (1<<27)
#define CPUID_EDX_HTT (1<<28)
#define CPUID_EDX_TM1 (1<<29)
#define CPUID_EDX_IA64 (1<<30)
#define CPUID_EDX_PBE (1<<31)
/* CPUID.07H:EBX.TSC_ADJUST*/
#define CPUID_EBX_TSC_ADJ (1<<1)
/* CPUID.07H:EDX.IBRS_IBPB*/
#define CPUID_EDX_IBRS_IBPB (1<<26)
/* CPUID.07H:EDX.STIBP*/
#define CPUID_EDX_STIBP (1<<27)
/* CPUID.80000001H:EDX.Page1GB*/
#define CPUID_EDX_PAGE1GB (1<<26)
/* CPUID.07H:EBX.INVPCID*/
#define CPUID_EBX_INVPCID (1<<10)
/* CPUID.01H:ECX.PCID*/
#define CPUID_ECX_PCID (1<<17)
/* CPUID source operands */
#define CPUID_VENDORSTRING 0
#define CPUID_FEATURES 1
#define CPUID_TLB 2
#define CPUID_SERIALNUM 3
#define CPUID_EXTEND_FEATURE 7
#define CPUID_EXTEND_FUNCTION_1 0x80000001
enum cpuid_cache_idx {
CPUID_VENDORSTRING_CACHE_IDX = 0,
CPUID_FEATURES_CACHE_IDX,
CPUID_EXTEND_FEATURE_CACHE_IDX,
CPUID_EXTEND_FEATURE_CACHE_MAX
};
struct cpuid_cache_entry {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
uint32_t inited;
uint32_t reserved;
};
static inline void native_cpuid_count(uint32_t op, uint32_t count,
uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
{
/* Execute CPUID instruction and save results */
asm volatile("cpuid":"=a"(*a), "=b"(*b),
"=c"(*c), "=d"(*d)
: "a"(op), "c" (count));
}
void cpuid_count(uint32_t op, uint32_t count,
uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d);
#define cpuid(op, a, b, c, d) cpuid_count(op, 0, a, b, c, d)
void emulate_cpuid(struct vcpu *vcpu, uint32_t src_op, uint32_t *eax_ptr,
uint32_t *ebx_ptr, uint32_t *ecx_ptr, uint32_t *edx_ptr);
#endif /* CPUID_H_ */

View File

@@ -0,0 +1,314 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 GDT_H
#define GDT_H
/* GDT is defined in assembly so it can be used to switch modes before long mode
* is established.
* With 64-bit EFI this is not required since are already in long mode when EFI
* transfers control to the hypervisor. However, for any instantiation of the
* ACRN Hypervisor that requires a boot from reset the GDT will be
* used as mode transitions are being made to ultimately end up in long mode.
* For this reason we establish the GDT in assembly.
* This should not affect usage and convenience of interacting with the GDT in C
* as the complete definition of the GDT is driven by the defines in this file.
*
* Unless it proves to be not viable we will use a single GDT for all hypervisor
* CPUs, with space for per CPU LDT and TSS.
*/
/*
* Segment selectors in x86-64 and i386 are the same size, 8 bytes.
* Local Descriptor Table (LDT) selectors are 16 bytes on x86-64 instead of 8
* bytes.
* Task State Segment (TSS) selectors are 16 bytes on x86-64 instead of 8 bytes.
*/
#define X64_SEG_DESC_SIZE (0x8) /* In long mode SEG Descriptors are 8 bytes */
#define X64_LDT_DESC_SIZE (0x10)/* In long mode LDT Descriptors are 16 bytes */
#define X64_TSS_DESC_SIZE (0x10)/* In long mode TSS Descriptors are 16 bytes */
/*****************************************************************************
*
* BEGIN: Definition of the GDT.
*
* NOTE:
* If you change the size of the GDT or rearrange the location of descriptors
* within the GDT you must change both the defines and the C structure header.
*
*****************************************************************************/
/* Number of global 8 byte segments descriptor(s) */
#define HOST_GDT_RING0_SEG_SELECTORS (0x3) /* rsvd, code, data */
/* Offsets of global 8 byte segment descriptors */
#define HOST_GDT_RING0_RSVD_SEL (0x0000)
#define HOST_GDT_RING0_CODE_SEL (0x0008)
#define HOST_GDT_RING0_DATA_SEL (0x0010)
/* Number of global 16 byte LDT descriptor(s) */
#define HOST_GDT_RING0_TSS_SELECTORS (0x1)
/* One for each CPU in the hypervisor. */
/*****************************************************************************
*
* END: Definition of the GDT.
*
*****************************************************************************/
/* Offset to start of LDT Descriptors */
#define HOST_GDT_RING0_LDT_SEL \
(HOST_GDT_RING0_SEG_SELECTORS * X64_SEG_DESC_SIZE)
/* Offset to start of LDT Descriptors */
#define HOST_GDT_RING0_CPU_TSS_SEL (HOST_GDT_RING0_LDT_SEL)
/* Size of the GDT */
#define HOST_GDT_SIZE \
(HOST_GDT_RING0_CPU_TSS_SEL + \
(HOST_GDT_RING0_TSS_SELECTORS * X64_TSS_DESC_SIZE))
/* Defined position of Interrupt Stack Tables */
#define MACHINE_CHECK_IST (0x1)
#define DOUBLE_FAULT_IST (0x2)
#define STACK_FAULT_IST (0x3)
#ifndef ASSEMBLER
#include <types.h>
#include <cpu.h>
#define TSS_AVAIL (9)
/*
* Definition of an 8 byte code segment descriptor.
*/
union code_segment_descriptor {
uint64_t value;
struct {
union {
uint32_t value;
struct {
uint32_t limit_15_0:16;
uint32_t base_15_0:16;
} bits;
} low32;
union {
uint32_t value;
struct {
uint32_t base_23_16:8;
uint32_t accessed:1;
uint32_t readeable:1;
uint32_t conforming:1;
uint32_t bit11_set:1;
uint32_t bit12_set:1;
uint32_t dpl:2;
uint32_t present:1;
uint32_t limit_19_16:4;
uint32_t avl:1;
uint32_t x64flag:1;
uint32_t dflt:1;
uint32_t granularity:1;
uint32_t base_31_24:8;
} bits;
} high32;
};
} __aligned(8);
/*
* Definition of an 8 byte data segment descriptor.
*/
union data_segment_descriptor {
uint64_t value;
struct {
union {
uint32_t value;
struct {
uint32_t limit_15_0:16;
uint32_t base_15_0:16;
} bits;
} low32;
union {
uint32_t value;
struct {
uint32_t base_23_16:8;
uint32_t accessed:1;
uint32_t writeable:1;
uint32_t expansion:1;
uint32_t bit11_clr:1;
uint32_t bit12_set:1;
uint32_t dpl:2;
uint32_t present:1;
uint32_t limit_19_16:4;
uint32_t avl:1;
uint32_t rsvd_clr:1;
uint32_t big:1;
uint32_t granularity:1;
uint32_t base_31_24:8;
} bits;
} high32;
};
} __aligned(8);
/*
* Definition of an 8 byte system segment descriptor.
*/
union system_segment_descriptor {
uint64_t value;
struct {
union {
uint32_t value;
struct {
uint32_t limit_15_0:16;
uint32_t base_15_0:16;
} bits;
} low32;
union {
uint32_t value;
struct {
uint32_t base_23_16:8;
uint32_t type:4;
uint32_t bit12_clr:1;
uint32_t dpl:2;
uint32_t present:1;
uint32_t limit_19_16:4;
uint32_t rsvd_1:1;
uint32_t rsvd_2_clr:1;
uint32_t rsvd_3:1;
uint32_t granularity:1;
uint32_t base_31_24:8;
} bits;
} high32;
};
} __aligned(8);
/*
* Definition of 16 byte TSS and LDT selectors.
*/
union tss_64_descriptor {
uint64_t value;
struct {
union {
uint32_t value;
struct {
uint32_t limit_15_0:16;
uint32_t base_15_0:16;
} bits;
} low32;
union {
uint32_t value;
struct {
uint32_t base_23_16:8;
uint32_t type:4;
uint32_t bit12_clr:1;
uint32_t dpl:2;
uint32_t present:1;
uint32_t limit_19_16:4;
uint32_t rsvd_1:1;
uint32_t rsvd_2_clr:1;
uint32_t rsvd_3:1;
uint32_t granularity:1;
uint32_t base_31_24:8;
} bits;
} high32;
uint32_t base_addr_63_32;
union {
uint32_t value;
struct {
uint32_t rsvd_7_0:8;
uint32_t bits_12_8_clr:4;
uint32_t rsvd_31_13:20;
} bits;
} offset_12;
};
} __aligned(8);
/*****************************************************************************
*
* BEGIN: Definition of the GDT.
*
* NOTE:
* If you change the size of the GDT or rearrange the location of descriptors
* within the GDT you must change both the defines and the C structure header.
*
*****************************************************************************/
struct host_gdt {
uint64_t rsvd;
union code_segment_descriptor host_gdt_code_descriptor;
union data_segment_descriptor host_gdt_data_descriptor;
union tss_64_descriptor host_gdt_tss_descriptors;
} __aligned(8);
/*****************************************************************************
*
* END: Definition of the GDT.
*
*****************************************************************************/
/*
* x86-64 Task State Segment (TSS) definition.
*/
struct tss_64 {
uint32_t rsvd1;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint32_t rsvd2;
uint32_t rsvd3;
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint32_t rsvd4;
uint32_t rsvd5;
uint16_t rsvd6;
uint16_t io_map_base_addr;
} __packed __aligned(16);
/*
* Definition of the GDT descriptor.
*/
struct host_gdt_descriptor {
unsigned short len;
struct host_gdt *gdt;
} __packed;
extern struct host_gdt HOST_GDT;
extern struct host_gdt_descriptor HOST_GDTR;
void load_gdtr_and_tr(void);
EXTERN_CPU_DATA(struct tss_64, tss);
EXTERN_CPU_DATA(struct host_gdt, gdt);
EXTERN_CPU_DATA(uint8_t[STACK_SIZE], mc_stack) __aligned(16);
EXTERN_CPU_DATA(uint8_t[STACK_SIZE], df_stack) __aligned(16);
EXTERN_CPU_DATA(uint8_t[STACK_SIZE], sf_stack) __aligned(16);
#endif /* end #ifndef ASSEMBLER */
#endif /* GDT_H */

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 GUEST_H
#define GUEST_H
/* Defines for VM Launch and Resume */
#define VM_RESUME 0
#define VM_LAUNCH 1
#define ACRN_DBG_PTIRQ 6
#define ACRN_DBG_IRQ 6
#ifndef ASSEMBLER
#define foreach_vcpu(idx, vm, vcpu) \
for (idx = 0, vcpu = vm->hw.vcpu_array[idx]; \
(idx < vm->hw.num_vcpus) & (vcpu != NULL); \
idx++, vcpu = vm->hw.vcpu_array[idx])
struct vhm_request;
/*
* VCPU related APIs
*/
#define ACRN_REQUEST_EVENT 0
#define ACRN_REQUEST_EXTINT 1
#define ACRN_REQUEST_NMI 2
#define ACRN_REQUEST_GP 3
#define ACRN_REQUEST_TMR_UPDATE 4
#define ACRN_REQUEST_TLB_FLUSH 5
#define E820_MAX_ENTRIES 32
struct e820_mem_params {
uint64_t mem_bottom;
uint64_t mem_top;
uint64_t max_ram_blk_base; /* used for the start address of UOS */
uint64_t max_ram_blk_size;
};
int prepare_vm0_memmap_and_e820(struct vm *vm);
/* Definition for a mem map lookup */
struct vm_lu_mem_map {
struct list_head list; /* EPT mem map lookup list*/
void *hpa; /* Host physical start address of the map*/
void *gpa; /* Guest physical start address of the map */
uint64_t size; /* Size of map */
};
/*
* VM related APIs
*/
bool is_vm0(struct vm *vm);
bool vm_lapic_disabled(struct vm *vm);
uint64_t vcpumask2pcpumask(struct vm *vm, uint64_t vdmask);
int init_vm0_boot_info(struct vm *vm);
uint64_t gva2gpa(struct vm *vm, uint64_t cr3, uint64_t gva);
struct vcpu *get_primary_vcpu(struct vm *vm);
struct vcpu *vcpu_from_vid(struct vm *vm, int vcpu_id);
struct vcpu *vcpu_from_pid(struct vm *vm, int pcpu_id);
void init_e820(void);
void obtain_e820_mem_info(void);
extern uint32_t e820_entries;
extern struct e820_entry e820[E820_MAX_ENTRIES];
extern uint32_t boot_regs[];
extern struct e820_mem_params e820_mem;
int rdmsr_handler(struct vcpu *vcpu);
int wrmsr_handler(struct vcpu *vcpu);
void init_msr_emulation(struct vcpu *vcpu);
extern const char vm_exit[];
int vmx_vmrun(struct run_context *context, int ops, int ibrs);
int load_guest(struct vm *vm, struct vcpu *vcpu);
int general_sw_loader(struct vm *vm, struct vcpu *vcpu);
typedef int (*vm_sw_loader_t)(struct vm *, struct vcpu *);
extern vm_sw_loader_t vm_sw_loader;
#endif /* !ASSEMBLER */
#endif /* GUEST_H*/

View File

@@ -0,0 +1,288 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 _VCPU_H_
#define _VCPU_H_
#define ACRN_VCPU_MMIO_COMPLETE (0)
/* Size of various elements within the VCPU structure */
#define REG_SIZE 8
/* Number of GPRs saved / restored for guest in VCPU structure */
#define NUM_GPRS 15
#define GUEST_STATE_AREA_SIZE 512
/* Indexes of GPRs saved / restored for guest */
#define VMX_MACHINE_T_GUEST_RAX_INDEX 0
#define VMX_MACHINE_T_GUEST_RBX_INDEX 1
#define VMX_MACHINE_T_GUEST_RCX_INDEX 2
#define VMX_MACHINE_T_GUEST_RDX_INDEX 3
#define VMX_MACHINE_T_GUEST_RBP_INDEX 4
#define VMX_MACHINE_T_GUEST_RSI_INDEX 5
#define VMX_MACHINE_T_GUEST_R8_INDEX 6
#define VMX_MACHINE_T_GUEST_R9_INDEX 7
#define VMX_MACHINE_T_GUEST_R10_INDEX 8
#define VMX_MACHINE_T_GUEST_R11_INDEX 9
#define VMX_MACHINE_T_GUEST_R12_INDEX 10
#define VMX_MACHINE_T_GUEST_R13_INDEX 11
#define VMX_MACHINE_T_GUEST_R14_INDEX 12
#define VMX_MACHINE_T_GUEST_R15_INDEX 13
#define VMX_MACHINE_T_GUEST_RDI_INDEX 14
/* Offsets of GPRs for guest within the VCPU data structure */
#define VMX_MACHINE_T_GUEST_RAX_OFFSET (VMX_MACHINE_T_GUEST_RAX_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RBX_OFFSET (VMX_MACHINE_T_GUEST_RBX_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RCX_OFFSET (VMX_MACHINE_T_GUEST_RCX_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RDX_OFFSET (VMX_MACHINE_T_GUEST_RDX_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RBP_OFFSET (VMX_MACHINE_T_GUEST_RBP_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RSI_OFFSET (VMX_MACHINE_T_GUEST_RSI_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_RDI_OFFSET (VMX_MACHINE_T_GUEST_RDI_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R8_OFFSET (VMX_MACHINE_T_GUEST_R8_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R9_OFFSET (VMX_MACHINE_T_GUEST_R9_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R10_OFFSET (VMX_MACHINE_T_GUEST_R10_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R11_OFFSET (VMX_MACHINE_T_GUEST_R11_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R12_OFFSET (VMX_MACHINE_T_GUEST_R12_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R13_OFFSET (VMX_MACHINE_T_GUEST_R13_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R14_OFFSET (VMX_MACHINE_T_GUEST_R14_INDEX*REG_SIZE)
#define VMX_MACHINE_T_GUEST_R15_OFFSET (VMX_MACHINE_T_GUEST_R15_INDEX*REG_SIZE)
/* Hard-coded offset of cr2 in struct run_context!! */
#define VMX_MACHINE_T_GUEST_CR2_OFFSET (128)
/* Hard-coded offset of cr2 in struct run_context!! */
#define VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET (192)
/*sizes of various registers within the VCPU data structure */
#define VMX_CPU_S_FXSAVE_GUEST_AREA_SIZE GUEST_STATE_AREA_SIZE
#ifndef ASSEMBLER
enum vcpu_state {
VCPU_INIT,
VCPU_RUNNING,
VCPU_PAUSED,
VCPU_ZOMBIE,
VCPU_UNKNOWN_STATE,
};
struct cpu_regs {
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rbp;
uint64_t rsi;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rdi;
};
struct segment {
uint64_t selector;
uint64_t base;
uint64_t limit;
uint64_t attr;
};
struct run_context {
/* Contains the guest register set.
* NOTE: This must be the first element in the structure, so that the offsets
* in vmx_asm.S match
*/
union {
struct cpu_regs regs;
uint64_t longs[NUM_GPRS];
} guest_cpu_regs;
/** The guests CR registers 0, 2, 3 and 4. */
uint64_t cr0;
/* VMX_MACHINE_T_GUEST_CR2_OFFSET =
* offsetof(struct run_context, cr2) = 128
*/
uint64_t cr2;
uint64_t cr3;
uint64_t cr4;
uint64_t rip;
uint64_t rsp;
uint64_t rflags;
uint64_t dr7;
uint64_t tsc_offset;
/* MSRs */
/* VMX_MACHINE_T_GUEST_SPEC_CTRL_OFFSET =
* offsetof(struct run_context, ia32_spec_ctrl) = 192
*/
uint64_t ia32_spec_ctrl;
uint64_t ia32_star;
uint64_t ia32_lstar;
uint64_t ia32_fmask;
uint64_t ia32_kernel_gs_base;
uint64_t ia32_pat;
uint64_t ia32_efer;
uint64_t ia32_sysenter_cs;
uint64_t ia32_sysenter_esp;
uint64_t ia32_sysenter_eip;
uint64_t ia32_debugctl;
/* segment registers */
struct segment cs;
struct segment ss;
struct segment ds;
struct segment es;
struct segment fs;
struct segment gs;
struct segment tr;
struct segment idtr;
struct segment ldtr;
struct segment gdtr;
/* The 512 bytes area to save the FPU/MMX/SSE states for the guest */
uint64_t
fxstore_guest_area[VMX_CPU_S_FXSAVE_GUEST_AREA_SIZE / sizeof(uint64_t)]
__aligned(16);
};
/* 2 worlds: 0 for Normal World, 1 for Secure World */
#define NR_WORLD 2
#define NORMAL_WORLD 0
#define SECURE_WORLD 1
struct vcpu_arch {
int cur_context;
struct run_context contexts[NR_WORLD];
/* A pointer to the VMCS for this CPU. */
void *vmcs;
/* Holds the information needed for IRQ/exception handling. */
struct {
/* The number of the exception to raise. */
int exception;
/* The error number for the exception. */
int error;
} exception_info;
uint8_t lapic_mask;
uint32_t irq_window_enabled;
uint32_t nrexits;
/* Auxiliary TSC value */
uint64_t msr_tsc_aux;
/* VCPU context state information */
uint64_t exit_reason;
uint64_t exit_interrupt_info;
uint64_t exit_qualification;
uint8_t inst_len;
/* Information related to secondary / AP VCPU start-up */
uint8_t cpu_mode;
uint8_t nr_sipi;
uint32_t sipi_vector;
/* interrupt injection information */
uint64_t pending_intr;
/* per vcpu lapic */
void *vlapic;
};
struct vm;
struct vcpu {
int pcpu_id; /* Physical CPU ID of this VCPU */
int vcpu_id; /* virtual identifier for VCPU */
struct vcpu_arch arch_vcpu;
/* Architecture specific definitions for this VCPU */
struct vm *vm; /* Reference to the VM this VCPU belongs to */
void *entry_addr; /* Entry address for this VCPU when first started */
/* State of this VCPU before suspend */
volatile enum vcpu_state prev_state;
volatile enum vcpu_state state; /* State of this VCPU */
/* State of debug request for this VCPU */
volatile enum vcpu_state dbg_req_state;
unsigned long sync; /*hold the bit events*/
struct vlapic *vlapic; /* per vCPU virtualized LAPIC */
struct list_head run_list; /* inserted to schedule runqueue */
unsigned long pending_pre_work; /* any pre work pending? */
bool launched; /* Whether the vcpu is launched on target pcpu */
unsigned int paused_cnt; /* how many times vcpu is paused */
unsigned int running; /* vcpu is picked up and run? */
unsigned int ioreq_pending; /* ioreq is ongoing or not? */
struct vhm_request req; /* used by io/ept emulation */
struct mem_io mmio; /* used by io/ept emulation */
/* save guest msr tsc aux register.
* Before VMENTRY, save guest MSR_TSC_AUX to this fields.
* After VMEXIT, restore this fields to guest MSR_TSC_AUX.
* This is only temperary workaround. Once MSR emulation
* is enabled, we should remove this fields and related
* code.
*/
uint64_t msr_tsc_aux_guest;
uint64_t *guest_msrs;
};
#define is_vcpu_bsp(vcpu) ((vcpu)->vcpu_id == 0)
/* do not update Guest RIP for next VM Enter */
#define VCPU_RETAIN_RIP(vcpu) ((vcpu)->arch_vcpu.inst_len = 0)
/* External Interfaces */
int create_vcpu(int cpu_id, struct vm *vm, struct vcpu **rtn_vcpu_handle);
int start_vcpu(struct vcpu *vcpu);
int shutdown_vcpu(struct vcpu *vcpu);
int destroy_vcpu(struct vcpu *vcpu);
void reset_vcpu(struct vcpu *vcpu);
void init_vcpu(struct vcpu *vcpu);
void pause_vcpu(struct vcpu *vcpu, enum vcpu_state new_state);
void resume_vcpu(struct vcpu *vcpu);
void schedule_vcpu(struct vcpu *vcpu);
int prepare_vcpu(struct vm *vm, int pcpu_id);
void request_vcpu_pre_work(struct vcpu *vcpu, int pre_work_id);
#endif
#endif

View File

@@ -0,0 +1,57 @@
/*-
* Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#ifndef _VIOAPIC_H_
#define _VIOAPIC_H_
#define VIOAPIC_BASE 0xFEC00000UL
#define VIOAPIC_SIZE 4096UL
struct vioapic *vioapic_init(struct vm *vm);
void vioapic_cleanup(struct vioapic *vioapic);
int vioapic_assert_irq(struct vm *vm, int irq);
int vioapic_deassert_irq(struct vm *vm, int irq);
int vioapic_pulse_irq(struct vm *vm, int irq);
void vioapic_update_tmr(struct vcpu *vcpu);
int vioapic_mmio_write(void *vm, uint64_t gpa,
uint64_t wval, int size);
int vioapic_mmio_read(void *vm, uint64_t gpa,
uint64_t *rval, int size);
int vioapic_pincount(struct vm *vm);
void vioapic_process_eoi(struct vm *vm, int vector);
bool vioapic_get_rte(struct vm *vm, int pin, void *rte);
int vioapic_mmio_access_handler(struct vcpu *vcpu, struct mem_io *mmio,
void *handler_private_data);
int get_vioapic_info(char *str, int str_max, int vmid);
#endif

View File

@@ -0,0 +1,132 @@
/*-
* Copyright (c) 2011 NetApp, Inc.
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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.
*
* $FreeBSD$
*/
#ifndef _VLAPIC_H_
#define _VLAPIC_H_
struct vlapic;
/* APIC write handlers */
void vlapic_set_cr8(struct vlapic *vlapic, uint64_t val);
uint64_t vlapic_get_cr8(struct vlapic *vlapic);
/*
* Returns 0 if there is no eligible vector that can be delivered to the
* guest at this time and non-zero otherwise.
*
* If an eligible vector number is found and 'vecptr' is not NULL then it will
* be stored in the location pointed to by 'vecptr'.
*
* Note that the vector does not automatically transition to the ISR as a
* result of calling this function.
*/
int vlapic_pending_intr(struct vlapic *vlapic, int *vecptr);
/*
* Transition 'vector' from IRR to ISR. This function is called with the
* vector returned by 'vlapic_pending_intr()' when the guest is able to
* accept this interrupt (i.e. RFLAGS.IF = 1 and no conditions exist that
* block interrupt delivery).
*/
void vlapic_intr_accepted(struct vlapic *vlapic, int vector);
struct vlapic *vm_lapic_from_vcpuid(struct vm *vm, int vcpu_id);
struct vlapic *vm_lapic_from_pcpuid(struct vm *vm, int pcpu_id);
bool vlapic_msr(uint32_t num);
int vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval, bool *retu);
int vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t wval, bool *retu);
int vlapic_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size);
int vlapic_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t wval, int size);
/*
* Signals to the LAPIC that an interrupt at 'vector' needs to be generated
* to the 'cpu', the state is recorded in IRR.
*/
int vlapic_set_intr(struct vcpu *vcpu, int vector, bool trig);
#define LAPIC_TRIG_LEVEL true
#define LAPIC_TRIG_EDGE false
static inline int
vlapic_intr_level(struct vcpu *vcpu, int vector)
{
return vlapic_set_intr(vcpu, vector, LAPIC_TRIG_LEVEL);
}
static inline int
vlapic_intr_edge(struct vcpu *vcpu, int vector)
{
return vlapic_set_intr(vcpu, vector, LAPIC_TRIG_EDGE);
}
/*
* Triggers the LAPIC local interrupt (LVT) 'vector' on 'cpu'. 'cpu' can
* be set to -1 to trigger the interrupt on all CPUs.
*/
int vlapic_set_local_intr(struct vm *vm, int cpu, int vector);
int vlapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg);
void vlapic_deliver_intr(struct vm *vm, bool level, uint32_t dest,
bool phys, int delmode, int vec);
/* Reset the trigger-mode bits for all vectors to be edge-triggered */
void vlapic_reset_tmr(struct vlapic *vlapic);
/*
* Set the trigger-mode bit associated with 'vector' to level-triggered if
* the (dest,phys,delmode) tuple resolves to an interrupt being delivered to
* this 'vlapic'.
*/
void vlapic_set_tmr_one_vec(struct vlapic *vlapic, int delmode,
int vector, bool level);
void
vlapic_apicv_batch_set_tmr(struct vlapic *vlapic);
int vlapic_mmio_access_handler(struct vcpu *vcpu, struct mem_io *mmio,
void *handler_private_data);
uint32_t vlapic_get_id(struct vlapic *vlapic);
uint8_t vlapic_get_apicid(struct vlapic *vlapic);
int vlapic_create(struct vcpu *vcpu);
void vlapic_free(struct vcpu *vcpu);
void vlapic_init(struct vlapic *vlapic);
bool vlapic_enabled(struct vlapic *vlapic);
uint64_t apicv_get_apic_access_addr(struct vm *vm);
uint64_t apicv_get_apic_page_addr(struct vlapic *vlapic);
bool vlapic_apicv_enabled(struct vcpu *vcpu);
void apicv_inject_pir(struct vlapic *vlapic);
int apicv_access_exit_handler(struct vcpu *vcpu);
int apicv_write_exit_handler(struct vcpu *vcpu);
int apicv_virtualized_eoi_exit_handler(struct vcpu *vcpu);
void calcvdest(struct vm *vm, uint64_t *dmask, uint32_t dest, bool phys);
#endif /* _VLAPIC_H_ */

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 VM_H_
#define VM_H_
enum vm_privilege_level {
VM_PRIVILEGE_LEVEL_HIGH = 0,
VM_PRIVILEGE_LEVEL_MEDIUM,
VM_PRIVILEGE_LEVEL_LOW
};
#define MAX_VM_NAME_LEN 16
struct vm_attr {
char name[16]; /* Virtual machine name string */
int id; /* Virtual machine identifier */
int boot_idx; /* Index indicating the boot sequence for this VM */
};
struct vm_hw_info {
int num_vcpus; /* Number of total virtual cores */
uint32_t created_vcpus; /* Number of created vcpus */
struct vcpu **vcpu_array; /* vcpu array of this VM */
uint64_t gpa_lowtop; /* top lowmem gpa of this VM */
};
struct sw_linux {
void *ramdisk_src_addr;
void *ramdisk_load_addr;
uint32_t ramdisk_size;
void *bootargs_src_addr;
void *bootargs_load_addr;
uint32_t bootargs_size;
void *dtb_src_addr;
void *dtb_load_addr;
uint32_t dtb_size;
};
struct sw_kernel_info {
void *kernel_src_addr;
void *kernel_load_addr;
void *kernel_entry_addr;
uint32_t kernel_size;
};
struct vm_sw_info {
int kernel_type; /* Guest kernel type */
/* Kernel information (common for all guest types) */
struct sw_kernel_info kernel_info;
/* Additional information specific to Linux guests */
struct sw_linux linux_info;
/* GPA Address of guest OS's request buffer */
uint64_t req_buf;
};
/* VM guest types */
#define VM_LINUX_GUEST 0x02
#define VM_MONO_GUEST 0x01
enum vpic_wire_mode {
VPIC_WIRE_INTR = 0,
VPIC_WIRE_LAPIC,
VPIC_WIRE_IOAPIC,
VPIC_WIRE_NULL
};
/* Enumerated type for VM states */
enum vm_state {
VM_CREATED = 0, /* VM created / awaiting start (boot) */
VM_STARTED, /* VM started (booted) */
VM_PAUSED, /* VM paused */
VM_STATE_UNKNOWN
};
/* Structure for VM state information */
struct vm_state_info {
enum vm_state state; /* State of the VM */
unsigned int privilege; /* Privilege level of the VM */
unsigned int boot_count;/* Number of times the VM has booted */
};
struct vm_arch {
void *guest_pml4; /* Guest pml4 */
void *ept; /* EPT hierarchy */
void *m2p; /* machine address to guest physical address */
void *tmp_pg_array; /* Page array for tmp guest paging struct */
void *iobitmap[2];/* IO bitmap page array base address for this VM */
void *msr_bitmap; /* MSR bitmap page base address for this VM */
void *virt_ioapic; /* Virtual IOAPIC base address */
/**
* A link to the IO handler of this VM.
* We only register io handle to this link
* when create VM on sequences and ungister it when
* destory VM. So there no need lock to prevent preempt.
* Besides, there only a few io handlers now, we don't
* need binary search temporary.
*/
struct vm_io_handler *io_handler;
/* reference to virtual platform to come here (as needed) */
};
struct vpic;
struct vm {
struct vm_attr attr; /* Reference to this VM's attributes */
struct vm_hw_info hw; /* Reference to this VM's HW information */
struct vm_sw_info sw; /* Reference to SW associated with this VM */
struct vm_arch arch_vm; /* Reference to this VM's arch information */
struct vm_state_info state_info;/* State info of this VM */
enum vm_state state; /* VM state */
struct vcpu *current_vcpu; /* VCPU that caused vm exit */
void *vuart; /* Virtual UART */
struct vpic *vpic; /* Virtual PIC */
uint32_t vpic_wire_mode;
struct iommu_domain *iommu_domain; /* iommu domain of this VM */
struct list_head list; /* list of VM */
spinlock_t spinlock; /* Spin-lock used to protect VM modifications */
struct list_head mmio_list; /* list for mmio. This list is not updated
* when vm is active. So no lock needed
*/
struct _vm_shared_memory *shared_memory_area;
struct {
struct _vm_virtual_device_node *head;
struct _vm_virtual_device_node *tail;
} virtual_device_list;
/* passthrough device link */
struct list_head ptdev_list;
spinlock_t ptdev_lock;
unsigned char GUID[16];
unsigned int secure_world_enabled;
};
struct vm_description {
/* Virtual machine identifier, assigned by the system */
char *vm_attr_name;
/* The logical CPU IDs associated with this VM - The first CPU listed
* will be the VM's BSP
*/
int *vm_hw_logical_core_ids;
unsigned char GUID[16]; /* GUID of the vm will be created */
int vm_hw_num_cores; /* Number of virtual cores */
/* Indicates to APs that the BSP has created a VM for this
* description
*/
bool vm_created;
/* Index indicating VM's privilege level */
unsigned int vm_state_info_privilege;
unsigned int secure_world_enabled; /* secure_world enabled? */
};
struct vm_description_array {
int num_vm_desc;
struct vm_description vm_desc_array[];
};
int shutdown_vm(struct vm *vm);
int pause_vm(struct vm *vm);
int start_vm(struct vm *vm);
int create_vm(struct vm_description *vm_desc, struct vm **vm);
int prepare_vm0(void);
struct vm *get_vm_from_vmid(int vm_id);
struct vm_description *get_vm_desc(int idx);
extern struct list_head vm_list;
extern spinlock_t vm_list_lock;
extern bool x2apic_enabled;
#endif /* VM_H_ */

View File

@@ -0,0 +1,110 @@
/*-
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2017 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*
* $FreeBSD$
*/
#ifndef _VPIC_H_
#define _VPIC_H_
#define ICU_IMR_OFFSET 1
/* Initialization control word 1. Written to even address. */
#define ICW1_IC4 0x01 /* ICW4 present */
#define ICW1_SNGL 0x02 /* 1 = single, 0 = cascaded */
#define ICW1_ADI 0x04 /* 1 = 4, 0 = 8 byte vectors */
#define ICW1_LTIM 0x08 /* 1 = level trigger, 0 = edge */
#define ICW1_RESET 0x10 /* must be 1 */
/* 0x20 - 0x80 - in 8080/8085 mode only */
/* Initialization control word 2. Written to the odd address. */
/* No definitions, it is the base vector of the IDT for 8086 mode */
/* Initialization control word 3. Written to the odd address. */
/* For a master PIC, bitfield indicating a slave 8259 on given input */
/* For slave, lower 3 bits are the slave's ID binary id on master */
/* Initialization control word 4. Written to the odd address. */
#define ICW4_8086 0x01 /* 1 = 8086, 0 = 8080 */
#define ICW4_AEOI 0x02 /* 1 = Auto EOI */
#define ICW4_MS 0x04 /* 1 = buffered master, 0 = slave */
#define ICW4_BUF 0x08 /* 1 = enable buffer mode */
#define ICW4_SFNM 0x10 /* 1 = special fully nested mode */
/* Operation control words. Written after initialization. */
/* Operation control word type 1 */
/*
* No definitions. Written to the odd address. Bitmask for interrupts.
* 1 = disabled.
*/
/* Operation control word type 2. Bit 3 (0x08) must be zero. Even address. */
#define OCW2_L0 0x01 /* Level */
#define OCW2_L1 0x02
#define OCW2_L2 0x04
/* 0x08 must be 0 to select OCW2 vs OCW3 */
/* 0x10 must be 0 to select OCW2 vs ICW1 */
#define OCW2_EOI 0x20 /* 1 = EOI */
#define OCW2_SL 0x40 /* EOI mode */
#define OCW2_R 0x80 /* EOI mode */
/* Operation control word type 3. Bit 3 (0x08) must be set. Even address. */
#define OCW3_RIS 0x01 /* 1 = read IS, 0 = read IR */
#define OCW3_RR 0x02 /* register read */
#define OCW3_P 0x04 /* poll mode command */
/* 0x08 must be 1 to select OCW3 vs OCW2 */
#define OCW3_SEL 0x08 /* must be 1 */
/* 0x10 must be 0 to select OCW3 vs ICW1 */
#define OCW3_SMM 0x20 /* special mode mask */
#define OCW3_ESMM 0x40 /* enable SMM */
#define IO_ELCR1 0x4d0
#define IO_ELCR2 0x4d1
enum vpic_trigger {
EDGE_TRIGGER,
LEVEL_TRIGGER
};
void *vpic_init(struct vm *vm);
void vpic_cleanup(struct vm *vm);
int vpic_assert_irq(struct vm *vm, int irq);
int vpic_deassert_irq(struct vm *vm, int irq);
int vpic_pulse_irq(struct vm *vm, int irq);
void vpic_pending_intr(struct vm *vm, int *vecptr);
void vpic_intr_accepted(struct vm *vm, int vector);
int vpic_set_irq_trigger(struct vm *vm, int irq, enum vpic_trigger trigger);
int vpic_get_irq_trigger(struct vm *vm, int irq, enum vpic_trigger *trigger);
struct vm_io_handler *vpic_create_io_handler(int flags, uint32_t port,
uint32_t len);
bool vpic_is_pin_mask(struct vpic *vpic, uint8_t virt_pin);
#endif /* _VPIC_H_ */

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 HV_ARCH_H
#define HV_ARCH_H
#include <cpu.h>
#include <gdt.h>
#include <idt.h>
#include <apicreg.h>
#include <ioapic.h>
#include <lapic.h>
#include <msr.h>
#include <io.h>
#include <vcpu.h>
#include <vm.h>
#include <cpuid.h>
#include <mmu.h>
#include <intr_ctx.h>
#include <irq.h>
#include <timer.h>
#include <softirq.h>
#include <vmx.h>
#include <assign.h>
#include <vtd.h>
#include <vpic.h>
#include <vlapic.h>
#include <vioapic.h>
#include <guest.h>
#include <vmexit.h>
#endif /* HV_ARCH_H */

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 IDT_H
#define IDT_H
/*
* IDT is defined in assembly so we handle exceptions as early as possible.
*/
/* Interrupt Descriptor Table (LDT) selectors are 16 bytes on x86-64 instead of
* 8 bytes.
*/
#define X64_IDT_DESC_SIZE (0x10)
/* Number of the HOST IDT entries */
#define HOST_IDT_ENTRIES (0x100)
/* Size of the IDT */
#define HOST_IDT_SIZE (HOST_IDT_ENTRIES * X64_IDT_DESC_SIZE)
#ifndef ASSEMBLER
/*
* Definition of an 16 byte IDT selector.
*/
union idt_64_descriptor {
uint64_t value;
struct {
union {
uint32_t value;
struct {
uint32_t offset_15_0:16;
uint32_t segment_sel:16;
} bits;
} low32;
union {
uint32_t value;
struct {
uint32_t ist:3;
uint32_t bit_3_clr:1;
uint32_t bit_4_clr:1;
uint32_t bits_5_7_clr:3;
uint32_t type:4;
uint32_t bit_12_clr:1;
uint32_t dpl:2;
uint32_t present:1;
uint32_t offset_31_16:16;
} bits;
} high32;
uint32_t offset_63_32;
uint32_t rsvd;
};
} __aligned(8);
/*****************************************************************************
*
* Definition of the IDT.
*
*****************************************************************************/
struct host_idt {
union idt_64_descriptor host_idt_descriptors[HOST_IDT_ENTRIES];
} __aligned(8);
/*
* Definition of the IDT descriptor.
*/
struct host_idt_descriptor {
unsigned short len;
struct host_idt *idt;
} __packed;
extern struct host_idt HOST_IDT;
extern struct host_idt_descriptor HOST_IDTR;
static inline void set_idt(struct host_idt_descriptor *idtd)
{
asm volatile (" lidtq %[idtd]\n" : /* no output parameters */
: /* input parameters */
[idtd] "m"(*idtd));
}
#endif /* end #ifndef ASSEMBLER */
#endif /* IDT_H */

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 INTR_CTX_H
#define INTR_CTX_H
/*
* Definition of the stack frame layout
*/
struct intr_ctx {
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rbx;
uint64_t rbp;
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t vector;
uint64_t error_code;
uint64_t rip;
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
};
#endif /* INTR_CTX_H */

View File

@@ -0,0 +1,622 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 IO_H
#define IO_H
/* Definition of a IO port range */
struct vm_io_range {
uint16_t base; /* IO port base */
uint16_t len; /* IO port range */
int flags; /* IO port attributes */
};
/* Write 1 byte to specified I/O port */
static inline void io_write_byte(uint8_t value, uint16_t port)
{
asm volatile ("outb %0,%1"::"a" (value), "dN"(port));
}
/* Read 1 byte from specified I/O port */
static inline uint8_t io_read_byte(uint16_t port)
{
uint8_t value;
asm volatile ("inb %1,%0":"=a" (value):"dN"(port));
return value;
}
/* Write 2 bytes to specified I/O port */
static inline void io_write_word(uint16_t value, uint16_t port)
{
asm volatile ("outw %0,%1"::"a" (value), "dN"(port));
}
/* Read 2 bytes from specified I/O port */
static inline uint16_t io_read_word(uint16_t port)
{
uint16_t value;
asm volatile ("inw %1,%0":"=a" (value):"dN"(port));
return value;
}
/* Write 4 bytes to specified I/O port */
static inline void io_write_long(uint32_t value, uint16_t port)
{
asm volatile ("outl %0,%1"::"a" (value), "dN"(port));
}
/* Read 4 bytes from specified I/O port */
static inline uint32_t io_read_long(uint16_t port)
{
uint32_t value;
asm volatile ("inl %1,%0":"=a" (value):"dN"(port));
return value;
}
static inline void io_write(uint32_t v, ioport_t addr, size_t sz)
{
if (sz == 1)
io_write_byte(v, addr);
else if (sz == 2)
io_write_word(v, addr);
else
io_write_long(v, addr);
}
static inline uint32_t io_read(ioport_t addr, size_t sz)
{
if (sz == 1)
return io_read_byte(addr);
if (sz == 2)
return io_read_word(addr);
return io_read_long(addr);
}
struct vm_io_handler;
struct vm;
struct vcpu;
typedef
uint32_t (*io_read_fn_t)(struct vm_io_handler *, struct vm *,
ioport_t, size_t);
typedef
void (*io_write_fn_t)(struct vm_io_handler *, struct vm *,
ioport_t, size_t, uint32_t);
/* Describes a single IO handler description entry. */
struct vm_io_handler_desc {
/** The base address of the IO range for this description. */
ioport_t addr;
/** The number of bytes covered by this description. */
size_t len;
/** A pointer to the "read" function.
*
* The read function is called from the hypervisor whenever
* a read access to a range described in "ranges" occur.
* The arguments to the callback are:
*
* - The address of the port to read from.
* - The width of the read operation (1,2 or 4).
*
* The implementation must return the ports content as
* byte, word or doubleword (depending on the width).
*
* If the pointer is null, a read of 1's is assumed.
*/
io_read_fn_t io_read;
/** A pointer to the "write" function.
*
* The write function is called from the hypervisor code
* whenever a write access to a range described in "ranges"
* occur. The arguments to the callback are:
*
* - The address of the port to write to.
* - The width of the write operation (1,2 or 4).
* - The value to write as byte, word or doubleword
* (depending on the width)
*
* The implementation must write the value to the port.
*
* If the pointer is null, the write access is ignored.
*/
io_write_fn_t io_write;
};
struct vm_io_handler {
struct vm_io_handler *next;
struct vm_io_handler_desc desc;
};
#define IO_ATTR_R 0
#define IO_ATTR_RW 1
#define IO_ATTR_NO_ACCESS 2
/* External Interfaces */
int io_instr_handler(struct vcpu *vcpu);
void setup_io_bitmap(struct vm *vm);
void free_io_emulation_resource(struct vm *vm);
void register_io_emulation_handler(struct vm *vm, struct vm_io_range *range,
io_read_fn_t io_read_fn_ptr,
io_write_fn_t io_write_fn_ptr);
int dm_emulate_pio_post(struct vcpu *vcpu);
/** Writes a 32 bit value to a memory mapped IO device.
*
* @param value The 32 bit value to write.
* @param addr The memory address to write to.
*/
static inline void mmio_write_long(uint32_t value, mmio_addr_t addr)
{
*((uint32_t *)addr) = value;
}
/** Writes a 16 bit value to a memory mapped IO device.
*
* @param value The 16 bit value to write.
* @param addr The memory address to write to.
*/
static inline void mmio_write_word(uint32_t value, mmio_addr_t addr)
{
*((uint16_t *)addr) = value;
}
/** Writes an 8 bit value to a memory mapped IO device.
*
* @param value The 8 bit value to write.
* @param addr The memory address to write to.
*/
static inline void mmio_write_byte(uint32_t value, mmio_addr_t addr)
{
*((uint8_t *)addr) = value;
}
/** Reads a 32 bit value from a memory mapped IO device.
*
* @param addr The memory address to read from.
*
* @return The 32 bit value read from the given address.
*/
static inline uint32_t mmio_read_long(mmio_addr_t addr)
{
return *((uint32_t *)addr);
}
/** Reads a 16 bit value from a memory mapped IO device.
*
* @param addr The memory address to read from.
*
* @return The 16 bit value read from the given address.
*/
static inline uint16_t mmio_read_word(mmio_addr_t addr)
{
return *((uint16_t *)addr);
}
/** Reads an 8 bit value from a memory mapped IO device.
*
* @param addr The memory address to read from.
*
* @return The 8 bit value read from the given address.
*/
static inline uint8_t mmio_read_byte(mmio_addr_t addr)
{
return *((uint8_t *)addr);
}
/** Sets bits in a 32 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_or_long(uint32_t mask, mmio_addr_t addr)
{
*((uint32_t *)addr) |= mask;
}
/** Sets bits in a 16 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_or_word(uint32_t mask, mmio_addr_t addr)
{
*((uint16_t *)addr) |= mask;
}
/** Sets bits in an 8 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_or_byte(uint32_t mask, mmio_addr_t addr)
{
*((uint8_t *)addr) |= mask;
}
/** Clears bits in a 32 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_and_long(uint32_t mask, mmio_addr_t addr)
{
*((uint32_t *)addr) &= ~mask;
}
/** Clears bits in a 16 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_and_word(uint32_t mask, mmio_addr_t addr)
{
*((uint16_t *)addr) &= ~mask;
}
/** Clears bits in an 8 bit value from a memory mapped IO device.
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_and_byte(uint32_t mask, mmio_addr_t addr)
{
*((uint8_t *)addr) &= ~mask;
}
/** Performs a read-modify-write cycle for a 32 bit value from a MMIO device.
*
* Reads a 32 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_rmw_long(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint32_t *)addr) =
(*((uint32_t *)addr) & ~clear) | set;
}
/** Performs a read-modify-write cycle for a 16 bit value from a MMIO device.
*
* Reads a 16 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_rmw_word(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint16_t *)addr) =
(*((uint16_t *)addr) & ~clear) | set;
}
/** Performs a read-modify-write cycle for an 8 bit value from a MMIO device.
*
* Reads an 8 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void mmio_rmw_byte(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint8_t *)addr) = (*((uint8_t *)addr) & ~clear) | set;
}
/** Writes a 32 bit value to a memory mapped IO device (ROM code version).
*
* @param value The 32 bit value to write.
* @param addr The memory address to write to.
*/
static inline void __mmio_write_long(uint32_t value, mmio_addr_t addr)
{
*((uint32_t *)addr) = value;
}
/** Writes a 16 bit value to a memory mapped IO device (ROM code version).
*
* @param value The 16 bit value to write.
* @param addr The memory address to write to.
*/
static inline void __mmio_write_word(uint32_t value, mmio_addr_t addr)
{
*((uint16_t *)addr) = value;
}
/** Writes an 8 bit value to a memory mapped IO device (ROM code version).
*
* @param value The 8 bit value to write.
* @param addr The memory address to write to.
*/
static inline void __mmio_write_byte(uint32_t value, mmio_addr_t addr)
{
*((uint8_t *)addr) = value;
}
/** Reads a 32 bit value from a memory mapped IO device (ROM code version).
*
* @param addr The memory address to read from.
*
* @return The 32 bit value read from the given address.
*/
static inline uint32_t __mmio_read_long(mmio_addr_t addr)
{
return *((uint32_t *)addr);
}
/** Reads a 16 bit value from a memory mapped IO device (ROM code version).
*
* @param addr The memory address to read from.
*
* @return The 16 bit value read from the given address.
*/
static inline uint16_t __mmio_read_word(mmio_addr_t addr)
{
return *((uint16_t *)addr);
}
/** Reads an 8 bit value from a memory mapped IO device (ROM code version).
*
* @param addr The memory address to read from.
*
* @return The 32 16 value read from the given address.
*/
static inline uint8_t __mmio_read_byte(mmio_addr_t addr)
{
return *((uint8_t *)addr);
}
/** Sets bits in a 32 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_or_long(uint32_t mask, mmio_addr_t addr)
{
*((uint32_t *)addr) |= mask;
}
/** Sets bits in a 16 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_or_word(uint32_t mask, mmio_addr_t addr)
{
*((uint16_t *)addr) |= mask;
}
/** Sets bits in an 8 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to set at the memory address.
* Bits set in this mask are set in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_or_byte(uint32_t mask, mmio_addr_t addr)
{
*((uint8_t *)addr) |= mask;
}
/** Clears bits in a 32 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_and_long(uint32_t mask, mmio_addr_t addr)
{
*((uint32_t *)addr) &= ~mask;
}
/** Clears bits in a 16 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_and_word(uint32_t mask, mmio_addr_t addr)
{
*((uint16_t *)addr) &= ~mask;
}
/** Clears bits in an 8 bit value from a MMIO device (ROM code version).
*
* @param mask Contains the bits to clear at the memory address.
* Bits set in this mask are cleared in the memory
* location.
* @param addr The memory address to read from/write to.
*/
static inline void __mmio_and_byte(uint32_t mask, mmio_addr_t addr)
{
*((uint8_t *)addr) &= ~mask;
}
/** Performs a read-modify-write cycle for a 32 bit value from a MMIO device
* (ROM code version).
*
* Reads a 32 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void
__mmio_rmw_long(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint32_t *)addr) =
(*((uint32_t *)addr) & ~clear) | set;
}
/** Performs a read-modify-write cycle for a 16 bit value from a MMIO device
* (ROM code version).
*
* Reads a 16 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void
__mmio_rmw_word(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint16_t *)addr) =
(*((uint16_t *)addr) & ~clear) | set;
}
/** Performs a read-modify-write cycle for an 8 bit value from a MMIO device
* (ROM code version).
*
* Reads an 8 bit value from a memory mapped IO device, sets and clears
* bits and writes the value back. If a bit is specified in both, the 'set'
* and in the 'clear' mask, it is undefined whether the resulting bit is set
* or cleared.
*
* @param set Contains the bits to set. Bits set in this mask
* are set at the memory address.
* @param clear Contains the bits to clear. Bits set in this
* mask are cleared at the memory address.
* @param addr The memory address to read from/write to.
*/
static inline void
__mmio_rmw_byte(uint32_t set, uint32_t clear, mmio_addr_t addr)
{
*((uint8_t *)addr) = (*((uint8_t *)addr) & ~clear) | set;
}
/** Reads a 32 Bit memory mapped IO register, mask it and write it back into
* memory mapped IO register.
*
* @param addr The address of the memory mapped IO register.
* @param mask The mask to apply to the value read.
* @param value The 32 bit value to write.
*/
static inline void setl(mmio_addr_t addr, uint32_t mask, uint32_t value)
{
mmio_write_long((mmio_read_long(addr) & ~mask) | value, addr);
}
/** Reads a 16 Bit memory mapped IO register, mask it and write it back into
* memory mapped IO register.
*
* @param addr The address of the memory mapped IO register.
* @param mask The mask to apply to the value read.
* @param value The 16 bit value to write.
*/
static inline void setw(mmio_addr_t addr, uint32_t mask, uint32_t value)
{
mmio_write_word((mmio_read_word(addr) & ~mask) | value, addr);
}
/** Reads a 8 Bit memory mapped IO register, mask it and write it back into
* memory mapped IO register.
*
* @param addr The address of the memory mapped IO register.
* @param mask The mask to apply to the value read.
* @param value The 8 bit value to write.
*/
static inline void setb(mmio_addr_t addr, uint32_t mask, uint32_t value)
{
mmio_write_byte((mmio_read_byte(addr) & ~mask) | value, addr);
}
/* MMIO memory access types */
enum mem_io_type {
HV_MEM_IO_READ = 0,
HV_MEM_IO_WRITE,
};
/* MMIO emulation related structures */
#define MMIO_TRANS_VALID 1
#define MMIO_TRANS_INVALID 0
struct mem_io {
uint64_t paddr; /* Physical address being accessed */
enum mem_io_type read_write; /* 0 = read / 1 = write operation */
uint8_t access_size; /* Access size being emulated */
uint8_t sign_extend_read; /* 1 if sign extension required for read */
uint64_t value; /* Value read or value to write */
uint8_t mmio_status; /* Indicates if this MMIO transaction is valid */
/* Used to store emulation context for this mmio transaction */
void *private_data;
};
#endif /* _IO_H defined */

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 IOAPIC_H
#define IOAPIC_H
/* IOAPIC_MAX_LINES is architecturally defined.
* The usable RTEs may be a subset of the total on a per IO APIC basis.
*/
#define IOAPIC_MAX_LINES 120
#define NR_LEGACY_IRQ 16
#define NR_MAX_GSI (NR_IOAPICS*IOAPIC_MAX_LINES)
#define GSI_MASK_IRQ(irq) irq_gsi_mask_unmask((irq), true)
#define GSI_UNMASK_IRQ(irq) irq_gsi_mask_unmask((irq), false)
#define GSI_SET_RTE(irq, rte) ioapic_set_rte((irq), (rte))
void setup_ioapic_irq(void);
int get_ioapic_info(char *str, int str_max_len);
bool irq_is_gsi(int irq);
int irq_gsi_num(void);
int irq_to_pin(int irq);
int pin_to_irq(int pin);
void irq_gsi_mask_unmask(int irq, bool mask);
void ioapic_set_rte(int irq, uint64_t rte);
void ioapic_get_rte(int irq, uint64_t *rte);
extern uint16_t legacy_irq_to_pin[];
#endif /* IOAPIC_H */

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 IRQ_H
#define IRQ_H
/* vectors for normal, usually for devices */
#define VECTOR_FOR_NOR_LOWPRI_START 0x20
#define VECTOR_FOR_NOR_LOWPRI_END 0x7F
#define VECTOR_FOR_NOR_HIGHPRI_START 0x80
#define VECTOR_FOR_NOR_HIGHPRI_END 0xDF
#define VECTOR_FOR_NOR_END VECTOR_FOR_NOR_HIGHPRI_END
#define VECTOR_FOR_INTR_START VECTOR_FOR_NOR_LOWPRI_START
/* vectors for priority, usually for HV service */
#define VECTOR_FOR_PRI_START 0xE0
#define VECTOR_FOR_PRI_END 0xFF
#define VECTOR_TIMER 0xEF
#define VECTOR_NOTIFY_VCPU 0xF0
#define VECTOR_VIRT_IRQ_VHM 0xF7
#define VECTOR_SPURIOUS 0xFF
#define NR_MAX_VECTOR 0xFF
#define VECTOR_INVALID (NR_MAX_VECTOR + 1)
#define IRQ_INVALID (NR_MAX_IRQS+1)
#define NR_MAX_IRQS (256+16)
#define DEFAULT_DEST_MODE IOAPIC_RTE_DESTLOG
#define DEFAULT_DELIVERY_MODE IOAPIC_RTE_DELLOPRI
#define ALL_CPUS_MASK ((1 << phy_cpu_num) - 1)
struct irq_desc;
enum irq_mode {
IRQ_PULSE,
IRQ_ASSERT,
IRQ_DEASSERT,
};
enum irq_state {
IRQ_NOT_ASSIGNED = 0,
IRQ_ASSIGNED_SHARED,
IRQ_ASSIGNED_NOSHARE,
};
enum irq_desc_state {
IRQ_DESC_PENDING,
IRQ_DESC_IN_PROCESS,
};
typedef int (*dev_handler_t)(int irq, void*);
struct dev_handler_node {
char name[32];
void *dev_data;
dev_handler_t dev_handler;
struct dev_handler_node *next;
struct irq_desc *desc;
};
struct irq_routing_entry {
unsigned short bdf; /* BDF */
int irq; /* PCI cfg offset 0x3C: IRQ pin */
int intx; /* PCI cfg offset 0x3D: 0-3 = INTA,INTB,INTC,INTD*/
};
int irq_mark_used(int irq);
int irq_alloc(void);
int irq_desc_alloc_vector(int irq, bool lowpri);
void irq_desc_try_free_vector(int irq);
int irq_to_vector(int irq);
int dev_to_irq(struct dev_handler_node *node);
int dev_to_vector(struct dev_handler_node *node);
int handle_level_interrupt_common(struct irq_desc *desc, void *handler_data);
int common_handler_edge(struct irq_desc *desc, void *handler_data);
int common_dev_handler_level(struct irq_desc *desc, void *handler_data);
int quick_handler_nolock(struct irq_desc *desc, void *handler_data);
typedef int (*irq_handler_t)(struct irq_desc*, void*);
void update_irq_handler(int irq, irq_handler_t func);
int init_default_irqs(unsigned int cpu);
int dispatch_interrupt(struct intr_ctx *ctx);
struct dev_handler_node*
pri_register_handler(int irq,
int vector,
dev_handler_t func,
void *dev_data,
const char *name);
struct dev_handler_node*
normal_register_handler(int irq,
dev_handler_t func,
void *dev_data,
bool share,
bool lowpri,
const char *name);
void unregister_handler_common(struct dev_handler_node *node);
int get_cpu_interrupt_info(char *str, int str_max);
void setup_notification(void);
typedef int (*spurious_handler_t)(int);
extern spurious_handler_t spurious_handler;
/*
* Some MSI message definitions
*/
#define MSI_ADDR_MASK 0xfff00000
#define MSI_ADDR_BASE 0xfee00000
#define MSI_ADDR_RH 0x00000008 /* Redirection Hint */
#define MSI_ADDR_LOG 0x00000004 /* Destination Mode */
/* RFLAGS */
#define HV_ARCH_VCPU_RFLAGS_IF (1<<9)
/* Interruptability State info */
#define HV_ARCH_VCPU_BLOCKED_BY_MOVSS (1<<1)
#define HV_ARCH_VCPU_BLOCKED_BY_STI (1<<0)
int vcpu_inject_extint(struct vcpu *vcpu);
int vcpu_inject_nmi(struct vcpu *vcpu);
int vcpu_inject_gp(struct vcpu *vcpu);
int vcpu_make_request(struct vcpu *vcpu, int eventid);
int exception_handler(struct vcpu *vcpu);
int interrupt_win_exiting_handler(struct vcpu *vcpu);
int external_interrupt_handler(struct vcpu *vcpu);
int acrn_do_intr_process(struct vcpu *vcpu);
int interrupt_init(uint32_t logical_id);
#endif /* IRQ_H */

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 INTR_LAPIC_H
#define INTR_LAPIC_H
#define DEBUG_LAPIC 0
enum intr_lapic_icr_delivery_mode {
INTR_LAPIC_ICR_FIXED = 0x0,
INTR_LAPIC_ICR_LP = 0x1,
INTR_LAPIC_ICR_SMI = 0x2,
INTR_LAPIC_ICR_NMI = 0x4,
INTR_LAPIC_ICR_INIT = 0x5,
INTR_LAPIC_ICR_STARTUP = 0x6,
};
enum intr_lapic_icr_dest_mode {
INTR_LAPIC_ICR_PHYSICAL = 0x0,
INTR_LAPIC_ICR_LOGICAL = 0x1
};
enum intr_lapic_icr_level {
INTR_LAPIC_ICR_DEASSERT = 0x0,
INTR_LAPIC_ICR_ASSERT = 0x1,
};
enum intr_lapic_icr_trigger {
INTR_LAPIC_ICR_EDGE = 0x0,
INTR_LAPIC_ICR_LEVEL = 0x1,
};
enum intr_lapic_icr_shorthand {
INTR_LAPIC_ICR_USE_DEST_ARRAY = 0x0,
INTR_LAPIC_ICR_SELF = 0x1,
INTR_LAPIC_ICR_ALL_INC_SELF = 0x2,
INTR_LAPIC_ICR_ALL_EX_SELF = 0x3,
};
/* Default LAPIC base */
#define LAPIC_BASE 0xFEE00000
/* LAPIC register offset for memory mapped IO access */
#define LAPIC_ID_REGISTER 0x00000020
#define LAPIC_VERSION_REGISTER 0x00000030
#define LAPIC_TASK_PRIORITY_REGISTER 0x00000080
#define LAPIC_ARBITRATION_PRIORITY_REGISTER 0x00000090
#define LAPIC_PROCESSOR_PRIORITY_REGISTER 0x000000A0
#define LAPIC_EOI_REGISTER 0x000000B0
#define LAPIC_REMOTE_READ_REGISTER 0x000000C0
#define LAPIC_LOGICAL_DESTINATION_REGISTER 0x000000D0
#define LAPIC_DESTINATION_FORMAT_REGISTER 0x000000E0
#define LAPIC_SPURIOUS_VECTOR_REGISTER 0x000000F0
#define LAPIC_IN_SERVICE_REGISTER_0 0x00000100
#define LAPIC_IN_SERVICE_REGISTER_1 0x00000110
#define LAPIC_IN_SERVICE_REGISTER_2 0x00000120
#define LAPIC_IN_SERVICE_REGISTER_3 0x00000130
#define LAPIC_IN_SERVICE_REGISTER_4 0x00000140
#define LAPIC_IN_SERVICE_REGISTER_5 0x00000150
#define LAPIC_IN_SERVICE_REGISTER_6 0x00000160
#define LAPIC_IN_SERVICE_REGISTER_7 0x00000170
#define LAPIC_TRIGGER_MODE_REGISTER_0 0x00000180
#define LAPIC_TRIGGER_MODE_REGISTER_1 0x00000190
#define LAPIC_TRIGGER_MODE_REGISTER_2 0x000001A0
#define LAPIC_TRIGGER_MODE_REGISTER_3 0x000001B0
#define LAPIC_TRIGGER_MODE_REGISTER_4 0x000001C0
#define LAPIC_TRIGGER_MODE_REGISTER_5 0x000001D0
#define LAPIC_TRIGGER_MODE_REGISTER_6 0x000001E0
#define LAPIC_TRIGGER_MODE_REGISTER_7 0x000001F0
#define LAPIC_INT_REQUEST_REGISTER_0 0x00000200
#define LAPIC_INT_REQUEST_REGISTER_1 0x00000210
#define LAPIC_INT_REQUEST_REGISTER_2 0x00000220
#define LAPIC_INT_REQUEST_REGISTER_3 0x00000230
#define LAPIC_INT_REQUEST_REGISTER_4 0x00000240
#define LAPIC_INT_REQUEST_REGISTER_5 0x00000250
#define LAPIC_INT_REQUEST_REGISTER_6 0x00000260
#define LAPIC_INT_REQUEST_REGISTER_7 0x00000270
#define LAPIC_ERROR_STATUS_REGISTER 0x00000280
#define LAPIC_LVT_CMCI_REGISTER 0x000002F0
#define LAPIC_INT_COMMAND_REGISTER_0 0x00000300
#define LAPIC_INT_COMMAND_REGISTER_1 0x00000310
#define LAPIC_LVT_TIMER_REGISTER 0x00000320
#define LAPIC_LVT_THERMAL_SENSOR_REGISTER 0x00000330
#define LAPIC_LVT_PMC_REGISTER 0x00000340
#define LAPIC_LVT_LINT0_REGISTER 0x00000350
#define LAPIC_LVT_LINT1_REGISTER 0x00000360
#define LAPIC_LVT_ERROR_REGISTER 0x00000370
#define LAPIC_INITIAL_COUNT_REGISTER 0x00000380
#define LAPIC_CURRENT_COUNT_REGISTER 0x00000390
#define LAPIC_DIVIDE_CONFIGURATION_REGISTER 0x000003E0
/* LAPIC CPUID bit and bitmask definitions */
#define CPUID_OUT_RDX_APIC_PRESENT ((uint64_t) 1 << 9)
#define CPUID_OUT_RCX_X2APIC_PRESENT ((uint64_t) 1 << 21)
/* LAPIC MSR bit and bitmask definitions */
#define MSR_01B_XAPIC_GLOBAL_ENABLE ((uint64_t) 1 << 11)
/* LAPIC register bit and bitmask definitions */
#define LAPIC_SVR_VECTOR 0x000000FF
#define LAPIC_SVR_APIC_ENABLE_MASK 0x00000100
#define LAPIC_LVT_MASK 0x00010000
#define LAPIC_DELIVERY_MODE_EXTINT_MASK 0x00000700
/* LAPIC Timer bit and bitmask definitions */
#define LAPIC_TMR_ONESHOT ((uint32_t) 0x0 << 17)
#define LAPIC_TMR_PERIODIC ((uint32_t) 0x1 << 17)
#define LAPIC_TMR_TSC_DEADLINE ((uint32_t) 0x2 << 17)
enum intr_cpu_startup_shorthand {
INTR_CPU_STARTUP_USE_DEST,
INTR_CPU_STARTUP_ALL_EX_SELF,
INTR_CPU_STARTUP_UNKNOWN,
};
union lapic_id {
uint32_t value;
struct {
uint8_t xapic_id;
uint8_t rsvd[3];
} xapic;
union {
uint32_t value;
struct {
uint8_t xapic_id;
uint8_t xapic_edid;
uint8_t rsvd[2];
} ioxapic_view;
struct {
uint32_t x2apic_id:4;
uint32_t x2apic_cluster:28;
} ldr_view;
} x2apic;
};
int early_init_lapic(void);
int init_lapic(uint32_t cpu_id);
int send_lapic_eoi(void);
uint32_t get_cur_lapic_id(void);
int send_startup_ipi(enum intr_cpu_startup_shorthand cpu_startup_shorthand,
uint32_t cpu_startup_dest,
paddr_t cpu_startup_start_address);
/* API to send an IPI to a single guest */
void send_single_ipi(uint32_t pcpu_id, uint32_t vector);
#endif /* INTR_LAPIC_H */

View File

@@ -0,0 +1,394 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 MMU_H
#define MMU_H
/* Size of all page-table entries (in bytes) */
#define IA32E_COMM_ENTRY_SIZE 8
/* Definitions common for all IA-32e related paging entries */
#define IA32E_COMM_P_BIT 0x0000000000000001
#define IA32E_COMM_RW_BIT 0x0000000000000002
#define IA32E_COMM_US_BIT 0x0000000000000004
#define IA32E_COMM_PWT_BIT 0x0000000000000008
#define IA32E_COMM_PCD_BIT 0x0000000000000010
#define IA32E_COMM_A_BIT 0x0000000000000020
#define IA32E_COMM_XD_BIT 0x8000000000000000
/* Defines for EPT paging entries */
#define IA32E_EPT_R_BIT 0x0000000000000001
#define IA32E_EPT_W_BIT 0x0000000000000002
#define IA32E_EPT_X_BIT 0x0000000000000004
#define IA32E_EPT_UNCACHED (0<<3)
#define IA32E_EPT_WC (1<<3)
#define IA32E_EPT_WT (4<<3)
#define IA32E_EPT_WP (5<<3)
#define IA32E_EPT_WB (6<<3)
#define IA32E_EPT_PAT_IGNORE 0x0000000000000040
#define IA32E_EPT_ACCESS_FLAG 0x0000000000000100
#define IA32E_EPT_DIRTY_FLAG 0x0000000000000200
#define IA32E_EPT_SNOOP_CTRL 0x0000000000000800
#define IA32E_EPT_SUPPRESS_VE 0x8000000000000000
/* Definitions common or ignored for all IA-32e related paging entries */
#define IA32E_COMM_D_BIT 0x0000000000000040
#define IA32E_COMM_G_BIT 0x0000000000000100
/* Definitions exclusive to a Page Map Level 4 Entry (PML4E) */
#define IA32E_PML4E_INDEX_MASK_START 39
#define IA32E_PML4E_ADDR_MASK 0x0000FF8000000000
/* Definitions exclusive to a Page Directory Pointer Table Entry (PDPTE) */
#define IA32E_PDPTE_D_BIT 0x0000000000000040
#define IA32E_PDPTE_PS_BIT 0x0000000000000080
#define IA32E_PDPTE_PAT_BIT 0x0000000000001000
#define IA32E_PDPTE_ADDR_MASK 0x0000FFFFC0000000
#define IA32E_PDPTE_INDEX_MASK_START \
(IA32E_PML4E_INDEX_MASK_START - IA32E_INDEX_MASK_BITS)
/* Definitions exclusive to a Page Directory Entry (PDE) 1G or 2M */
#define IA32E_PDE_D_BIT 0x0000000000000040
#define IA32E_PDE_PS_BIT 0x0000000000000080
#define IA32E_PDE_PAT_BIT 0x0000000000001000
#define IA32E_PDE_ADDR_MASK 0x0000FFFFFFE00000
#define IA32E_PDE_INDEX_MASK_START \
(IA32E_PDPTE_INDEX_MASK_START - IA32E_INDEX_MASK_BITS)
/* Definitions exclusive to Page Table Entries (PTE) */
#define IA32E_PTE_D_BIT 0x0000000000000040
#define IA32E_PTE_PAT_BIT 0x0000000000000080
#define IA32E_PTE_G_BIT 0x0000000000000100
#define IA32E_PTE_ADDR_MASK 0x0000FFFFFFFFF000
#define IA32E_PTE_INDEX_MASK_START \
(IA32E_PDE_INDEX_MASK_START - IA32E_INDEX_MASK_BITS)
/** The 'Present' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_P 0x00000001
/** The 'Read/Write' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_RW 0x00000002
/** The 'User/Supervisor' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_US 0x00000004
/** The 'Page Write Through' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_PWT 0x00000008
/** The 'Page Cache Disable' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_PCD 0x00000010
/** The 'Accessed' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_A 0x00000020
/** The 'Dirty' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_D 0x00000040
/** The 'Page Size' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_PS 0x00000080
/** The 'Global' bit in a 32 bit paging page directory entry */
#define MMU_32BIT_PDE_G 0x00000100
/** The 'PAT' bit in a page 32 bit paging directory entry */
#define MMU_32BIT_PDE_PAT 0x00001000
/** The flag that indicates that the page fault was caused by a non present
* page.
*/
#define PAGE_FAULT_P_FLAG 0x00000001
/** The flag that indicates that the page fault was caused by a write access. */
#define PAGE_FAULT_WR_FLAG 0x00000002
/** The flag that indicates that the page fault was caused in user mode. */
#define PAGE_FAULT_US_FLAG 0x00000004
/** The flag that indicates that the page fault was caused by a reserved bit
* violation.
*/
#define PAGE_FAULT_RSVD_FLAG 0x00000008
/** The flag that indicates that the page fault was caused by an instruction
* fetch.
*/
#define PAGE_FAULT_ID_FLAG 0x00000010
/* Defines used for common memory sizes */
#define MEM_1K 1024UL
#define MEM_2K (MEM_1K * 2UL)
#define MEM_4K (MEM_1K * 4UL)
#define MEM_8K (MEM_1K * 8UL)
#define MEM_16K (MEM_1K * 16UL)
#define MEM_32K (MEM_1K * 32UL)
#define MEM_64K (MEM_1K * 64UL)
#define MEM_128K (MEM_1K * 128UL)
#define MEM_256K (MEM_1K * 256UL)
#define MEM_512K (MEM_1K * 512UL)
#define MEM_1M (MEM_1K * 1024UL)
#define MEM_2M (MEM_1M * 2UL)
#define MEM_4M (MEM_1M * 4UL)
#define MEM_8M (MEM_1M * 8UL)
#define MEM_16M (MEM_1M * 16UL)
#define MEM_32M (MEM_1M * 32UL)
#define MEM_64M (MEM_1M * 64UL)
#define MEM_128M (MEM_1M * 128UL)
#define MEM_256M (MEM_1M * 256UL)
#define MEM_512M (MEM_1M * 512UL)
#define MEM_1G (MEM_1M * 1024UL)
#define MEM_2G (MEM_1G * 2UL)
#define MEM_3G (MEM_1G * 3UL)
#define MEM_4G (MEM_1G * 4UL)
#define MEM_5G (MEM_1G * 5UL)
#define MEM_6G (MEM_1G * 6UL)
#ifndef ASSEMBLER
/* Define cache line size (in bytes) */
#define CACHE_LINE_SIZE 64
/* Size of all page structures for IA-32e */
#define IA32E_STRUCT_SIZE MEM_4K
/* IA32E Paging constants */
#define IA32E_INDEX_MASK_BITS 9
#define IA32E_NUM_ENTRIES 512
#define IA32E_INDEX_MASK (uint64_t)(IA32E_NUM_ENTRIES - 1)
#define IA32E_REF_MASK 0x7FFFFFFFFFFFF000
#define IA32E_FIRST_BLOCK_INDEX 1
/* Macro to get PML4 index given an address */
#define IA32E_PML4E_INDEX_CALC(address) \
(uint32_t)((((uint64_t)address >> IA32E_PML4E_INDEX_MASK_START) & \
IA32E_INDEX_MASK) * sizeof(uint64_t))
/* Macro to get PDPT index given an address */
#define IA32E_PDPTE_INDEX_CALC(address) \
(uint32_t)((((uint64_t)address >> IA32E_PDPTE_INDEX_MASK_START) & \
IA32E_INDEX_MASK) * sizeof(uint64_t))
/* Macro to get PD index given an address */
#define IA32E_PDE_INDEX_CALC(address) \
(uint32_t)((((uint64_t)address >> IA32E_PDE_INDEX_MASK_START) & \
IA32E_INDEX_MASK) * sizeof(uint64_t))
/* Macro to get PT index given an address */
#define IA32E_PTE_INDEX_CALC(address) \
(uint32_t)((((uint64_t)address >> IA32E_PTE_INDEX_MASK_START) & \
IA32E_INDEX_MASK) * sizeof(uint64_t))
/* Macro to obtain a 2 MB page offset from given linear address */
#define IA32E_GET_2MB_PG_OFFSET(address) \
(address & 0x001FFFFF)
/* Macro to obtain a 4KB page offset from given linear address */
#define IA32E_GET_4KB_PG_OFFSET(address) \
(address & 0x00000FFF)
/*
* The following generic attributes MMU_MEM_ATTR_FLAG_xxx may be OR'd with one
* and only one of the MMU_MEM_ATTR_TYPE_xxx definitions
*/
/* Generic memory attributes */
#define MMU_MEM_ATTR_READ 0x00000001
#define MMU_MEM_ATTR_WRITE 0x00000002
#define MMU_MEM_ATTR_EXECUTE 0x00000004
#define MMU_MEM_ATTR_USER 0x00000008
#define MMU_MEM_ATTR_WB_CACHE 0x00000040
#define MMU_MEM_ATTR_WT_CACHE 0x00000080
#define MMU_MEM_ATTR_UNCACHED 0x00000100
#define MMU_MEM_ATTR_WC 0x00000200
#define MMU_MEM_ATTR_WP 0x00000400
/* Definitions for memory types related to x64 */
#define MMU_MEM_ATTR_BIT_READ_WRITE IA32E_COMM_RW_BIT
#define MMU_MEM_ATTR_BIT_USER_ACCESSIBLE IA32E_COMM_US_BIT
#define MMU_MEM_ATTR_BIT_EXECUTE_DISABLE IA32E_COMM_XD_BIT
/* Selection of Page Attribute Table (PAT) entries with PAT, PCD and PWT
* encoding. See also pat.h
*/
/* Selects PAT0 WB */
#define MMU_MEM_ATTR_TYPE_CACHED_WB (0x0000000000000000)
/* Selects PAT1 WT */
#define MMU_MEM_ATTR_TYPE_CACHED_WT (IA32E_COMM_PWT_BIT)
/* Selects PAT2 UCM */
#define MMU_MEM_ATTR_TYPE_UNCACHED_MINUS (IA32E_COMM_PCD_BIT)
/* Selects PAT3 UC */
#define MMU_MEM_ATTR_TYPE_UNCACHED \
(IA32E_COMM_PCD_BIT | IA32E_COMM_PWT_BIT)
/* Selects PAT6 WC */
#define MMU_MEM_ATTR_TYPE_WRITE_COMBINED \
(IA32E_PDPTE_PAT_BIT | IA32E_COMM_PCD_BIT)
/* Selects PAT7 WP */
#define MMU_MEM_ATTR_TYPE_WRITE_PROTECTED \
(IA32E_PDPTE_PAT_BIT | IA32E_COMM_PCD_BIT | IA32E_COMM_PWT_BIT)
#define ROUND_PAGE_UP(addr) (((addr) + CPU_PAGE_SIZE - 1) & IA32E_REF_MASK)
#define ROUND_PAGE_DOWN(addr) ((addr) & IA32E_REF_MASK)
struct map_params {
/* enum _page_table_type: HOST or EPT*/
int page_table_type;
/* used HVA->HPA for HOST, used GPA->HPA for EPT */
void *pml4_base;
/* used HPA->HVA for HOST, used HPA->GPA for EPT */
void *pml4_inverted;
};
struct entry_params {
uint32_t entry_level;
uint32_t entry_present;
uint64_t entry_base;
uint64_t entry_off;
uint64_t entry_val;
uint64_t page_size;
};
enum _page_table_type {
PT_HOST = 0, /* Mapping for hypervisor */
PT_EPT = 1,
PAGETABLE_TYPE_UNKNOWN,
};
/* Represent the 4 levels of translation tables in IA-32e paging mode */
enum _page_table_level {
IA32E_PML4 = 0,
IA32E_PDPT = 1,
IA32E_PD = 2,
IA32E_PT = 3,
IA32E_UNKNOWN,
};
/* Page table entry present */
enum _page_table_present {
PT_NOT_PRESENT = 0,
PT_PRESENT = 1,
};
/* Page size */
#define PAGE_SIZE_4K MEM_4K
#define PAGE_SIZE_2M MEM_2M
#define PAGE_SIZE_1G MEM_1G
/* Macros for reading/writing memory */
#define MEM_READ8(addr) (*(volatile uint8_t *)(addr))
#define MEM_WRITE8(addr, data) \
(*(volatile uint8_t *)(addr) = (uint8_t)(data))
#define MEM_READ16(addr) (*(volatile uint16_t *)(addr))
#define MEM_WRITE16(addr, data) \
(*(volatile uint16_t *)(addr) = (uint16_t)(data))
#define MEM_READ32(addr) (*(volatile uint32_t *)(addr))
#define MEM_WRITE32(addr, data) \
(*(volatile uint32_t *)(addr) = (uint32_t)(data))
#define MEM_READ64(addr) (*(volatile uint64_t *)(addr))
#define MEM_WRITE64(addr, data) \
(*(volatile uint64_t *)(addr) = (uint64_t)(data))
/* Typedef for MMIO handler and range check routine */
typedef int(*hv_mem_io_handler_t)(struct vcpu *, struct mem_io *, void *);
/* Structure for MMIO handler node */
struct mem_io_node {
hv_mem_io_handler_t read_write;
void *handler_private_data;
struct list_head list;
uint64_t range_start;
uint64_t range_end;
};
void *get_paging_pml4(void);
void *alloc_paging_struct();
void enable_paging(void *pml4_base_addr);
void init_paging(void);
void map_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags);
void unmap_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags);
void modify_mem(struct map_params *map_params, void *paddr, void *vaddr,
uint64_t size, uint32_t flags);
void mmu_invept(struct vcpu *vcpu);
void obtain_last_page_table_entry(struct map_params *map_params,
struct entry_params *entry, void *addr, bool direct);
int register_mmio_emulation_handler(struct vm *vm,
hv_mem_io_handler_t read_write, uint64_t start,
uint64_t end, void *handler_private_data);
void unregister_mmio_emulation_handler(struct vm *vm, uint64_t start,
uint64_t end);
#pragma pack(1)
/** Defines a single entry in an E820 memory map. */
struct e820_entry {
/** The base address of the memory range. */
uint64_t baseaddr;
/** The length of the memory range. */
uint64_t length;
/** The type of memory region. */
uint32_t type;
};
#pragma pack()
/* E820 memory types */
#define E820_TYPE_RAM 1 /* EFI 1, 2, 3, 4, 5, 6, 7 */
#define E820_TYPE_RESERVED 2
/* EFI 0, 11, 12, 13 (everything not used elsewhere) */
#define E820_TYPE_ACPI_RECLAIM 3 /* EFI 9 */
#define E820_TYPE_ACPI_NVS 4 /* EFI 10 */
#define E820_TYPE_UNUSABLE 5 /* EFI 8 */
/** Calculates the page table address for a given address.
*
* @param pd The base address of the page directory.
* @param vaddr The virtual address to calculate the page table address for.
*
* @return A pointer to the page table for the specified virtual address.
*
*/
static inline void *mmu_pt_for_pde(uint32_t *pd, uint32_t vaddr)
{
return pd + ((vaddr >> 22) + 1) * 1024;
}
#define CACHE_FLUSH_INVALIDATE_ALL() \
{ \
asm volatile (" wbinvd\n" : : : "memory"); \
}
/* External variable declarations */
extern uint8_t CPU_Boot_Page_Tables_Start_VM[];
/* External Interfaces */
int is_ept_supported(void);
void *create_guest_paging(struct vm *vm);
void destroy_ept(struct vm *vm);
uint64_t gpa2hpa(struct vm *vm, uint64_t gpa);
uint64_t gpa2hpa_check(struct vm *vm, uint64_t gpa,
uint64_t size, int *found, bool assert);
uint64_t hpa2gpa(struct vm *vm, uint64_t hpa);
int ept_mmap(struct vm *vm, uint64_t hpa,
uint64_t gpa, uint64_t size, uint32_t type, uint32_t prot);
int ept_violation_handler(struct vcpu *vcpu);
int ept_misconfig_handler(struct vcpu *vcpu);
int dm_emulate_mmio_post(struct vcpu *vcpu);
#endif /* ASSEMBLER not defined */
#endif /* MMU_H */

View File

@@ -0,0 +1,563 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 MSR_H
#define MSR_H
/* architectural (common) MSRs */
#define MSR_IA32_P5_MC_ADDR 0x00000000
/* Machine check address for MC exception handler */
#define MSR_IA32_P5_MC_TYPE 0x00000001
/* Machine check error type for MC exception handler */
#define MSR_IA32_MONITOR_FILTER_SIZE 0x00000006
/* System coherence line size for MWAIT/MONITOR */
#define MSR_IA32_TIME_STAMP_COUNTER 0x00000010 /* TSC as MSR */
#define MSR_IA32_PLATFORM_ID 0x00000017 /* Platform ID */
#define MSR_IA32_APIC_BASE 0x0000001B
/* Information about LAPIC */
#define MSR_IA32_FEATURE_CONTROL 0x0000003A
/* Speculation Control */
#define MSR_IA32_SPEC_CTRL 0x00000048
/* Prediction Command */
#define MSR_IA32_PRED_CMD 0x00000049
/* Control Features in Intel 64 processor */
#define MSR_IA32_ADJUST_TSC 0x0000003B /* Adjust TSC value */
#define MSR_IA32_BIOS_UPDT_TRIG 0x00000079
/* BIOS update trigger */
#define MSR_IA32_BIOS_SIGN_ID 0x0000008B
/* BIOS update signature */
#define MSR_IA32_SMM_MONITOR_CTL 0x0000009B
/* SMM monitor configuration */
#define MSR_IA32_PMC0 0x000000C1
/* General performance counter 0 */
#define MSR_IA32_PMC1 0x000000C2
/* General performance counter 1 */
#define MSR_IA32_PMC2 0x000000C3
/* General performance counter 2 */
#define MSR_IA32_PMC3 0x000000C4
/* General performance counter 3 */
#define MSR_IA32_MPERF 0x000000E7
/* Max. qualified performance clock counter */
#define MSR_IA32_APERF 0x000000E8
/* Actual performance clock counter */
#define MSR_IA32_MTRR_CAP 0x000000FE /* MTRR capability */
#define MSR_IA32_SYSENTER_CS 0x00000174 /* CS for sysenter */
#define MSR_IA32_SYSENTER_ESP 0x00000175 /* ESP for sysenter */
#define MSR_IA32_SYSENTER_EIP 0x00000176 /* EIP for sysenter */
#define MSR_IA32_MCG_CAP 0x00000179
/* Global machine check capability */
#define MSR_IA32_MCG_STATUS 0x0000017A
/* Global machine check status */
#define MSR_IA32_MCG_CTL 0x0000017B
/* Global machine check control */
#define MSR_IA32_PERFEVTSEL0 0x00000186
/* Performance Event Select Register 0 */
#define MSR_IA32_PERFEVTSEL1 0x00000187
/* Performance Event Select Register 1 */
#define MSR_IA32_PERFEVTSEL2 0x00000188
/* Performance Event Select Register 2 */
#define MSR_IA32_PERFEVTSEL3 0x00000189
/* Performance Event Select Register 3 */
#define MSR_IA32_PERF_STATUS 0x00000198
/* Current performance state */
#define MSR_IA32_PERF_CTL 0x00000199
/* Performance control */
#define MSR_IA32_CLOCK_MODULATION 0x0000019A
/* Clock modulation control */
#define MSR_IA32_THERM_INTERRUPT 0x0000019B
/* Thermal interrupt control */
#define MSR_IA32_THERM_STATUS 0x0000019C
/* Thermal status information */
#define MSR_IA32_MISC_ENABLE 0x000001A0
/* Enable misc. processor features */
#define MSR_IA32_ENERGY_PERF_BIAS 0x000001B0
/* Performance energy bias hint */
#define MSR_IA32_DEBUGCTL 0x000001D9
/* Trace/Profile resource control */
#define MSR_IA32_SMRR_PHYSBASE 0x000001F2 /* SMRR base address */
#define MSR_IA32_SMRR_PHYSMASK 0x000001F3 /* SMRR range mask */
#define MSR_IA32_PLATFORM_DCA_CAP 0x000001F8 /* DCA capability */
#define MSR_IA32_CPU_DCA_CAP 0x000001F9
/* Prefetch hint type capability */
#define MSR_IA32_DCA_0_CAP 0x000001FA
/* DCA type 0 status/control */
#define MSR_IA32_MTRR_PHYSBASE_0 0x00000200
/* variable range MTRR base 0 */
#define MSR_IA32_MTRR_PHYSMASK_0 0x00000201
/* variable range MTRR mask 0 */
#define MSR_IA32_MTRR_PHYSBASE_1 0x00000202
/* variable range MTRR base 1 */
#define MSR_IA32_MTRR_PHYSMASK_1 0x00000203
/* variable range MTRR mask 1 */
#define MSR_IA32_MTRR_PHYSBASE_2 0x00000204
/* variable range MTRR base 2 */
#define MSR_IA32_MTRR_PHYSMASK_2 0x00000205
/* variable range MTRR mask 2 */
#define MSR_IA32_MTRR_PHYSBASE_3 0x00000206
/* variable range MTRR base 3 */
#define MSR_IA32_MTRR_PHYSMASK_3 0x00000207
/* variable range MTRR mask 3 */
#define MSR_IA32_MTRR_PHYSBASE_4 0x00000208
/* variable range MTRR base 4 */
#define MSR_IA32_MTRR_PHYSMASK_4 0x00000209
/* variable range MTRR mask 4 */
#define MSR_IA32_MTRR_PHYSBASE_5 0x0000020A
/* variable range MTRR base 5 */
#define MSR_IA32_MTRR_PHYSMASK_5 0x0000020B
/* variable range MTRR mask 5 */
#define MSR_IA32_MTRR_PHYSBASE_6 0x0000020C
/* variable range MTRR base 6 */
#define MSR_IA32_MTRR_PHYSMASK_6 0x0000020D
/* variable range MTRR mask 6 */
#define MSR_IA32_MTRR_PHYSBASE_7 0x0000020E
/* variable range MTRR base 7 */
#define MSR_IA32_MTRR_PHYSMASK_7 0x0000020F
/* variable range MTRR mask 7 */
#define MSR_IA32_MTRR_PHYSBASE_8 0x00000210
/* variable range MTRR base 8 */
#define MSR_IA32_MTRR_PHYSMASK_8 0x00000211
/* variable range MTRR mask 8 */
#define MSR_IA32_MTRR_PHYSBASE_9 0x00000212
/* variable range MTRR base 9 */
#define MSR_IA32_MTRR_PHYSMASK_9 0x00000213
/* variable range MTRR mask 9 */
#define MSR_IA32_MTRR_FIX64K_00000 0x00000250
/* fixed range MTRR 16K/0x00000 */
#define MSR_IA32_MTRR_FIX16K_80000 0x00000258
/* fixed range MTRR 16K/0x80000 */
#define MSR_IA32_MTRR_FIX16K_A0000 0x00000259
/* fixed range MTRR 16K/0xA0000 */
#define MSR_IA32_MTRR_FIX4K_C0000 0x00000268
/* fixed range MTRR 4K/0xC0000 */
#define MSR_IA32_MTRR_FIX4K_C8000 0x00000269
/* fixed range MTRR 4K/0xC8000 */
#define MSR_IA32_MTRR_FIX4K_D0000 0x0000026A
/* fixed range MTRR 4K/0xD0000 */
#define MSR_IA32_MTRR_FIX4K_D8000 0x0000026B
/* fixed range MTRR 4K/0xD8000 */
#define MSR_IA32_MTRR_FIX4K_E0000 0x0000026C
/* fixed range MTRR 4K/0xE0000 */
#define MSR_IA32_MTRR_FIX4K_E8000 0x0000026D
/* fixed range MTRR 4K/0xE8000 */
#define MSR_IA32_MTRR_FIX4K_F0000 0x0000026E
/* fixed range MTRR 4K/0xF0000 */
#define MSR_IA32_MTRR_FIX4K_F8000 0x0000026F
/* fixed range MTRR 4K/0xF8000 */
#define MSR_IA32_PAT 0x00000277 /* PAT */
#define MSR_IA32_MC0_CTL2 0x00000280
/* Corrected error count threshold 0 */
#define MSR_IA32_MC1_CTL2 0x00000281
/* Corrected error count threshold 1 */
#define MSR_IA32_MC2_CTL2 0x00000282
/* Corrected error count threshold 2 */
#define MSR_IA32_MC3_CTL2 0x00000283
/* Corrected error count threshold 3 */
#define MSR_IA32_MC4_CTL2 0x00000284
/* Corrected error count threshold 4 */
#define MSR_IA32_MC5_CTL2 0x00000285
/* Corrected error count threshold 5 */
#define MSR_IA32_MC6_CTL2 0x00000286
/* Corrected error count threshold 6 */
#define MSR_IA32_MC7_CTL2 0x00000287
/* Corrected error count threshold 7 */
#define MSR_IA32_MC8_CTL2 0x00000288
/* Corrected error count threshold 8 */
#define MSR_IA32_MC9_CTL2 0x00000289
/* Corrected error count threshold 9 */
#define MSR_IA32_MC10_CTL2 0x0000028A
/* Corrected error count threshold 10 */
#define MSR_IA32_MC11_CTL2 0x0000028B
/* Corrected error count threshold 11 */
#define MSR_IA32_MC12_CTL2 0x0000028C
/* Corrected error count threshold 12 */
#define MSR_IA32_MC13_CTL2 0x0000028D
/* Corrected error count threshold 13 */
#define MSR_IA32_MC14_CTL2 0x0000028E
/* Corrected error count threshold 14 */
#define MSR_IA32_MC15_CTL2 0x0000028F
/* Corrected error count threshold 15 */
#define MSR_IA32_MC16_CTL2 0x00000290
/* Corrected error count threshold 16 */
#define MSR_IA32_MC17_CTL2 0x00000291
/* Corrected error count threshold 17 */
#define MSR_IA32_MC18_CTL2 0x00000292
/* Corrected error count threshold 18 */
#define MSR_IA32_MC19_CTL2 0x00000293
/* Corrected error count threshold 19 */
#define MSR_IA32_MC20_CTL2 0x00000294
/* Corrected error count threshold 20 */
#define MSR_IA32_MC21_CTL2 0x00000295
/* Corrected error count threshold 21 */
#define MSR_IA32_MTRR_DEF_TYPE 0x000002FF
/* Default memory type/MTRR control */
#define MSR_IA32_FIXED_CTR0 0x00000309
/* Fixed-function performance counter 0 */
#define MSR_IA32_FIXED_CTR1 0x0000030A
/* Fixed-function performance counter 1 */
#define MSR_IA32_FIXED_CTR2 0x0000030B
/* Fixed-function performance counter 2 */
#define MSR_IA32_PERF_CAPABILITIES 0x00000345
/* Performance capability */
#define MSR_IA32_FIXED_CTR_CTL 0x0000038D
/* Fixed-function performance counter control */
#define MSR_IA32_PERF_GLOBAL_STATUS 0x0000038E
/* Global performance counter status */
#define MSR_IA32_PERF_GLOBAL_CTRL 0x0000038F
/* Global performance counter control */
#define MSR_IA32_PERF_GLOBAL_OVF_CTRL 0x00000390
/* Global performance counter overflow control */
#define MSR_IA32_PEBS_ENABLE 0x000003F1 /* PEBS control */
#define MSR_IA32_MC0_CTL 0x00000400 /* MC 0 control */
#define MSR_IA32_MC0_STATUS 0x00000401 /* MC 0 status */
#define MSR_IA32_MC0_ADDR 0x00000402 /* MC 0 address */
#define MSR_IA32_MC0_MISC 0x00000403 /* MC 0 misc. */
#define MSR_IA32_MC1_CTL 0x00000404 /* MC 1 control */
#define MSR_IA32_MC1_STATUS 0x00000405 /* MC 1 status */
#define MSR_IA32_MC1_ADDR 0x00000406 /* MC 1 address */
#define MSR_IA32_MC1_MISC 0x00000407 /* MC 1 misc. */
#define MSR_IA32_MC2_CTL 0x00000408 /* MC 2 control */
#define MSR_IA32_MC2_STATUS 0x00000409 /* MC 2 status */
#define MSR_IA32_MC2_ADDR 0x0000040A /* MC 2 address */
#define MSR_IA32_MC2_MISC 0x0000040B /* MC 2 misc. */
#define MSR_IA32_MC3_CTL 0x0000040C /* MC 3 control */
#define MSR_IA32_MC3_STATUS 0x0000040D /* MC 3 status */
#define MSR_IA32_MC3_ADDR 0x0000040E /* MC 3 address */
#define MSR_IA32_MC3_MISC 0x0000040F /* MC 3 misc. */
#define MSR_IA32_MC4_CTL 0x00000410 /* MC 4 control */
#define MSR_IA32_MC4_STATUS 0x00000411 /* MC 4 status */
#define MSR_IA32_MC4_ADDR 0x00000412 /* MC 4 address */
#define MSR_IA32_MC4_MISC 0x00000413 /* MC 4 misc. */
#define MSR_IA32_MC5_CTL 0x00000414 /* MC 5 control */
#define MSR_IA32_MC5_STATUS 0x00000415 /* MC 5 status */
#define MSR_IA32_MC5_ADDR 0x00000416 /* MC 5 address */
#define MSR_IA32_MC5_MISC 0x00000417 /* MC 5 misc. */
#define MSR_IA32_MC6_CTL 0x00000418 /* MC 6 control */
#define MSR_IA32_MC6_STATUS 0x00000419 /* MC 6 status */
#define MSR_IA32_MC6_ADDR 0x0000041A /* MC 6 address */
#define MSR_IA32_MC6_MISC 0x0000041B /* MC 6 misc. */
#define MSR_IA32_MC7_CTL 0x0000041C /* MC 7 control */
#define MSR_IA32_MC7_STATUS 0x0000041D /* MC 7 status */
#define MSR_IA32_MC7_ADDR 0x0000041E /* MC 7 address */
#define MSR_IA32_MC7_MISC 0x0000041F /* MC 7 misc. */
#define MSR_IA32_MC8_CTL 0x00000420 /* MC 8 control */
#define MSR_IA32_MC8_STATUS 0x00000421 /* MC 8 status */
#define MSR_IA32_MC8_ADDR 0x00000422 /* MC 8 address */
#define MSR_IA32_MC8_MISC 0x00000423 /* MC 8 misc. */
#define MSR_IA32_MC9_CTL 0x00000424 /* MC 9 control */
#define MSR_IA32_MC9_STATUS 0x00000425 /* MC 9 status */
#define MSR_IA32_MC9_ADDR 0x00000426 /* MC 9 address */
#define MSR_IA32_MC9_MISC 0x00000427 /* MC 9 misc. */
#define MSR_IA32_MC10_CTL 0x00000428 /* MC 10 control */
#define MSR_IA32_MC10_STATUS 0x00000429 /* MC 10 status */
#define MSR_IA32_MC10_ADDR 0x0000042A /* MC 10 address */
#define MSR_IA32_MC10_MISC 0x0000042B /* MC 10 misc. */
#define MSR_IA32_MC11_CTL 0x0000042C /* MC 11 control */
#define MSR_IA32_MC11_STATUS 0x0000042D /* MC 11 status */
#define MSR_IA32_MC11_ADDR 0x0000042E /* MC 11 address */
#define MSR_IA32_MC11_MISC 0x0000042F /* MC 11 misc. */
#define MSR_IA32_MC12_CTL 0x00000430 /* MC 12 control */
#define MSR_IA32_MC12_STATUS 0x00000431 /* MC 12 status */
#define MSR_IA32_MC12_ADDR 0x00000432 /* MC 12 address */
#define MSR_IA32_MC12_MISC 0x00000433 /* MC 12 misc. */
#define MSR_IA32_MC13_CTL 0x00000434 /* MC 13 control */
#define MSR_IA32_MC13_STATUS 0x00000435 /* MC 13 status */
#define MSR_IA32_MC13_ADDR 0x00000436 /* MC 13 address */
#define MSR_IA32_MC13_MISC 0x00000437 /* MC 13 misc. */
#define MSR_IA32_MC14_CTL 0x00000438 /* MC 14 control */
#define MSR_IA32_MC14_STATUS 0x00000439 /* MC 14 status */
#define MSR_IA32_MC14_ADDR 0x0000043A /* MC 14 address */
#define MSR_IA32_MC14_MISC 0x0000043B /* MC 14 misc. */
#define MSR_IA32_MC15_CTL 0x0000043C /* MC 15 control */
#define MSR_IA32_MC15_STATUS 0x0000043D /* MC 15 status */
#define MSR_IA32_MC15_ADDR 0x0000043E /* MC 15 address */
#define MSR_IA32_MC15_MISC 0x0000043F /* MC 15 misc. */
#define MSR_IA32_MC16_CTL 0x00000440 /* MC 16 control */
#define MSR_IA32_MC16_STATUS 0x00000441 /* MC 16 status */
#define MSR_IA32_MC16_ADDR 0x00000442 /* MC 16 address */
#define MSR_IA32_MC16_MISC 0x00000443 /* MC 16 misc. */
#define MSR_IA32_MC17_CTL 0x00000444 /* MC 17 control */
#define MSR_IA32_MC17_STATUS 0x00000445 /* MC 17 status */
#define MSR_IA32_MC17_ADDR 0x00000446 /* MC 17 address */
#define MSR_IA32_MC17_MISC 0x00000447 /* MC 17 misc. */
#define MSR_IA32_MC18_CTL 0x00000448 /* MC 18 control */
#define MSR_IA32_MC18_STATUS 0x00000449 /* MC 18 status */
#define MSR_IA32_MC18_ADDR 0x0000044A /* MC 18 address */
#define MSR_IA32_MC18_MISC 0x0000044B /* MC 18 misc. */
#define MSR_IA32_MC19_CTL 0x0000044C /* MC 19 control */
#define MSR_IA32_MC19_STATUS 0x0000044D /* MC 19 status */
#define MSR_IA32_MC19_ADDR 0x0000044E /* MC 19 address */
#define MSR_IA32_MC19_MISC 0x0000044F /* MC 19 misc. */
#define MSR_IA32_MC20_CTL 0x00000450 /* MC 20 control */
#define MSR_IA32_MC20_STATUS 0x00000451 /* MC 20 status */
#define MSR_IA32_MC20_ADDR 0x00000452 /* MC 20 address */
#define MSR_IA32_MC20_MISC 0x00000453 /* MC 20 misc. */
#define MSR_IA32_MC21_CTL 0x00000454 /* MC 21 control */
#define MSR_IA32_MC21_STATUS 0x00000455 /* MC 21 status */
#define MSR_IA32_MC21_ADDR 0x00000456 /* MC 21 address */
#define MSR_IA32_MC21_MISC 0x00000457 /* MC 21 misc. */
#define MSR_IA32_VMX_BASIC 0x00000480
/* Capability reporting register basic VMX capabilities */
#define MSR_IA32_VMX_PINBASED_CTLS 0x00000481
/* Capability reporting register pin based VM execution controls */
#define MSR_IA32_VMX_PROCBASED_CTLS 0x00000482
/* Capability reporting register primary processor based VM execution controls*/
#define MSR_IA32_VMX_EXIT_CTLS 0x00000483
/* Capability reporting register VM exit controls */
#define MSR_IA32_VMX_ENTRY_CTLS 0x00000484
/* Capability reporting register VM entry controls */
#define MSR_IA32_VMX_MISC 0x00000485
/* Reporting register misc. VMX capabilities */
#define MSR_IA32_VMX_CR0_FIXED0 0x00000486
/* Capability reporting register of CR0 bits fixed to 0 */
#define MSR_IA32_VMX_CR0_FIXED1 0x00000487
/* Capability reporting register of CR0 bits fixed to 1 */
#define MSR_IA32_VMX_CR4_FIXED0 0x00000488
/* Capability reporting register of CR4 bits fixed to 0 */
#define MSR_IA32_VMX_CR4_FIXED1 0x00000489
/* Capability reporting register of CR4 bits fixed to 1 */
#define MSR_IA32_VMX_VMCS_ENUM 0x0000048A
/* Capability reporting register of VMCS field enumeration */
#define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048B
/* Capability reporting register of secondary processor based VM execution
* controls
*/
#define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048C
/* Capability reporting register of EPT and VPID */
#define MSR_IA32_VMX_TRUE_PINBASED_CTLS 0x0000048D
/* Capability reporting register of pin based VM execution flex controls */
#define MSR_IA32_VMX_TRUE_PROCBASED_CTLS 0x0000048E
/* Capability reporting register of primary processor based VM execution flex
* controls
*/
#define MSR_IA32_VMX_TRUE_EXIT_CTLS 0x0000048F
/* Capability reporting register of VM exit flex controls */
#define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490
/* Capability reporting register of VM entry flex controls */
#define MSR_IA32_DS_AREA 0x00000600 /* DS save area */
/* APIC TSC deadline MSR */
#define MSR_IA32_TSC_DEADLINE 0x000006E0
#define MSR_IA32_EXT_XAPICID 0x00000802 /* x2APIC ID */
#define MSR_IA32_EXT_APIC_VERSION 0x00000803 /* x2APIC version */
#define MSR_IA32_EXT_APIC_TPR 0x00000808
/* x2APIC task priority */
#define MSR_IA32_EXT_APIC_PPR 0x0000080A
/* x2APIC processor priority */
#define MSR_IA32_EXT_APIC_EOI 0x0000080B /* x2APIC EOI */
#define MSR_IA32_EXT_APIC_LDR 0x0000080D
/* x2APIC logical destination */
#define MSR_IA32_EXT_APIC_SIVR 0x0000080F
/* x2APIC spurious interrupt vector */
#define MSR_IA32_EXT_APIC_ISR0 0x00000810
/* x2APIC in-service register 0 */
#define MSR_IA32_EXT_APIC_ISR1 0x00000811
/* x2APIC in-service register 1 */
#define MSR_IA32_EXT_APIC_ISR2 0x00000812
/* x2APIC in-service register 2 */
#define MSR_IA32_EXT_APIC_ISR3 0x00000813
/* x2APIC in-service register 3 */
#define MSR_IA32_EXT_APIC_ISR4 0x00000814
/* x2APIC in-service register 4 */
#define MSR_IA32_EXT_APIC_ISR5 0x00000815
/* x2APIC in-service register 5 */
#define MSR_IA32_EXT_APIC_ISR6 0x00000816
/* x2APIC in-service register 6 */
#define MSR_IA32_EXT_APIC_ISR7 0x00000817
/* x2APIC in-service register 7 */
#define MSR_IA32_EXT_APIC_TMR0 0x00000818
/* x2APIC trigger mode register 0 */
#define MSR_IA32_EXT_APIC_TMR1 0x00000819
/* x2APIC trigger mode register 1 */
#define MSR_IA32_EXT_APIC_TMR2 0x0000081A
/* x2APIC trigger mode register 2 */
#define MSR_IA32_EXT_APIC_TMR3 0x0000081B
/* x2APIC trigger mode register 3 */
#define MSR_IA32_EXT_APIC_TMR4 0x0000081C
/* x2APIC trigger mode register 4 */
#define MSR_IA32_EXT_APIC_TMR5 0x0000081D
/* x2APIC trigger mode register 5 */
#define MSR_IA32_EXT_APIC_TMR6 0x0000081E
/* x2APIC trigger mode register 6 */
#define MSR_IA32_EXT_APIC_TMR7 0x0000081F
/* x2APIC trigger mode register 7 */
#define MSR_IA32_EXT_APIC_IRR0 0x00000820
/* x2APIC interrupt request register 0 */
#define MSR_IA32_EXT_APIC_IRR1 0x00000821
/* x2APIC interrupt request register 1 */
#define MSR_IA32_EXT_APIC_IRR2 0x00000822
/* x2APIC interrupt request register 2 */
#define MSR_IA32_EXT_APIC_IRR3 0x00000823
/* x2APIC interrupt request register 3 */
#define MSR_IA32_EXT_APIC_IRR4 0x00000824
/* x2APIC interrupt request register 4 */
#define MSR_IA32_EXT_APIC_IRR5 0x00000825
/* x2APIC interrupt request register 5 */
#define MSR_IA32_EXT_APIC_IRR6 0x00000826
/* x2APIC interrupt request register 6 */
#define MSR_IA32_EXT_APIC_IRR7 0x00000827
/* x2APIC interrupt request register 7 */
#define MSR_IA32_EXT_APIC_ESR 0x00000828
/* x2APIC error status */
#define MSR_IA32_EXT_APIC_LVT_CMCI 0x0000082F
/* x2APIC LVT corrected machine check interrupt register */
#define MSR_IA32_EXT_APIC_ICR 0x00000830
/* x2APIC interrupt command register */
#define MSR_IA32_EXT_APIC_LVT_TIMER 0x00000832
/* x2APIC LVT timer interrupt register */
#define MSR_IA32_EXT_APIC_LVT_THERMAL 0x00000833
/* x2APIC LVT thermal sensor interrupt register */
#define MSR_IA32_EXT_APIC_LVT_PMI 0x00000834
/* x2APIC LVT performance monitor interrupt register */
#define MSR_IA32_EXT_APIC_LVT_LINT0 0x00000835
/* x2APIC LVT LINT0 register */
#define MSR_IA32_EXT_APIC_LVT_LINT1 0x00000836
/* x2APIC LVT LINT1 register */
#define MSR_IA32_EXT_APIC_LVT_ERROR 0x00000837
/* x2APIC LVT error register */
#define MSR_IA32_EXT_APIC_INIT_COUNT 0x00000838
/* x2APIC initial count register */
#define MSR_IA32_EXT_APIC_CUR_COUNT 0x00000839
/* x2APIC current count register */
#define MSR_IA32_EXT_APIC_DIV_CONF 0x0000083E
/* x2APIC divide configuration register */
#define MSR_IA32_EXT_APIC_SELF_IPI 0x0000083F
/* x2APIC self IPI register */
#define MSR_IA32_EFER 0xC0000080
/* Extended feature enables */
#define MSR_IA32_STAR 0xC0000081
/* System call target address */
#define MSR_IA32_LSTAR 0xC0000082
/* IA-32e mode system call target address */
#define MSR_IA32_FMASK 0xC0000084
/* System call flag mask */
#define MSR_IA32_FS_BASE 0xC0000100
/* Map of BASE address of FS */
#define MSR_IA32_GS_BASE 0xC0000101
/* Map of BASE address of GS */
#define MSR_IA32_KERNEL_GS_BASE 0xC0000102
/* Swap target of BASE address of GS */
#define MSR_IA32_TSC_AUX 0xC0000103 /* Auxiliary TSC */
/* ATOM specific MSRs */
#define MSR_ATOM_EBL_CR_POWERON 0x0000002A
/* Processor hard power-on configuration */
#define MSR_ATOM_LASTBRANCH_0_FROM_IP 0x00000040
/* Last branch record 0 from IP */
#define MSR_ATOM_LASTBRANCH_1_FROM_IP 0x00000041
/* Last branch record 1 from IP */
#define MSR_ATOM_LASTBRANCH_2_FROM_IP 0x00000042
/* Last branch record 2 from IP */
#define MSR_ATOM_LASTBRANCH_3_FROM_IP 0x00000043
/* Last branch record 3 from IP */
#define MSR_ATOM_LASTBRANCH_4_FROM_IP 0x00000044
/* Last branch record 4 from IP */
#define MSR_ATOM_LASTBRANCH_5_FROM_IP 0x00000045
/* Last branch record 5 from IP */
#define MSR_ATOM_LASTBRANCH_6_FROM_IP 0x00000046
/* Last branch record 6 from IP */
#define MSR_ATOM_LASTBRANCH_7_FROM_IP 0x00000047
/* Last branch record 7 from IP */
#define MSR_ATOM_LASTBRANCH_0_TO_LIP 0x00000060
/* Last branch record 0 to IP */
#define MSR_ATOM_LASTBRANCH_1_TO_LIP 0x00000061
/* Last branch record 1 to IP */
#define MSR_ATOM_LASTBRANCH_2_TO_LIP 0x00000062
/* Last branch record 2 to IP */
#define MSR_ATOM_LASTBRANCH_3_TO_LIP 0x00000063
/* Last branch record 3 to IP */
#define MSR_ATOM_LASTBRANCH_4_TO_LIP 0x00000064
/* Last branch record 4 to IP */
#define MSR_ATOM_LASTBRANCH_5_TO_LIP 0x00000065
/* Last branch record 5 to IP */
#define MSR_ATOM_LASTBRANCH_6_TO_LIP 0x00000066
/* Last branch record 6 to IP */
#define MSR_ATOM_LASTBRANCH_7_TO_LIP 0x00000067
/* Last branch record 7 to IP */
#define MSR_ATOM_FSB_FREQ 0x000000CD /* Scalable bus speed */
#define MSR_PLATFORM_INFO 0x000000CE
/* Maximum resolved bus ratio */
#define MSR_ATOM_BBL_CR_CTL3 0x0000011E /* L2 hardware enabled */
#define MSR_ATOM_THERM2_CTL 0x0000019D
/* Mode of automatic thermal monitor */
#define MSR_ATOM_LASTBRANCH_TOS 0x000001C9
/* Last branch record stack TOS */
#define MSR_ATOM_LER_FROM_LIP 0x000001DD
/* Last exception record from linear IP */
#define MSR_ATOM_LER_TO_LIP 0x000001DE
/* Last exception record to linear IP */
/* LINCROFT specific MSRs */
#define MSR_LNC_BIOS_CACHE_AS_RAM 0x000002E0 /* Configure CAR */
/* MSR_IA32_VMX_EPT_VPID_CAP: EPT and VPID capability bits */
#define MSR_VMX_EPT_VPID_CAP_1GB (1UL << 17)/* EPT 1GB page */
#define MSR_VMX_INVEPT (1UL << 20)/* INVEPT */
#define MSR_VMX_INVEPT_SINGLE_CONTEXT (1UL << 25)/* INVEPT Single */
#define MSR_VMX_INVEPT_GLOBAL_CONTEXT (1UL << 26)/* INVEPT Global */
#define MSR_VMX_INVVPID (1UL << 32)/* INVVPID */
#define MSR_VMX_INVVPID_SINGLE_CONTEXT (1UL << 41)/* INVVPID Single */
#define MSR_VMX_INVVPID_GLOBAL_CONTEXT (1UL << 42)/* INVVPID Global */
/* EFER bits */
#define MSR_IA32_EFER_SCE_BIT (1<<0)
#define MSR_IA32_EFER_LME_BIT (1<<8) /* IA32e mode enable */
#define MSR_IA32_EFER_LMA_BIT (1<<10) /* IA32e mode active */
#define MSR_IA32_EFER_NXE_BIT (1<<11)
/* FEATURE CONTROL bits */
#define MSR_IA32_FEATURE_CONTROL_LOCK (1<<0)
#define MSR_IA32_FEATURE_CONTROL_VMX_SMX (1<<1)
#define MSR_IA32_FEATURE_CONTROL_VMX_NO_SMX (1<<2)
/* PAT memory type definitions */
#define PAT_MEM_TYPE_UC 0x00 /* uncached */
#define PAT_MEM_TYPE_WC 0x01 /* write combining */
#define PAT_MEM_TYPE_WT 0x04 /* write through */
#define PAT_MEM_TYPE_WP 0x05 /* write protected */
#define PAT_MEM_TYPE_WB 0x06 /* writeback */
#define PAT_MEM_TYPE_UCM 0x07 /* uncached minus */
/* MTRR memory type definitions */
#define MTRR_MEM_TYPE_UC 0x00 /* uncached */
#define MTRR_MEM_TYPE_WC 0x01 /* write combining */
#define MTRR_MEM_TYPE_WT 0x04 /* write through */
#define MTRR_MEM_TYPE_WP 0x05 /* write protected */
#define MTRR_MEM_TYPE_WB 0x06 /* writeback */
/* misc. MTRR flag definitions */
#define MTRR_ENABLE 0x800 /* MTRR enable */
#define MTRR_FIX_ENABLE 0x400 /* fixed range MTRR enable */
#define MTRR_VALID 0x800 /* MTRR setting is valid */
/* SPEC & PRED bit */
#define SPEC_ENABLE_IBRS (1<<0)
#define SPEC_ENABLE_STIBP (1<<1)
#define PRED_SET_IBPB (1<<0)
#endif /* MSR_H */

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2018 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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 MULTIBOOT_H
#define MULTIBOOT_H
#define MULTIBOOT_INFO_MAGIC 0x2BADB002
#define MULTIBOOT_INFO_HAS_CMDLINE 0x00000004
#define MULTIBOOT_INFO_HAS_MODS 0x00000008
struct multiboot_info {
uint32_t mi_flags;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MEMORY. */
uint32_t mi_mem_lower;
uint32_t mi_mem_upper;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_BOOT_DEVICE. */
uint8_t mi_boot_device_part3;
uint8_t mi_boot_device_part2;
uint8_t mi_boot_device_part1;
uint8_t mi_boot_device_drive;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CMDLINE. */
uint32_t mi_cmdline;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MODS. */
uint32_t mi_mods_count;
uint32_t mi_mods_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_{AOUT,ELF}_SYMS. */
uint32_t mi_elfshdr_num;
uint32_t mi_elfshdr_size;
uint32_t mi_elfshdr_addr;
uint32_t mi_elfshdr_shndx;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MMAP. */
uint32_t mi_mmap_length;
uint32_t mi_mmap_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_DRIVES. */
uint32_t mi_drives_length;
uint32_t mi_drives_addr;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CONFIG_TABLE. */
uint32_t unused_mi_config_table;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_LOADER_NAME. */
uint32_t mi_loader_name;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_APM. */
uint32_t unused_mi_apm_table;
/* Valid if mi_flags sets MULTIBOOT_INFO_HAS_VBE. */
uint32_t unused_mi_vbe_control_info;
uint32_t unused_mi_vbe_mode_info;
uint32_t unused_mi_vbe_interface_seg;
uint32_t unused_mi_vbe_interface_off;
uint32_t unused_mi_vbe_interface_len;
};
struct multiboot_mmap {
uint32_t size;
uint64_t baseaddr;
uint64_t length;
uint32_t type;
} __packed;
struct multiboot_module {
uint32_t mm_mod_start;
uint32_t mm_mod_end;
uint32_t mm_string;
uint32_t mm_reserved;
};
int parse_hv_cmdline(void);
#endif

Some files were not shown because too many files have changed in this diff Show More