l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
requires: stdlibs
# aliens, isr, singlestep, start-with-exc, utcb-ipc, vm-tz
optional: l4re_c-util
# ipc, migrate, uirq
optional: libpthread
# uirq
optional: libstdc++
# vcpu, vmtest, vm-tz
optional: libvcpu
# vcpu
optional: cxx_io cxx_libc_io
# vmtest
optional: l4util
# vm-tz
optional: libsigma0
Maintainer: adam@os.inf.tu-dresden.de

View File

@@ -0,0 +1,6 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../../..
TARGET = $(wildcard [a-z]*)
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_aliens
SRC_C = main.c
REQUIRES_LIBS = l4re_c-util
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,302 @@
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Björn Döbel <doebel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/*
* Example to show syscall tracing.
*/
#if defined(ARCH_x86) || defined(ARCH_amd64)
// MEASURE only works on x86/amd64
//#define MEASURE
#endif
#include <l4/sys/ipc.h>
#include <l4/sys/thread.h>
#include <l4/sys/factory.h>
#include <l4/sys/utcb.h>
#include <l4/util/util.h>
#include <l4/re/env.h>
#include <l4/re/c/util/cap_alloc.h>
#include <l4/re/c/util/kumem_alloc.h>
#include <l4/sys/debugger.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Architecture specifics */
#if defined(ARCH_x86) || defined(ARCH_amd64)
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{
#if defined(ARCH_x86)
return exc->err & 4;
#else
return exc->err == 1;
#endif
}
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx Err=%08lx Trap=%lx, %s syscall, SC-Nr: %lx\n",
l4_utcb_exc_pc(exc), exc->sp, exc->err,
exc->trapno, is_alien_after_call(exc) ? " after" : "before",
exc->err >> 3);
}
#elif defined(ARCH_arm)
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{ return exc->err & 0x40; } // TODO: Should change this to (1 << 16)
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx ULR=%08lx CPSR=%08lx Err=%lx/%lx, %s syscall\n",
l4_utcb_exc_pc(exc), exc->sp, exc->ulr, exc->cpsr,
exc->err, exc->err >> 26,
is_alien_after_call(exc) ? " after" : "before");
}
#elif defined(ARCH_arm64)
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{ return exc->err & (1ul << 16); }
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx PSTATE=%08lx Err=%lx/%lx, %s syscall\n",
l4_utcb_exc_pc(exc), exc->sp, exc->pstate,
exc->err, exc->err >> 26,
is_alien_after_call(exc) ? " after" : "before");
}
#elif defined(ARCH_mips)
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{ return 0; }
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx Cause=%lx, %s syscall\n",
l4_utcb_exc_pc(exc), exc->sp, exc->cause,
is_alien_after_call(exc) ? " after" : "before");
}
#elif defined(ARCH_riscv)
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{ return exc->cause == L4_riscv_ec_l4_alien_after_syscall; }
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx Cause=%lx, %s syscall\n",
l4_utcb_exc_pc(exc), exc->sp, exc->cause,
is_alien_after_call(exc) ? " after" : "before");
}
#else
static int
is_alien_after_call(l4_exc_regs_t const *exc)
{ return exc->err & 1; }
static inline void
_print_exc_state(l4_exc_regs_t const *exc)
{
printf("PC=%08lx SP=%08lx, %s syscall\n",
l4_utcb_exc_pc(exc), exc->sp,
is_alien_after_call(exc) ? " after" : "before");
}
#endif
/* Measurement mode specifics.
*
* In measurement mode the code is less verbose and uses RDTSC for alient exception
* performance measurement.
*/
#ifdef MEASURE
#include <l4/util/rdtsc.h>
static inline void
calibrate_timer(void)
{
l4_calibrate_tsc(l4re_kip());
}
static inline void
print_timediff(l4_cpu_time_t start)
{
e = l4_rdtsc();
printf("time %lld\n", l4_tsc_to_ns(e - start));
}
static inline void
alien_sleep(void)
{
l4_sleep(0);
}
static inline void
print_exc_state(l4_exc_regs_t const *exc)
{
if (0)
_print_exc_state(exc);
}
#else
static inline void
calibrate_timer(void)
{
}
static inline void
print_timediff(l4_cpu_time_t start)
{
(void)start;
}
static inline l4_cpu_time_t
l4_rdtsc(void)
{
return 0;
}
static inline void
alien_sleep(void)
{
l4_sleep(1000);
}
static inline void
print_exc_state(l4_exc_regs_t const *exc)
{
_print_exc_state(exc);
}
#endif
static char alien_thread_stack[8 << 10];
static l4_cap_idx_t alien;
static void alien_thread(void)
{
while (1)
{
l4_ipc_call(0x1234 << L4_CAP_SHIFT, l4_utcb(),
l4_msgtag(0, 0, 0, 0), L4_IPC_NEVER);
alien_sleep();
}
}
int main(void)
{
l4_msgtag_t tag;
l4_cpu_time_t s;
l4_utcb_t *u = l4_utcb();
l4_exc_regs_t exc;
l4_umword_t mr0, mr1;
printf("Alien feature testing\n");
l4_debugger_set_object_name(l4re_env()->main_thread, "alientest");
/* Start alien thread */
if (l4_is_invalid_cap(alien = l4re_util_cap_alloc()))
return 1;
l4_touch_rw(alien_thread_stack, sizeof(alien_thread_stack));
tag = l4_factory_create_thread(l4re_env()->factory, alien);
if (l4_error(tag))
return 2;
l4_debugger_set_object_name(alien, "alienth");
l4_addr_t kumem;
if (l4re_util_kumem_alloc(&kumem, 0, L4RE_THIS_TASK_CAP, l4re_env()->rm))
return 3;
l4_thread_control_start();
l4_thread_control_pager(l4re_env()->main_thread);
l4_thread_control_exc_handler(l4re_env()->main_thread);
l4_thread_control_bind((l4_utcb_t *)kumem, L4RE_THIS_TASK_CAP);
l4_thread_control_alien(1);
tag = l4_thread_control_commit(alien);
if (l4_error(tag))
return 4;
tag = l4_thread_ex_regs(alien,
(l4_umword_t)alien_thread,
(l4_umword_t)alien_thread_stack + sizeof(alien_thread_stack),
0);
if (l4_error(tag))
return 5;
l4_sched_param_t sp = l4_sched_param(1, 0);
tag = l4_scheduler_run_thread(l4re_env()->scheduler, alien, &sp);
if (l4_error(tag))
return 6;
calibrate_timer();
/* Pager/Exception loop */
if (l4_msgtag_has_error(tag = l4_ipc_receive(alien, u, L4_IPC_NEVER)))
{
printf("l4_ipc_receive failed");
return 7;
}
memcpy(&exc, l4_utcb_exc(), sizeof(exc));
mr0 = l4_utcb_mr()->mr[0];
mr1 = l4_utcb_mr()->mr[1];
for (;;)
{
s = l4_rdtsc();
if (l4_msgtag_is_exception(tag))
{
print_exc_state(&exc);
tag = l4_msgtag(is_alien_after_call(&exc)
? 0 : L4_PROTO_ALLOW_SYSCALL,
L4_UTCB_EXCEPTION_REGS_SIZE, 0, 0);
}
else
printf("Umm, non-handled request (like PF): %lx %lx\n", mr0, mr1);
memcpy(l4_utcb_exc(), &exc, sizeof(exc));
/* Reply and wait */
if (l4_msgtag_has_error(tag = l4_ipc_call(alien, u, tag, L4_IPC_NEVER)))
{
printf("l4_ipc_call failed\n");
return 8;
}
memcpy(&exc, l4_utcb_exc(), sizeof(exc));
mr0 = l4_utcb_mr()->mr[0];
mr1 = l4_utcb_mr()->mr[1];
print_timediff(s);
}
return 0;
}

View File

@@ -0,0 +1,7 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = dump_obj
SRC_CC = dump_obj.cc
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,159 @@
#include <cstdio>
#include <l4/sys/debugger.h>
#include <l4/sys/obj_info.h>
#include <l4/re/error_helper>
#include <l4/re/util/kumem_alloc>
int main()
{
enum
{
Order_kumem_pages = 4,
Size_kumem = L4_PAGESIZE << Order_kumem_pages,
};
l4_addr_t kumem;
L4Re::chksys(L4Re::Util::kumem_alloc(&kumem, Order_kumem_pages),
"Allocate KU memory for object information");
unsigned skip_entries = 0;
for (;;)
{
l4_umword_t result_cnt;
l4_umword_t result_all;
L4Re::chksys(l4_debugger_query_obj_infos(L4_BASE_DEBUGGER_CAP,
kumem, Size_kumem, skip_entries,
&result_cnt, &result_all),
"Retrieve kernel object information");
if (skip_entries == 0)
printf("\033[31mGot %lu/%lu entries during first call.\033[m\n",
result_cnt, result_all);
if (result_cnt == 0)
break;
auto const *o = reinterpret_cast<L4_kobj_info *>(kumem);
for (unsigned i = 0; i < result_cnt; ++i, ++o)
{
if (o->type != L4_kobj_info::Mapping::Type)
printf("%llx %llx ", o->id, o->mapping_ptr);
switch (o->type)
{
case L4_kobj_info::Mapping::Type:
printf("%*s%0*lx[C:%x]: space=D:%llx%s%.*s%s rights=%x flags=%x obj=0x%llx",
8, "",
sizeof(l4_umword_t) == 8 ? 16 : 8,
(l4_umword_t)o->mapping.mapping_ptr,
o->mapping.cap_idx,
o->id,
o->mapping.space_name[0] ? "(" : "",
o->mapping.space_name[0] ? int{sizeof(o->mapping.space_name)} : 0,
o->mapping.space_name[0] ? o->mapping.space_name : "",
o->mapping.space_name[0] ? ")" : "",
o->mapping.entry_rights,
o->mapping.entry_flags,
o->mapping.entry_ptr);
break;
case L4_kobj_info::Thread::Type:
printf("[\033[32mThread\033[m]%s C=%u",
o->thread.is_kernel ? " {KERNEL}" : "",
o->thread.home_cpu);
if (o->thread.home_cpu != o->thread.current_cpu)
printf(":%u", o->thread.current_cpu);
if (o->thread.is_kernel_task)
printf(" R=%lld rdy%s",
o->thread.ref_cnt,
o->thread.is_current ? " \033[32mcur\033[m" : "");
else
printf(" S=D:%llx R=%lld%s%s",
o->thread.space_id,
o->thread.ref_cnt,
o->thread.in_ready_list ? " rdy" : "",
o->thread.is_current ? " \033[32mcur\033[m" : "");
break;
case L4_kobj_info::Space::Type:
printf("[\033[31mTask\033[m]%s R=%lld",
o->space.is_kernel ? " {KERNEL}" : "",
o->space.ref_cnt);
break;
case L4_kobj_info::Vm::Type:
printf("Vm:");
break;
case L4_kobj_info::Ipc_gate::Type:
printf("[\033[35mGate\033[m] L=%s%08llx\033[m D=%llx",
o->ipc_gate.label & 3 ? "\033[36;1m" : "",
o->ipc_gate.label,
o->ipc_gate.thread_id);
break;
case L4_kobj_info::Irq_sender::Type:
printf("[\033[37mIRQ ipc\033[m] I=%x %.*s F=%x L=%llx T=%llx Q=%lld",
o->irq_sender.pin,
int{sizeof(o->irq_sender.chip_type)},
o->irq_sender.chip_type,
o->irq_sender.flags,
o->irq_sender.label,
o->irq_sender.target_id,
o->irq_sender.queued);
break;
case L4_kobj_info::Irq_semaphore::Type:
printf("[\033[37mIRQ sem\033[m] I=%x %.*s F=%x Q=%lld",
o->irq_semaphore.pin,
int{sizeof(o->irq_semaphore.chip_type)},
o->irq_semaphore.chip_type,
o->irq_semaphore.flags,
o->irq_semaphore.queued);
break;
case L4_kobj_info::Factory::Type:
printf("\033[33;1mFactory\033[m c=%llu l=%llu",
o->factory.current,
o->factory.limit);
break;
case L4_kobj_info::Jdb::Type:
printf("[Jdb]");
break;
case L4_kobj_info::Scheduler::Type:
printf("[\033[34mSched\033[m]");
break;
case L4_kobj_info::Vlog::Type:
printf("[Vlog]");
break;
case L4_kobj_info::Pfc::Type:
printf("[Icu/Pfc]");
break;
case L4_kobj_info::Dmar_space::Type:
printf("[IOMMU]");
break;
case L4_kobj_info::Iommu::Type:
printf("[IOMMU]");
break;
case L4_kobj_info::Smmu::Type:
printf("[SMMU]");
break;
}
if (o->type != L4_kobj_info::Mapping::Type)
printf(" ref_cnt=%lld", o->ref_cnt);
printf("\n");
}
skip_entries += result_cnt;
}
printf("\nFinished!\n");
return 0;
}

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_ipc1
SRC_C = ipc_example.c
REQUIRES_LIBS = libpthread
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,5 @@
# vim:se ft=lua:
local L4 = require("L4");
L4.default_loader:start({}, "rom/ex_ipc1");

View File

@@ -0,0 +1,114 @@
/**
* \file
* \brief Low-level example of communication.
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* This example shows how two threads can exchange data using the L4 IPC
* mechanism. One thread is sending an integer to the other thread which is
* returning the square of the integer. Both values are printed.
*/
/*
* (c) 2008-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/sys/ipc.h>
#include <pthread-l4.h>
#include <unistd.h>
#include <stdio.h>
static pthread_t t2;
/* Thread1 is the initiator thread, i.e. it initiates the IPC calls. In
* other words, it takes the client role. It uses L4 IPC mechanisms to send
* an integer value to thread2 and received a calculation result back. */
static void *thread1_fn(void *arg)
{
l4_msgtag_t tag;
int ipc_error;
unsigned long value = 1;
(void)arg;
while (1)
{
printf("Sending: %ld\n", value);
/* Store the value which we want to have squared in the first message
* register of our UTCB. */
l4_utcb_mr()->mr[0] = value;
/* To an L4 IPC call, i.e. send a message to thread2 and wait for a
* reply from thread2. The '1' in the msgtag denotes that we want to
* transfer one word of our message registers (i.e. MR0). No timeout. */
tag = l4_ipc_call(pthread_l4_cap(t2), l4_utcb(),
l4_msgtag(0, 1, 0, 0), L4_IPC_NEVER);
/* Check for IPC error, if yes, print out the IPC error code, if not,
* print the received result. */
ipc_error = l4_ipc_error(tag, l4_utcb());
if (ipc_error)
fprintf(stderr, "thread1: IPC error: %x\n", ipc_error);
else
printf("Received: %ld\n", l4_utcb_mr()->mr[0]);
/* Wait some time and increment our value. */
sleep(1);
value++;
}
return NULL;
}
/* Thread2 is in the server role, i.e. it waits for requests from others and
* sends back the calculation results. */
static void *thread2_fn(void *arg)
{
l4_msgtag_t tag;
l4_umword_t label;
int ipc_error;
(void)arg;
/* Wait for requests from any thread. No timeout, i.e. wait forever. */
tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
while (1)
{
/* Check if we had any IPC failure, if yes, print the error code
* and just wait again. */
ipc_error = l4_ipc_error(tag, l4_utcb());
if (ipc_error)
{
fprintf(stderr, "thread2: IPC error: %x\n", ipc_error);
tag = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
continue;
}
/* So, the IPC was ok, now take the value out of message register 0
* of the UTCB and store the square of it back to it. */
l4_utcb_mr()->mr[0] = l4_utcb_mr()->mr[0] * l4_utcb_mr()->mr[0];
/* Send the reply and wait again for new messages.
* The '1' in the msgtag indicated that we want to transfer 1 word in
* the message registers (i.e. MR0) */
tag = l4_ipc_reply_and_wait(l4_utcb(), l4_msgtag(0, 1, 0, 0),
&label, L4_IPC_NEVER);
}
return NULL;
}
int main(void)
{
// We will have two threads, one is already running the main function, the
// other (thread2) will be created using pthread_create.
if (pthread_create(&t2, NULL, thread2_fn, NULL))
{
fprintf(stderr, "Thread creation failed\n");
return 1;
}
// Just run thread1 in the main thread
thread1_fn(NULL);
return 0;
}

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_isr
SRC_C = main.c
REQUIRES_LIBS = l4re_c-util
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,147 @@
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Björn Döbel <doebel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/*
* This example shall show how to connect to an interrupt, receive interrupt
* events and detach again. As the interrupt source we'll use the virtual
* key interrupt -- pin 0 of the log capability.
*/
#include <l4/re/c/util/cap_alloc.h>
#include <l4/re/c/namespace.h>
#include <l4/sys/factory.h>
#include <l4/sys/icu.h>
#include <l4/sys/irq.h>
#include <l4/sys/vcon.h>
#include <l4/sys/utcb.h>
#include <stdio.h>
int main(void)
{
int const irqno = 0;
l4_cap_idx_t irqcap, icucap;
l4_vcon_attr_t attr;
long err;
icucap = l4re_env()->log;
/* Get a free capability slot for the ICU capability. */
if (l4_is_invalid_cap(icucap))
{
printf("Did not find the Vlog ICU.\n");
return 1;
}
/* Get another free capability slot for the corresponding IRQ object. */
if (l4_is_invalid_cap(irqcap = l4re_util_cap_alloc()))
{
printf("Cannot allocate capability slot.\n");
return 1;
}
/* Create IRQ object. */
if ((err = l4_error(l4_factory_create_irq(l4re_env()->factory, irqcap))))
{
printf("Could not create IRQ object: %ld (%s).\n", err, l4sys_errtostr(err));
return 1;
}
/*
* Bind the recently allocated IRQ object to the IRQ number irqno as provided
* by the ICU.
*/
if ((err = l4_error(l4_icu_bind(icucap, irqno, irqcap))))
{
printf("Could not bind IRQ%d to ICU: %ld (%s).\n", irqno, err, l4sys_errtostr(err));
return 1;
}
if ((err = l4_error(l4_vcon_get_attr(icucap, &attr))))
{
printf("Could not get Vcon attributes: %ld (%s).\n", err, l4sys_errtostr(err));
return 1;
}
/* Disable echo at Vcon console. */
attr.l_flags &= ~L4_VCON_ECHO;
if ((err = l4_error(l4_vcon_set_attr(icucap, &attr))))
{
printf("Could not set Vcon attributes: %ld (%s).\n", err, l4sys_errtostr(err));
return 1;
}
printf("Vcon echo disabled.\n");
/* Bind ourselves to the IRQ. Define the IPC label which is sent if an IRQ
* IPC arrives. */
if ((err = l4_error(l4_rcv_ep_bind_thread(irqcap, l4re_env()->main_thread, 0x1234))))
{
printf("Could not bind to IRQ%d: %ld (%s).\n", irqno, err, l4sys_errtostr(err));
return 1;
}
printf("Attached to key IRQ %d.\nPress keys now, Shift-Q to exit.\n", irqno);
/* IRQ receive loop. */
while (1)
{
/* Wait for the interrupt to happen. If we received an IRQ, the label
* return code is set to 0. If we didn't receive an IRQ, the error flag
* in the message tag is set and l4_error() reads the IPC error code from
* the UTCB. */
l4_umword_t label;
if ((err = l4_error(l4_irq_wait(irqcap, &label, L4_IPC_NEVER))))
printf("Could not receive IRQ: %ld (%s).\n", err, l4sys_errtostr(err));
else
{
char buf[128];
int n;
if (label != 0x1234)
{
printf("Unexpected label %0lx -- ignoring interrupt.\n", label);
continue;
}
/* Process the interrupt -- may do a 'break' */
printf("Got IRQ with expected label 0x%lX.\n", label);
n = l4_vcon_read(icucap, buf, sizeof(buf));
if (n < 0)
printf("Could not read from Vcon interface: %d (%s).\n", n, l4sys_errtostr(n));
else
{
unsigned i;
int terminate = 0;
for (i = 0; i < (unsigned)n && i < sizeof(buf); ++i)
{
int c = (unsigned char)buf[i];
if (c >= 32 && c < 128) // Filter UTF-8 encodings.
printf("Got key '%c'.\n", c);
else
printf("Got keycode %d.\n", c);
if (buf[i] == 'Q')
terminate = 1;
}
if (terminate)
break;
}
}
}
/* We're done, detach from the interrupt. */
if ((err = l4_error(l4_irq_detach(irqcap))))
printf("Could not detach from IRQ: %ld (%s).\n", err, l4sys_errtostr(err));
printf("Application terminated.\n");
return 0;
}

View File

@@ -0,0 +1,8 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_map_irq_client ex_map_irq_server
SRC_CC_ex_map_irq_client = client.cc
SRC_CC_ex_map_irq_server = server.cc
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1 @@
Example showcasing how to map a cap from a client to a server.

View File

@@ -0,0 +1,81 @@
/*
* Example for mapping a capability from a client to a server - client part.
*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is licensed under the terms of the GNU General Public License 2.
* See file COPYING-GPL-2 for details.
*/
#include <l4/sys/factory>
#include <l4/sys/irq>
#include <l4/sys/ipc_gate>
#include <l4/cxx/ipc_stream>
#include <l4/re/env>
#include <l4/re/error_helper>
#include <l4/re/util/cap_alloc>
#include <cstdio>
#include "shared.h"
enum { Nr_of_triggers = 3 };
static int run()
{
using L4Re::chksys;
using L4Re::chkcap;
printf("Hello from ex_map_irq_client.\n");
// allocate cap for IRQ
L4::Cap<L4::Irq> irq = chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(),
"could not find a free cap slot");
// create IRQ kernel object
chksys(L4Re::Env::env()->factory()->create(irq),
"create a new IRQ kernel object");
// look out for server
L4::Cap<Irq_source> server;
server = chkcap(L4Re::Env::env()->get_cap<Irq_source>("ex_map_irq"),
"get 'ex_map_irq' capability");
// map irq to server
printf("Mapping IRQ cap to server.\n");
chksys(server->map_irq(irq), "map irq");
// bind to IRQ and wait for the server to trigger it
chksys(irq->bind_thread(L4Re::Env::env()->main_thread(), 0),
"bind to IRQ");
// tell the server to start triggering us and how many times it should
// trigger the IRQ
chksys(server->start(Nr_of_triggers), "starting triggers");
for (int i = 0; i < Nr_of_triggers; ++i)
{
chksys(irq->receive(), "receiving IRQ");
printf("Received IRQ.\n");
}
printf("ex_map_irq_client finished.\n");
return 0;
}
int main()
{
try
{
return run();
}
catch (L4::Runtime_error &e)
{
printf("Runtime error: %s.\n", e.str());
}
catch (...)
{
printf("Uncaught error.\n");
}
return 1;
}

View File

@@ -0,0 +1,32 @@
-- (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
-- Licensed under the terms of the GNU General Public License 2.
-- See file COPYING-GPL-2 for details.
-- Example config for map_irq
local L4 = require("L4");
-- create a shortcut for L4.default_loader
local l = L4.default_loader;
-- create a communication channel
local channel = l:new_channel();
-- start the server
l:start({
caps = {
-- Give the channel (an IPC gate) to the server
-- The "svr()" directive instructs the loader
-- to supply the capability with server permissions
ex_map_irq = channel:svr()
},
-- configure log output: tag = server, color = magenta
log = { "server", "m" },
}, "rom/ex_map_irq_server");
-- start the client
l:start({
caps = {
ex_map_irq = channel -- give the channel to the client
},
log = {"client", "y"},
}, "rom/ex_map_irq_client");

View File

@@ -0,0 +1,13 @@
# (c) Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
# Licensed under the terms of the GNU General Public License 2.
# See file COPYING-GPL-2 for details.
#
# Config for loading the ex_map_irq example
entry ex_map_irq
roottask moe rom/ex_map_irq.cfg
module ned
module l4re
module ex_map_irq.cfg
module ex_map_irq_server
module ex_map_irq_client

View File

@@ -0,0 +1,148 @@
/*
* Example for mapping a capability from a client to a server - server part.
*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is licensed under the terms of the GNU General Public License 2.
* See file COPYING-GPL-2 for details.
*/
#include <l4/re/error_helper>
#include <l4/sys/cxx/ipc_epiface>
#include <l4/sys/irq>
#include <l4/re/env>
#include <l4/sys/factory>
#include <l4/re/util/object_registry>
#include <l4/re/util/br_manager>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <stdlib.h>
#include "shared.h"
// Those are error helpers that simplify our code by hiding error checking.
// Errors flagged through C++ exceptions.
using L4Re::chksys;
using L4Re::chkcap;
// This class reacts on notifications from Server
class Trigger : public L4::Irqep_t<Trigger>
{
public:
void handle_irq()
{
for (unsigned i = 0; i < _nrs; ++i)
{
printf("Triggering IRQ.\n");
_irq->trigger();
sleep(1);
}
printf("ex_map_irq_server finished.\n");
exit(0);
}
void num_triggers(unsigned num);
void irq(L4::Cap<L4::Irq> irq);
void trigger() { _irq->trigger(); }
private:
L4::Cap<L4::Irq> _irq;
unsigned _nrs;
};
void Trigger::num_triggers(unsigned num)
{
_nrs = num;
}
void Trigger::irq(L4::Cap<L4::Irq> irq)
{
_irq = irq;
}
// This class handles the reception of the IRQ cap from the client.
class Server : public L4::Epiface_t<Server, Irq_source>
{
public:
Server(L4::Cap<L4::Irq> irq, Trigger *trigger)
: _trigger_notification(irq), _trigger(trigger)
{}
int op_map_irq(Irq_source::Rights, L4::Ipc::Snd_fpage const &irq);
int op_start(Irq_source::Rights, unsigned nrs);
private:
void do_client_trigger(unsigned);
L4::Cap<L4::Irq> _trigger_notification;
Trigger *_trigger;
};
int
Server::op_map_irq(Irq_source::Rights, L4::Ipc::Snd_fpage const &irq)
{
if (!irq.cap_received())
return -L4_EINVAL;
// We use rcv_cap for receiving only.
// After receiving the cap, we tell the kernel to associate
// the kernel object that rcv_cap points to with irq.
// allocate new cap
_trigger->irq(L4Re::chkcap(server_iface()->rcv_cap<L4::Irq>(0),
"reading receive capability 0"));
L4Re::chksys(server_iface()->realloc_rcv_cap(0),
"allocating new receive capability");
printf("Received IRQ from client.\n");
return L4_EOK;
}
int
Server::op_start(Irq_source::Rights, unsigned nrs)
{
_trigger->num_triggers(nrs);
_trigger_notification->trigger();
return L4_EOK;
}
static L4Re::Util::Registry_server<L4Re::Util::Br_manager_hooks> server;
static int run()
{
printf("Hello from ex_map_irq_server.\n");
Trigger trigger;
L4::Cap<L4::Irq> notification_irq;
notification_irq = chkcap(server.registry()->register_irq_obj(&trigger),
"could not register notification trigger");
Server map_irq_srv(notification_irq, &trigger);
L4Re::chkcap(server.registry()->register_obj(&map_irq_srv, "ex_map_irq"),
"could not register service side");
server.loop();
return 0;
}
int main()
{
try
{
return run();
}
catch (L4::Runtime_error &e)
{
printf("Runtime error: %s.\n", e.str());
}
catch (...)
{
printf("Uncaught error.\n");
}
return 1;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <l4/sys/capability>
#include <l4/sys/cxx/ipc_iface>
#include <l4/sys/cxx/ipc_types>
struct Irq_source :
L4::Kobject_t<Irq_source, L4::Kobject, 0x77, L4::Type_info::Demand_t<1> >
{
L4_INLINE_RPC(int, map_irq, (L4::Ipc::Cap<L4::Irq> irq));
L4_INLINE_RPC(int, start, (unsigned nrs));
typedef L4::Typeid::Rpcs<map_irq_t, start_t> Rpcs;
};

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_thread_migrate ex_thread_migrate_irq
SRC_CC_ex_thread_migrate = thread_migrate.cc
SRC_CC_ex_thread_migrate_irq = thread_migrate_irq.cc
REQUIRES_LIBS = libpthread
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,132 @@
/**
* \file
* \brief Example of thread migration.
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
* This example shows how to migrate threads on CPUs. First, it queries the
* available CPUs, creates a couple of threads, and then shuffles the
* threads on the available CPUs.
*/
/*
* (c) 2008-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/sys/scheduler>
#include <l4/re/env>
#include <l4/re/util/cap_alloc>
#include <pthread-l4.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
enum { NR_THREADS = 12 };
static L4::Cap<L4::Thread> threads[NR_THREADS];
static l4_umword_t cpu_map, cpu_nrs;
/* Function for the threads. The content is not really relevant, so lets
* just sleep around a bit. */
static void *thread_fn(void *)
{
while (1)
sleep(1);
return 0;
}
/* Check how many CPUs we have available.
*/
static int check_cpus(void)
{
l4_sched_cpu_set_t cs = l4_sched_cpu_set(0, 0);
if (l4_error(L4Re::Env::env()->scheduler()->info(&cpu_nrs, &cs)) < 0)
return 1;
cpu_map = cs.map;
printf("%ld maximal supported CPUs.\n", cpu_nrs);
if (cpu_nrs >= L4_MWORD_BITS)
{
printf("Will only handle %ld CPUs.\n", cpu_nrs);
cpu_nrs = L4_MWORD_BITS;
}
else if (cpu_nrs == 1)
printf("Only found 1 CPU.\n");
return cpu_nrs < 2;
}
/* Create a couple of threads and store their capabilities in an array */
static int create_threads(void)
{
unsigned i;
for (i = 0; i < NR_THREADS; ++i)
{
pthread_t t;
if (pthread_create(&t, NULL, thread_fn, NULL))
return 1;
threads[i] = L4::Cap<L4::Thread>(pthread_l4_cap(t));
}
printf("Created %d threads.\n", NR_THREADS);
return 0;
}
/* Helper function to get the next CPU */
static unsigned get_next_cpu(unsigned c)
{
unsigned x = c;
for (;;)
{
x = (x + 1) % cpu_nrs;
if (L4Re::Env::env()->scheduler()->is_online(x))
return x;
if (x == c)
return c;
}
}
/* Function that shuffles the threads on the available CPUs */
static void shuffle(void)
{
unsigned start = 0;
while (1)
{
unsigned t;
unsigned c = start;
for (t = 0; t < NR_THREADS; ++t)
{
l4_sched_param_t sp = l4_sched_param(20);
c = get_next_cpu(c);
sp.affinity = l4_sched_cpu_set(c, 0);
if (l4_error(L4Re::Env::env()->scheduler()->run_thread(threads[t], sp)))
printf("Error migrating thread%02d to CPU%02d\n", t, c);
printf("Migrated Thread%02d -> CPU%02d\n", t, c);
}
start++;
if (start == cpu_nrs)
start = 0;
sleep(1);
}
}
int main(void)
{
if (check_cpus())
return 1;
if (create_threads())
return 1;
shuffle();
return 0;
}

View File

@@ -0,0 +1,7 @@
-- vim:set ft=lua:
local L4 = require("L4");
-- The log prefix will be 'migrate', colored green.
L4.default_loader:start({ log = { "migrate", "green" } },
"rom/ex_thread_migrate");

View File

@@ -0,0 +1,209 @@
/**
* \file
* \brief Example of thread migration.
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* Matthias Lange <matthias.lange@kernkonzept.com>
*
* This example shows how to migrate threads on CPUs. First, it queries the
* available CPUs, creates a couple of threads, and then shuffles the
* threads on the available CPUs.
*/
/*
* (c) 2008-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/sys/scheduler>
#include <l4/sys/cxx/ipc_epiface>
#include <l4/re/env>
#include <l4/re/util/cap_alloc>
#include <l4/re/util/object_registry>
#include <l4/re/util/br_manager>
#include <l4/re/error_helper>
#include <l4/cxx/exceptions>
#include <l4/cxx/unique_ptr>
#include <pthread-l4.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/* Function for the threads. The content is not really relevant, so lets
* just sleep around a bit. */
static void *thread_fn(void *)
{
while (1)
sleep(1);
return 0;
}
class Migrate_server : public L4::Ipc_svr::Timeout_queue::Timeout,
public L4::Irqep_t<Migrate_server>
{
private:
enum { NR_THREADS = 12 };
public:
Migrate_server(L4::Ipc_svr::Server_iface *sif, unsigned interval_us)
: _sif(sif), _interval(interval_us), _start_cpu(0),
_current_set(l4_sched_cpu_set(0, 0))
{
check_cpus();
create_threads();
_sif->add_timeout(this, l4_kip_clock(l4re_kip()) + _interval);
}
void expired()
{
unsigned c = _start_cpu;
for (unsigned t = 0; t < NR_THREADS; ++t)
{
c = get_next_cpu(c);
l4_sched_param_t sp = l4_sched_param(20);
sp.affinity = l4_sched_cpu_set(c, 0);
L4Re::chksys(L4Re::Env::env()->scheduler()->run_thread(threads[t], sp),
"Migrating thread");
printf("Migrated Thread%02d -> CPU%02d\n", t, c);
}
_start_cpu++;
if (_start_cpu == _cpu_nrs)
_start_cpu = 0;
// migrate in _interval ms again
_sif->add_timeout(this, timeout() + _interval);
}
void handle_irq()
{
l4_umword_t cpu_nrs;
l4_sched_cpu_set_t cs = l4_sched_cpu_set(0, 0);
L4Re::chksys(L4Re::Env::env()->scheduler()->info(&cpu_nrs, &cs),
"Get scheduler info");
// the maximal number of supported CPUs does not change dynamically
if (cpu_nrs != _cpu_nrs)
printf("Warning: maximum number of CPUs has changed.\n"
" Ignoring excess CPUs.\n");
// see what has changed
l4_umword_t old = _current_set.map;
l4_umword_t n = cs.map;
for (unsigned i = 0; i < L4_MWORD_BITS; ++i)
{
// old online CPU now offline
if ((old & 1UL) && !(n & 1UL))
printf("CPU%02u went offline\n", i);
// offline CPU now online
if (!(old & 1UL) && (n & 1UL))
printf("CPU%02u came online\n", i);
old >>= 1UL;
n >>= 1UL;
if (!(old | n))
break;
}
// update internal data structure
_current_set.map = cs.map;
}
private:
L4::Ipc_svr::Server_iface *_sif;
unsigned _interval;
unsigned _start_cpu;
L4::Cap<L4::Thread> threads[NR_THREADS];
l4_umword_t _cpu_nrs;
l4_sched_cpu_set_t _current_set;
void check_cpus()
{
L4Re::chksys(L4Re::Env::env()->scheduler()->info(&_cpu_nrs, &_current_set),
"Get scheduler info");
printf("CPU map 0x%lx, gran=%u, offset=%u\n", _current_set.map,
_current_set.granularity(),
_current_set.offset());
printf("%ld maximum number of supported CPUs.\n", _cpu_nrs);
if (_cpu_nrs >= L4_MWORD_BITS)
{
printf("Will only handle %ld CPUs.\n", _cpu_nrs);
_cpu_nrs = L4_MWORD_BITS;
}
else if (_cpu_nrs == 1)
throw L4::Runtime_error(-L4_EINVAL, "Only found 1 CPU.");
}
/* Create a couple of threads and store their capabilities in an array */
void create_threads()
{
for (unsigned i = 0; i < NR_THREADS; ++i)
{
pthread_t t;
if (pthread_create(&t, NULL, thread_fn, NULL))
throw L4::Runtime_error(-L4_ENOSYS, "Could not create thread.");
threads[i] = L4::Cap<L4::Thread>(pthread_l4_cap(t));
}
printf("Created %d threads.\n", NR_THREADS);
}
/* Helper function to get the next CPU */
unsigned get_next_cpu(unsigned c)
{
unsigned x = c;
for (;;)
{
x = (x + 1) % _cpu_nrs;
// test if CPU x is online
if ((1UL << x) & _current_set.map)
return x;
if (x == c)
return c;
}
}
};
static L4Re::Util::Registry_server<L4Re::Util::Br_manager_timeout_hooks> server;
int main(void)
{
try
{
// create a migration server object that migrates threads every second
cxx::unique_ptr<Migrate_server> svr =
cxx::make_unique<Migrate_server>(&server, 1000000);
L4::Cap<L4::Irq> _sched_irq =
server.registry()->register_irq_obj(svr.get());
// bind our irq object to the Icu of the scheduler object
L4Re::chksys(L4Re::Env::env()->scheduler()->bind(0, _sched_irq),
"Bind scheduler irq");
L4Re::Env::env()->scheduler()->unmask(1);
svr.release();
server.loop();
}
catch (L4::Runtime_error &e)
{
printf("Runtime error: %s. Reason: %s.\n", e.str(), e.extra_str());
}
return 0;
}

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_singlestep
SYSTEMS = x86-l4f amd64-l4f
SRC_C = main.c
REQUIRES_LIBS = l4re_c-util
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,158 @@
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Björn Döbel <doebel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/*
* Single stepping example for the x86-32 architecture.
*/
#include <l4/sys/ipc.h>
#include <l4/sys/factory.h>
#include <l4/sys/thread.h>
#include <l4/sys/utcb.h>
#include <l4/sys/kdebug.h>
#include <l4/util/util.h>
#include <l4/re/env.h>
#include <l4/re/c/util/cap_alloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static char thread_stack[8 << 10];
static void thread_func(void)
{
while (1)
{
unsigned long d = 0;
/* Enable single stepping */
asm volatile("pushf; pop %0; or $256,%0; push %0; popf\n"
: "=r" (d) : "r" (d));
/* Some instructions */
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
asm volatile("mov $0x12345000, %%edx" : : : "edx"); // a non-existent cap
asm volatile("int $0x30\n");
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
/* Disabled single stepping */
asm volatile("pushf; pop %0; and $~256,%0; push %0; popf\n"
: "=r" (d) : "r" (d));
/* You won't see those */
asm volatile("nop");
asm volatile("nop");
asm volatile("nop");
}
}
int main(void)
{
l4_msgtag_t tag;
int ipc_stat = 0;
l4_cap_idx_t th = l4re_util_cap_alloc();
l4_exc_regs_t exc;
l4_umword_t mr0, mr1;
l4_utcb_t *u = l4_utcb();
printf("Singlestep testing\n");
if (l4_is_invalid_cap(th))
return 1;
l4_touch_rw(thread_stack, sizeof(thread_stack));
l4_touch_ro(thread_func, 1);
tag = l4_factory_create_thread(l4re_env()->factory, th);
if (l4_error(tag))
return 1;
l4_thread_control_start();
l4_thread_control_pager(l4re_env()->main_thread);
l4_thread_control_exc_handler(l4re_env()->main_thread);
l4_thread_control_bind((l4_utcb_t *)l4re_env()->first_free_utcb,
L4RE_THIS_TASK_CAP);
l4_thread_control_alien(1);
tag = l4_thread_control_commit(th);
if (l4_error(tag))
return 2;
tag = l4_thread_ex_regs(th, (l4_umword_t)thread_func,
(l4_umword_t)thread_stack + sizeof(thread_stack),
0);
if (l4_error(tag))
return 3;
l4_sched_param_t sp = l4_sched_param(1, 0);
tag = l4_scheduler_run_thread(l4re_env()->scheduler, th, &sp);
if (l4_error(tag))
return 4;
/* Pager/Exception loop */
if (l4_msgtag_has_error(tag = l4_ipc_receive(th, u, L4_IPC_NEVER)))
{
printf("l4_ipc_receive failed");
return 5;
}
memcpy(&exc, l4_utcb_exc(), sizeof(exc));
mr0 = l4_utcb_mr()->mr[0];
mr1 = l4_utcb_mr()->mr[1];
for (;;)
{
if (l4_msgtag_is_exception(tag))
{
printf("PC = %08lx Trap = %08lx Err = %08lx, SP = %08lx SC-Nr: %lx\n",
l4_utcb_exc_pc(&exc), exc.trapno, exc.err,
exc.sp, exc.err >> 3);
if (exc.err >> 3)
{
if (!(exc.err & 4))
{
tag = l4_msgtag(L4_PROTO_ALLOW_SYSCALL,
L4_UTCB_EXCEPTION_REGS_SIZE, 0, 0);
if (ipc_stat)
enter_kdebug("Should not be 1");
}
else
{
tag = l4_msgtag(L4_PROTO_NONE,
L4_UTCB_EXCEPTION_REGS_SIZE, 0, 0);
if (!ipc_stat)
enter_kdebug("Should not be 0");
}
ipc_stat = !ipc_stat;
}
l4_sleep(100);
}
else
printf("Umm, non-handled request: %ld, %08lx %08lx\n",
l4_msgtag_label(tag), mr0, mr1);
memcpy(l4_utcb_exc(), &exc, sizeof(exc));
/* Reply and wait */
if (l4_msgtag_has_error(tag = l4_ipc_call(th, u, tag, L4_IPC_NEVER)))
{
printf("l4_ipc_call failed\n");
return 5;
}
memcpy(&exc, l4_utcb_exc(), sizeof(exc));
mr0 = l4_utcb_mr()->mr[0];
mr1 = l4_utcb_mr()->mr[1];
}
return 0;
}

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_start-with-exc
SYSTEMS = x86-l4f arm-l4f arm64-l4f
SRC_C = main.c
REQUIRES_LIBS = l4re_c-util
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,179 @@
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Björn Döbel <doebel@os.inf.tu-dresden.de>,
* Frank Mehnert <fm3@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/*
* Start a thread with an exception reply. This example does only work on
* the x86-32 and ARM architectures.
*/
#include <l4/sys/thread.h>
#include <l4/sys/factory.h>
#include <l4/sys/ipc.h>
#include <l4/sys/utcb.h>
#include <l4/util/util.h>
#include <l4/re/env.h>
#include <l4/re/c/util/cap_alloc.h>
#include <stdlib.h>
#include <stdio.h>
/* Stack for the thread to be created. 8kB are enough. */
static char thread_stack[8 << 10];
/* The thread to be created. For illustration it will print out its
* register set.
*/
static void L4_STICKY(thread_func(l4_umword_t *d))
{
while (1)
{
printf("hey, I'm a thread\n");
printf("got register values: %ld %ld %ld %ld %ld %ld %ld\n",
d[7], d[6], d[5], d[4], d[2], d[1], d[0]);
l4_sleep(800);
}
}
/* Startup trick for this example. Put all the CPU registers on the stack so
* that the C function above can get it on the stack. */
asm(
".global thread \n"
"thread: \n"
#ifdef ARCH_x86
" pusha \n"
" push %esp \n"
" call thread_func \n"
#endif
#ifdef ARCH_arm
" push {r0-r7} \n"
" mov r0, sp \n"
" bl thread_func \n"
#endif
#ifdef ARCH_arm64
" stp x0, x1, [sp, #0]! \n"
" stp x2, x3, [sp, #0]! \n"
" stp x4, x5, [sp, #0]! \n"
" stp x6, x7, [sp, #0]! \n"
" mov x0, sp \n"
" bl thread_func \n"
#endif
);
extern void thread(void);
/* Our main function */
int main(void)
{
/* Get a capability slot for our new thread. */
l4_cap_idx_t t1 = l4re_util_cap_alloc();
l4_utcb_t *u = l4_utcb();
l4_exc_regs_t *e = l4_utcb_exc_u(u);
l4_msgtag_t tag;
int err;
printf("Example showing how to start a thread with an exception.\n");
/* We do not want to implement a pager here, take the shortcut. */
printf("Make sure to start this program with ldr-flags=eager_map\n");
if (l4_is_invalid_cap(t1))
return 1;
/* Create the thread using our default factory */
tag = l4_factory_create_thread(l4re_env()->factory, t1);
if (l4_error(tag))
return 1;
/* Setup the thread by setting the pager and task. */
l4_thread_control_start();
l4_thread_control_pager(l4re_env()->main_thread);
l4_thread_control_exc_handler(l4re_env()->main_thread);
l4_thread_control_bind((l4_utcb_t *)l4re_env()->first_free_utcb,
L4RE_THIS_TASK_CAP);
tag = l4_thread_control_commit(t1);
if (l4_error(tag))
return 2;
/* Start the thread by finally setting instruction and stack pointer */
tag = l4_thread_ex_regs(t1,
(l4_umword_t)thread,
(l4_umword_t)thread_stack + sizeof(thread_stack),
L4_THREAD_EX_REGS_TRIGGER_EXCEPTION);
if (l4_error(tag))
return 3;
l4_sched_param_t sp = l4_sched_param(1, 0);
tag = l4_scheduler_run_thread(l4re_env()->scheduler, t1, &sp);
if (l4_error(tag))
return 4;
/* Receive initial exception from just started thread */
tag = l4_ipc_receive(t1, u, L4_IPC_NEVER);
if ((err = l4_ipc_error(tag, u)))
{
printf("Umm, ipc error: %x\n", err);
return 1;
}
/* We expect an exception IPC */
if (!l4_msgtag_is_exception(tag))
{
printf("PF?: %lx %lx (not prepared to handle this) %ld\n",
l4_utcb_mr_u(u)->mr[0], l4_utcb_mr_u(u)->mr[1], l4_msgtag_label(tag));
return 1;
}
/* Fill out the complete register set of the new thread */
e->sp = (l4_umword_t)(thread_stack + sizeof(thread_stack));
#ifdef ARCH_x86
e->ip = (l4_umword_t)thread;
e->edi = 0;
e->esi = 1;
e->ebp = 2;
e->ebx = 4;
e->edx = 5;
e->ecx = 6;
e->eax = 7;
#endif
#ifdef ARCH_arm
e->pc = (l4_umword_t)thread;
e->r[0] = 0;
e->r[1] = 1;
e->r[2] = 2;
e->r[3] = 3;
e->r[4] = 4;
e->r[5] = 5;
e->r[6] = 6;
e->r[7] = 7;
#endif
#ifdef ARCH_arm64
e->pc = (l4_umword_t)thread;
e->r[0] = 0;
e->r[1] = 1;
e->r[2] = 2;
e->r[3] = 3;
e->r[4] = 4;
e->r[5] = 5;
e->r[6] = 6;
e->r[7] = 7;
#endif
/* Send a complete exception */
tag = l4_msgtag(0, L4_UTCB_EXCEPTION_REGS_SIZE, 0, 0);
/* Send reply and start the thread with the defined CPU register set */
tag = l4_ipc_send(t1, u, tag, L4_IPC_NEVER);
if ((err = l4_ipc_error(tag, u)))
printf("Error sending IPC: %x\n", err);
/* Idle around */
while (1)
l4_sleep(10000);
return 0;
}

View File

@@ -0,0 +1,7 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_timeouts
SRC_C = main.c
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,109 @@
/* SPDX-License-Identifier: GPL-2.0-only OR License-Ref-kk-custom */
/*
* Copyright (C) 2023, 2021 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*/
#include <l4/sys/types.h>
#include <cstdio>
/**
* Same function as l4_timeout_from_us() with subtracting `e = log2(us) - 7`.
*/
static l4_timeout_s custom_l4_timeout_from_us_man7(l4_uint32_t us)
{
l4_timeout_s t;
unsigned m;
int e = (31 - __builtin_clz(us)) - 7;
if (e < 0) e = 0;
m = us >> e;
t.t = (e << 10) | m;
return t;
}
/**
* Same function as l4_timeout_from_us() with subtracting `e = log2(us) - 9`.
*/
static l4_timeout_s custom_l4_timeout_from_us_man9(l4_uint32_t us)
{
l4_timeout_s t;
unsigned m;
int e = (31 - __builtin_clz(us)) - 9;
if (e < 0) e = 0;
m = us >> e;
t.t = (e << 10) | m;
return t;
}
static void deviation(l4_uint32_t us, l4_uint32_t d,
unsigned *value, unsigned *order, bool verbose = false)
{
unsigned v, o;
if (d == 0)
{
*value = 0;
*order = 0;
return;
}
for (v = us; d < 10000; d *= 10, v *= 10)
if (verbose)
printf("1: d=%d, v=%d\n", d, v);
for (o = 0; (d*10 / v) < 99; v /= 10, ++o)
if (verbose)
printf("2: v=%d, o=%d\n", v, o);
*value = d / v;
*order = o - 1;
if (verbose)
printf("%d / %d\n", d / v, o - 1);
}
static void enumerate_timeouts()
{
printf("Enumerating all value timeout values...\n");
// 0xffffffff == L4_TIMEOUT_NEVER, see l4_timeout_from_us().
for (l4_uint32_t us = 1; us < ~0U; ++us)
{
if ((us % 50000000) == 1)
printf(" Finished %llu%%\n",
((l4_uint64_t)us * 100) / ((1ULL << 32) - 1));
l4_timeout_s to1 = custom_l4_timeout_from_us_man7(us);
l4_uint32_t res1 = l4_timeout_rel_get(to1);
l4_timeout_s to2 = custom_l4_timeout_from_us_man9(us);
l4_uint32_t res2 = l4_timeout_rel_get(to2);
// The resulting time periods in microseconds are always smaller than or
// equal to the original time period. The bigger the result, the closer
// to the original time period.
if ( res2 < res1 // custom_l4_timeout_from_us_man9() worse than
// custom_l4_timeout_from_us_man7()
|| res1 > us // calculated timeout unexpectedly bigger
|| res2 > us) // calculated timeout unexpectedly bigger
{
// Unexpected: Show the results for both ways.
unsigned v1, o1;
deviation(us, us - res1, &v1, &o1);
unsigned v2, o2;
deviation(us, us - res2, &v2, &o2);
printf("%8d: res1: 0x%04x e=%d => %8d; res2: 0x%04x e=%d => %8d",
us,
to1.t, (to1.t & 0x7c00) >> 10, res1,
to2.t, (to2.t & 0x7c00) >> 10, res2);
if (v1 || v2)
{
if (!v1)
printf(" ");
else
printf(" %d.%u-E%u", v1 / 10, v1 % 10, o1);
if (v2)
printf(" vs %d.%u-E%u", v2 / 10, v2 % 10, o2);
}
printf("\n");
}
}
printf("Done!\n");
}
int main()
{
enumerate_timeouts();
return 0;
}

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_uirq
SRC_CC = ex_uirq.cc
REQUIRES_LIBS = libstdc++ libpthread
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,67 @@
/*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is licensed under the terms of the GNU General Public License 2.
* See file COPYING-GPL-2 for details.
*/
/*
* This example shows how to create a user IRQ and how to receive and trigger
* them. Triggering and receiving is done in different threads, created with
* C++11's std::thread.
*/
#include <l4/re/env>
#include <l4/re/error_helper>
#include <l4/re/util/cap_alloc>
#include <l4/sys/irq>
#include <l4/sys/factory>
#include <pthread-l4.h>
#include <thread>
#include <cstdio>
#include <unistd.h>
int main()
{
try
{
printf("IRQ triggering example.\n");
L4::Cap<L4::Irq> irq;
// Allocate a capability to use for the IRQ object
irq = L4Re::chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>());
// Create the IRQ object with the capability, using our default
// factory
L4Re::chksys(L4Re::Env::env()->factory()->create(irq),
"Failed to create IRQ.");
// bind to ourselves to the IRQ to receive its triggers
L4Re::chksys(irq->bind_thread(Pthread::L4::cap(pthread_self()), 0),
"Could not bind to IRQ.");
// Create a thread and also tell it about our IRQ capability
std::thread thread = std::thread([irq](){ irq->trigger(); });
L4Re::chksys(irq->receive(), "Receive failed.");
printf("Received irq!\n");
// 'Wait' for thread to finish
thread.join();
printf("Example finished.\n");
return 0;
}
catch (L4::Runtime_error &e)
{
fprintf(stderr, "Runtime error: %s.\n", e.str());
}
catch (...)
{
fprintf(stderr, "Unknown exception.\n");
}
return 1;
}

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_utcb_ipc
REQUIRES_LIBS = l4re_c-util
DEPENDS_PKGS = $(REQUIRES_LIBS)
SRC_C = main.c
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,176 @@
/**
* \file
* \brief Low-level example of communication.
*
* This example shows how two threads can exchange data using the L4 IPC
* mechanism.
*/
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Björn Döbel <doebel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/sys/ipc.h>
#include <l4/sys/thread.h>
#include <l4/sys/factory.h>
#include <l4/sys/utcb.h>
#include <l4/sys/task.h>
#include <l4/sys/vcon.h>
#include <l4/re/env.h>
#include <l4/re/c/util/cap_alloc.h>
#include <l4/re/c/util/kumem_alloc.h>
#include <l4/util/thread.h>
#include <stdio.h>
#include <string.h>
static unsigned char stack2[8 << 10] __attribute__((aligned(8)));
static l4_cap_idx_t thread1_cap, thread2_cap;
static void vlogprintn(const char *s, int l)
{
if (l > L4_VCON_WRITE_SIZE)
l = L4_VCON_WRITE_SIZE;
l4_vcon_send(L4_BASE_LOG_CAP, s, l);
}
static void vlogprint(const char *s)
{
vlogprintn(s, strlen(s));
}
static void vlogprintc(const char c)
{
vlogprintn(&c, 1);
}
static void thread1(void)
{
l4_msg_regs_t *mr = l4_utcb_mr();
l4_msgtag_t tag;
int i, j;
printf("Thread1 up (%p)\n", l4_utcb());
for (i = 0; i < 10; i++)
{
for (j = 0; j < L4_UTCB_GENERIC_DATA_SIZE; j++)
mr->mr[j] = 'A' + (i + j) % ('~' - 'A' + 1);
tag = l4_msgtag(0, L4_UTCB_GENERIC_DATA_SIZE, 0, 0);
if (l4_msgtag_has_error(l4_ipc_send(thread2_cap, l4_utcb(), tag, L4_IPC_NEVER)))
printf("IPC-send error\n");
}
mr->mr[0] = 1;
if (l4_msgtag_has_error(l4_ipc_send(thread2_cap, l4_utcb(), tag, L4_IPC_NEVER)))
printf("IPC-send error\n");
printf("Thread1 done\n");
}
L4UTIL_THREAD_STATIC_FUNC(thread2)
{
l4_msgtag_t tag;
l4_msg_regs_t mr;
unsigned i;
// No printf() here because this would require a working pthread environment!
vlogprint("Thread2 up\n");
while (1)
{
if (l4_msgtag_has_error(tag = l4_ipc_receive(thread1_cap, l4_utcb(), L4_IPC_NEVER)))
vlogprint("IPC receive error\n");
memcpy(&mr, l4_utcb_mr(), sizeof(mr));
if (mr.mr[0] == 1) // exit notification
break;
vlogprint("Thread2 receive: ");
for (i = 0; i < l4_msgtag_words(tag); i++)
vlogprintc((char)mr.mr[i]);
vlogprint("\n");
}
vlogprint("Thread2 done, switching to thread1\n");
if (l4_msgtag_has_error(l4_ipc_send(thread1_cap, l4_utcb(),
tag, L4_IPC_NEVER)))
vlogprint("IPC-send error\n");
// In theory this could hit if the above IPC send operation doesn't switch
// to the other thread.
__builtin_trap();
}
int main(void)
{
l4_msgtag_t tag;
thread1_cap = l4re_env()->main_thread;
thread2_cap = l4re_util_cap_alloc();
if (l4_is_invalid_cap(thread2_cap))
{
printf("Cannot allocate thread2 capability\n");
return 1;
}
tag = l4_factory_create_thread(l4re_env()->factory, thread2_cap);
if (l4_error(tag))
{
printf("Cannot create thread2\n");
return 2;
}
l4_addr_t kumem;
if (l4re_util_kumem_alloc(&kumem, 0, L4RE_THIS_TASK_CAP, l4re_env()->rm))
{
printf("Cannot allocate UTCB for thread2\n");
return 3;
}
l4_thread_control_start();
l4_thread_control_pager(l4re_env()->rm);
l4_thread_control_exc_handler(l4re_env()->rm);
l4_thread_control_bind((l4_utcb_t *)kumem, L4RE_THIS_TASK_CAP);
tag = l4_thread_control_commit(thread2_cap);
if (l4_error(tag))
{
printf("Cannot set thread2 thread parameters\n");
return 4;
}
tag = l4_thread_ex_regs(thread2_cap,
(l4_umword_t)thread2,
(l4_umword_t)(stack2 + sizeof(stack2)), 0);
if (l4_error(tag))
{
printf("Cannot set thread2 IP/SP\n");
return 5;
}
l4_sched_param_t sp = l4_sched_param(1, 0);
tag = l4_scheduler_run_thread(l4re_env()->scheduler, thread2_cap, &sp);
if (l4_error(tag))
{
printf("Cannot start thread2\n");
return 6;
}
thread1();
if (l4_msgtag_has_error(l4_ipc_receive(thread2_cap, l4_utcb(),
L4_IPC_NEVER)))
printf("IPC-receive error\n");
l4_task_unmap(L4RE_THIS_TASK_CAP,
l4_obj_fpage(thread2_cap, 0, L4_FPAGE_RWX),
L4_FP_ALL_SPACES);
printf("Terminated thread2. Terminating.\n");
return 0;
}

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_vcpu
SRC_CC = vcpu.cc
REQUIRES_LIBS = libvcpu cxx_io cxx_libc_io
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,472 @@
/*
* (c) 2009 Technische Universität Dresden
* This file is part of TUD:OS, which is distributed under the terms of the
* GNU General Public License 2. Please see the COPYING file for details.
*/
#include <l4/sys/ipc.h>
#include <l4/sys/thread>
#include <l4/sys/factory>
#include <l4/sys/scheduler>
#include <l4/sys/utcb.h>
#include <l4/sys/kdebug.h>
#include <l4/util/util.h>
#include <l4/util/thread.h>
#include <l4/re/env>
#include <l4/re/util/cap_alloc>
#include <l4/re/util/kumem_alloc>
#include <l4/sys/debugger.h>
#include <l4/vcpu/vcpu.h>
#include <l4/cxx/iostream>
#include <l4/re/error_helper>
#include <l4/sys/compiler.h>
#include <l4/sys/task>
#include <l4/sys/irq>
#include <l4/sys/vcpu.h>
#include <cstdarg>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using L4Re::chksys;
using L4Re::chkcap;
static L4::Cap<L4::Irq> irq;
static char thread_stack[8 << 10];
static char hdl_stack[8 << 10];
static L4::Cap<L4::Task> vcpu_task;
static l4_vcpu_state_t *vcpu;
const l4_addr_t super_code_map_addr = 0x10000;
extern char my_super_code[];
extern char my_super_code_excp[];
extern char my_super_code_excp_after[];
static char print_buf[512];
static int print_buf_pos;
static void vvprint(char const *s);
/* We don't want to call libc's multi-threaded printf
* implementation in a vcpu running in another task */
static void vvprint(char const *s)
{
int l = strlen(s);
for (int i = 0; i < l; ++i)
{
print_buf[print_buf_pos] = s[i];
print_buf_pos = (print_buf_pos + 1) % sizeof(print_buf);
}
}
#if defined(ARCH_x86) || defined(ARCH_amd64)
static unsigned long fs;
static unsigned long ds;
asm
(
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
"my_super_code_excp: \t\n"
" ud2a \t\n"
"my_super_code_excp_after: \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" add $4, %eax \t\n"
" jmp 1b \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *v)
{
asm volatile ("mov %%fs, %0" : "=r"(fs));
asm volatile ("mov %%ds, %0" : "=r"(ds));
#ifndef __amd64__
v->r.gs = ds;
v->r.fs = ds;
v->r.es = ds;
v->r.ds = ds;
#endif
v->r.ss = ds;
}
static void handler_prolog()
{
asm volatile ("mov %0, %%es \t\n"
"mov %0, %%ds \t\n"
"mov %1, %%fs \t\n"
: : "r"(ds), "r"(fs));
}
#elif defined(ARCH_arm)
asm
(
#if !defined(CONFIG_MMU) && defined(__clang__)
".arch armv7-r \t\n"
#endif
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
"my_super_code_excp: \t\n"
" swi 0 \t\n"
"my_super_code_excp_after: \t\n"
" mrc p8, 0, r0, cr7, cr14 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" add r0, r0, #4 \t\n"
" b 1b \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#elif defined(ARCH_arm64)
asm
(
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
"my_super_code_excp: \t\n"
" svc 0 \t\n"
"my_super_code_excp_after: \t\n"
" sysl x0, #0, c7, c14, #2 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" add x0, x0, #4 \t\n"
" b 1b \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#elif defined(ARCH_ppc32)
asm
(
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: addi %r4, %r4, 4 \t\n"
" addi %r4, %r4, 4 \t\n"
" addi %r4, %r4, 4 \t\n"
"my_super_code_excp: \t\n"
" trap \t\n"
"my_super_code_excp_after: \t\n"
" addi %r4, %r4, 4 \t\n"
" addi %r4, %r4, 4 \t\n"
" addi %r4, %r4, 4 \t\n"
" b 1b \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#elif defined(ARCH_sparc)
asm
(
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: add %o0, 4, %o0 \t\n"
"my_super_code_excp: \t\n"
" ta 2 \t\n"
"my_super_code_excp_after: \t\n"
" add %o0, 4, %o0 \t\n"
" b 1b \t\n"
" nop \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#elif defined(ARCH_mips)
asm
(
".p2align 14 \t\n" // mapping must be L4_PAGESHIFT aligned
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
"my_super_code_excp: \t\n"
" mfc0 $t0, $10, 0 \t\n" /* break inst enters jdb, use mfc0 to trigger CpU exception */
"my_super_code_excp_after: \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" addiu $a0, $a0, 4 \t\n"
" b 1b \t\n"
" nop \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#elif defined(ARCH_riscv)
asm
(
".p2align 12 \t\n"
".global my_super_code \t\n"
".global my_super_code_excp \t\n"
".global my_super_code_excp_after \t\n"
"my_super_code: \t\n"
"1: addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
"my_super_code_excp: \t\n"
" ecall \t\n"
"my_super_code_excp_after: \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" addi a0, a0, 4 \t\n"
" j 1b \t\n"
);
static void setup_user_state_arch(l4_vcpu_state_t *) { }
static void handler_prolog() {}
#else
#error Add your architecture.
#endif
static void handler(void)
{
handler_prolog();
vcpu->state &= ~L4_VCPU_F_EXCEPTIONS;
if (0)
l4vcpu_print_state(vcpu, "");
// very simple page-fault handling
// we're just replying with the only page we have, without checking any
// values
if (l4vcpu_is_page_fault_entry(vcpu))
{
vcpu_task->map(L4Re::This_task, l4_fpage((l4_addr_t)my_super_code,
L4_PAGESHIFT, L4_FPAGE_RWX),
super_code_map_addr);
vcpu->saved_state |= L4_VCPU_F_PAGE_FAULTS;
}
else if (l4vcpu_is_irq_entry(vcpu))
{
// We use the label 2000 for our IRQ
if (vcpu->i.label == 2000)
vvprint("Our triggered IRQ\n");
else if (vcpu->i.label == 0)
// direct IPC message to vCPU without
// going through an IPCgate, label is set to 0
vvprint("IPC\n");
else
vvprint("Unclassifiable message\n");
}
else
// we should also check the exception number here
if (vcpu->r.ip == (l4_addr_t)my_super_code_excp - (l4_addr_t)my_super_code + super_code_map_addr)
vcpu->r.ip += my_super_code_excp_after - my_super_code_excp;
//vvprint("resume\n");
L4::Cap<L4::Thread> self;
self->vcpu_resume_commit(self->vcpu_resume_start());
l4_infinite_loop();
}
L4UTIL_THREAD_STATIC_FUNC(vcpu_thread)
{
vvprint("Hello vCPU\n");
memset(hdl_stack, 0, sizeof(hdl_stack));
setup_user_state_arch(vcpu);
vcpu->saved_state = L4_VCPU_F_USER_MODE
| L4_VCPU_F_EXCEPTIONS
| L4_VCPU_F_PAGE_FAULTS
| L4_VCPU_F_IRQ;
vcpu->r.ip = super_code_map_addr;
vcpu->r.sp = 0x40000; // actually doesn't matter, we're not using any
// stack memory in our code
L4::Cap<L4::Thread> self;
vvprint("IRET\n");
vcpu->user_task = vcpu_task.cap();
self->vcpu_resume_commit(self->vcpu_resume_start());
vvprint("IRET: failed!\n");
l4_infinite_loop();
}
static int run(void)
{
l4_utcb_t *u = l4_utcb();
L4::Cap<L4::Thread> vcpu_cap;
printf("vCPU example\n");
l4_debugger_set_object_name(l4re_env()->main_thread, "vcputest");
// new task
vcpu_task = chkcap(L4Re::Util::cap_alloc.alloc<L4::Task>(),
"Task cap alloc");
l4_fpage_t utcb_area = l4_fpage_invalid();
chksys(L4Re::Env::env()->factory()->create_task(vcpu_task, &utcb_area),
"create task");
l4_debugger_set_object_name(vcpu_task.cap(), "vcpu 'user' task");
/* new thread/vCPU */
vcpu_cap = chkcap(L4Re::Util::cap_alloc.alloc<L4::Thread>(),
"vCPU cap alloc");
l4_touch_rw(thread_stack, sizeof(thread_stack));
chksys(L4Re::Env::env()->factory()->create(vcpu_cap), "create thread");
l4_debugger_set_object_name(vcpu_cap.cap(), "vcpu thread");
// get an IRQ
irq = chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(),
"Irq cap alloc");
chksys(L4Re::Env::env()->factory()->create(irq), "irq");
l4_debugger_set_object_name(irq.cap(), "some irq");
// get memory for vCPU state
l4_addr_t kumem;
if (0)
kumem = (l4_addr_t)l4re_env()->first_free_utcb;
else
{
if (L4Re::Util::kumem_alloc(&kumem, 0))
exit(1);
}
l4_utcb_t *vcpu_utcb = (l4_utcb_t *)kumem;
vcpu = (l4_vcpu_state_t *)(kumem + L4_UTCB_OFFSET);
vcpu->entry_sp = (l4_umword_t)hdl_stack + sizeof(hdl_stack);
vcpu->entry_ip = (l4_umword_t)handler;
printf("VCPU: utcb = %p, vcpu = %p\n", vcpu_utcb, vcpu);
// Create and start vCPU thread
L4::Thread::Attr attr;
attr.pager(L4Re::Env::env()->rm());
attr.exc_handler(L4Re::Env::env()->main_thread());
attr.bind(vcpu_utcb, L4Re::This_task);
chksys(vcpu_cap->control(attr), "control");
chksys(vcpu_cap->vcpu_control((l4_addr_t)vcpu), "enable VCPU");
chksys(vcpu_cap->ex_regs((l4_umword_t)vcpu_thread,
(l4_umword_t)thread_stack + sizeof(thread_stack),
0));
chksys(L4Re::Env::env()->scheduler()->run_thread(vcpu_cap,
l4_sched_param(1)));
// Bind IRQ to our vCPU thread
chksys(irq->bind_thread(vcpu_cap, 2000));
// Send some IPCs to the vCPU
l4_sleep(10);
for (int i = 0; i < 20; ++i)
l4_ipc_send(vcpu_cap.cap(), u, l4_msgtag(10 + i, 0, 0, 0), L4_IPC_NEVER);
// Some IRQ inbetween
irq->trigger();
l4_sleep(10);
for (int i = 21; i < 40; ++i)
l4_ipc_send(vcpu_cap.cap(), u, l4_msgtag(10 + i, 0, 0, 0), L4_IPC_NEVER);
// finally, trigger IRQs and look for output to do
int cur_pos = 0;
while (1)
{
while (cur_pos == print_buf_pos)
{
l4_sleep(500);
irq->trigger();
l4_mb();
}
do
{
putchar(print_buf[cur_pos]);
cur_pos = (cur_pos + 1) % sizeof(print_buf);
l4_mb();
}
while (cur_pos != print_buf_pos);
}
return 0;
}
int main()
{
try { return run(); }
catch (L4::Runtime_error &e)
{
L4::cerr << "FATAL uncaught exception: " << e
<< "\nterminating...\n";
}
catch (...)
{
L4::cerr << "FATAL uncaught exception of unknown type\n"
<< "terminating...\n";
}
return 1;
}

View File

@@ -0,0 +1,11 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = ex_vmtest
SYSTEMS = x86-l4f amd64-l4f
SRC_S = guest.S
SRC_CC = vm.cc vmx.cc svm.cc main.cc
REQUIRES_LIBS = libvcpu l4util
DEPENDS_PKGS = $(REQUIRES_LIBS)
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
.section .bss
.p2align 12
.globl is_vmx
is_vmx:
.long 0x0
.macro CALLVMM edx
push %eax
push %edx
movl \edx, %edx
movl $(((vmcall) - (guestcode32)) + 0x1000), %eax
call *%eax // vmcall is on same page as guestcode
pop %edx
pop %eax
.endm
/**
* Guest code.
*
* This code runs inside the virtual machine.
*/
.section .text
.code32
.globl guestcode32
.p2align 12
guestcode32:
// first, prove that code runs in the VM
inc %eax
CALLVMM edx=$0x22446688
movl $0, %ecx
// test divide by zero exception, should be handled by guest (irq_dz)
// this ensures that the guest IDT works
movl $0, %eax
movl $0, %ebx
divl %ebx
// test that the GDT works
// data segment
movl $0x10, %eax
movl %eax, %ds
// stack segment
movl $0x28, %eax
movl %eax, %ss // set stack segment
// test stack
pushl %eax
popl %eax
// switch to ring3 to trigger #ac
mov %esp, %eax
push $0x23 //user data segment with bottom 2 bits set for ring 3
push %eax //push our current stack
pushf
push $0x1b; //user code segment with bottom 2 bits set for ring 3
push $(((usermode) - (guestcode32)) + 0x1000)
iret
.globl usermode
usermode:
// set data segment
movl $0x23, %eax
movl %eax, %ds
// set proper stack segment
movl $0x33, %eax
movl %eax, %ss
// now switch to user stack...
movl $0x5000, %eax
movl %eax, %esp
// now, test stack
pushl %eax
popl %eax
// trigger alignment check exception
sub $3, %esp
push %eax
popl %eax
// never reach this
ret
.globl guestcode64
guestcode64:
CALLVMM edx=$0x4242
// never reach this
ret
// code left here for documentation
// trigger debug exception
movl $(((debug) - (guestcode32)) + 0x1000), %eax
movl %eax, %dr0 // this will be intercepted in kvm
debug:
nop
nop
// function vmcall, uses edx to indicate reason for vmcall
.globl vmcall
.type vmcall, @function
vmcall:
cmpl $0, (0x3000) // is_vmx
jne _vmx
vmmcall
jmp _out
_vmx:
vmcall
_out:
ret
.globl interrupt_handlers
.p2align 12
interrupt_handlers:
nop
.globl irq_dz
// #dz interrupt handler - just advances the eip over the offending instruction
irq_dz:
CALLVMM edx=$0x42
addl $2, (%esp) // move eip
ret
.globl irq_db
irq_db:
CALLVMM edx=$0x44
ret
.globl irq_bp
irq_bp:
CALLVMM edx=$0x48
ret
.globl irq_ac
irq_ac:
CALLVMM edx=$0x50
add $4, %esp
iret

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015-2016 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
#include <l4/util/cpu.h>
#include "vmx.h"
#include "svm.h"
#include <cstdio>
enum Cpu_vendor
{
Intel,
Amd,
Unknown
};
static Cpu_vendor get_cpu_vendor()
{
if (!l4util_cpu_has_cpuid())
return Unknown;
l4_umword_t ax, bx, cx, dx;
l4util_cpu_cpuid(0, &ax, &bx, &cx, &dx);
if (bx == 0x756e6547 && cx == 0x6c65746e && dx == 0x49656e69)
return Intel;
if (bx == 0x68747541 && cx == 0x444d4163 && dx == 0x69746e65)
return Amd;
return Unknown;
}
int main()
{
printf("Hello from vmtest\n");
printf("TAP TEST START\n");
switch (get_cpu_vendor())
{
case Intel:
{
Vmx vmxtest;
vmxtest.run_tests();
}
break;
case Amd:
{
Svm svmtest;
svmtest.run_tests();
}
break;
default:
printf("# Unknown CPU. Bye.\n");
return 1;
}
printf("TAP TEST FINISH\n");
return 0;
}

View File

@@ -0,0 +1,11 @@
# vim: set ft=l4mods:
# Module configuration file for ex_vmtest
# include this into your main modules.list, so that your
# default-kernel and default-bootstrap and modaddr options are being used
entry ex_vmtest
roottask moe --init=rom/ex_vmtest
module l4re
module moe
module ex_vmtest

View File

@@ -0,0 +1,305 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*/
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Henning Schild <hschild@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include "svm.h"
#include <l4/util/cpu.h>
#include <cstdio>
static char const *const svm_features[] = {
"Nested Paging",
"LbrVirt",
"SVML",
"nRIP",
"TscRateMsr",
"VmcbClean",
"FlushByAsid",
"DecodeAssists",
0,
0,
"PauseFilter",
0,
"PauseFilterThreshold",
"AVIC",
};
bool
Svm::cpu_virt_capable()
{
l4_umword_t ax, bx, cx, dx;
if (!l4util_cpu_has_cpuid())
return false;
l4util_cpu_cpuid(0x80000001, &ax, &bx, &cx, &dx);
if (!(cx & 4))
{
printf("# CPU does not support SVM.\n");
return false;
}
l4util_cpu_cpuid(0x8000000a, &ax, &bx, &cx, &dx);
printf("# SVM revision: %lx\n", ax & 0xf);
printf("# Number of ASIDs: %lu\n", bx);
printf("# This CPU has the following extra SVM features:\n");
for (unsigned i = 0; i < (sizeof(svm_features)/sizeof(svm_features[0])); ++i)
if (svm_features[i] && dx & (1 << i))
printf("#\t%s\n", svm_features[i]);
return true;
}
bool
Svm::npt_available()
{
l4_umword_t ax, bx, cx, dx;
if (!l4util_cpu_has_cpuid())
return false;
l4util_cpu_cpuid(0x8000000a, &ax, &bx, &cx, &dx);
printf("# NPT available: %s\n", dx & 1 ? "yes" : "no");
return (dx & 1);
}
void
Svm::initialize_ext_state(unsigned)
{
vmcb_s->control_area.np_enable = 1;
vmcb_s->control_area.guest_asid_tlb_ctl = 1;
vmcb_s->state_save_area.es.selector = 0;
vmcb_s->state_save_area.es.attrib = 0;
vmcb_s->state_save_area.es.limit = 0;
vmcb_s->state_save_area.es.base = 0ULL;
//vmcb[256 + 0] = 0; // es; attrib sel
vmcb_s->state_save_area.cs.selector = 0x10;
vmcb_s->state_save_area.cs.attrib = 0xc9b;
vmcb_s->state_save_area.cs.limit = 0xffffffff;
vmcb_s->state_save_area.cs.base = 0ULL;
vmcb_s->state_save_area.ss.selector = 0x18;
vmcb_s->state_save_area.ss.attrib = 0xc93;
vmcb_s->state_save_area.ss.limit = 0xffffffff;
vmcb_s->state_save_area.ss.base = 0ULL;
vmcb_s->state_save_area.ds.selector = 0x20;
vmcb_s->state_save_area.ds.attrib = 0xcf3;
vmcb_s->state_save_area.ds.limit = 0xffffffff;
vmcb_s->state_save_area.ds.base = 0ULL;
vmcb_s->state_save_area.fs.selector = 0;
vmcb_s->state_save_area.fs.attrib = 0xcf3;
vmcb_s->state_save_area.fs.limit = 0xffffffff;
vmcb_s->state_save_area.fs.base = 0ULL;
vmcb_s->state_save_area.gs.selector = 0;
vmcb_s->state_save_area.gs.attrib = 0xcf3;
vmcb_s->state_save_area.gs.limit = 0xffffffff;
vmcb_s->state_save_area.gs.base = 0ULL;
vmcb_s->state_save_area.gdtr.selector = 0;
vmcb_s->state_save_area.gdtr.attrib = 0;
vmcb_s->state_save_area.gdtr.limit = 0xffff;
vmcb_s->state_save_area.gdtr.base = Gdt;
vmcb_s->state_save_area.ldtr.selector = 0;
vmcb_s->state_save_area.ldtr.attrib = 0;
vmcb_s->state_save_area.ldtr.limit = 0;
vmcb_s->state_save_area.ldtr.base = 0;
vmcb_s->state_save_area.idtr.selector = 0;
vmcb_s->state_save_area.idtr.attrib = 0;
vmcb_s->state_save_area.idtr.limit = 0xffff;
vmcb_s->state_save_area.idtr.base = Idt;
vmcb_s->state_save_area.tr.selector = 0x28;
vmcb_s->state_save_area.tr.attrib = 0x8b;
vmcb_s->state_save_area.tr.limit = 0x67;
vmcb_s->state_save_area.tr.base = 0;
vmcb_s->state_save_area.g_pat = 0x7040600010406ULL;
vmcb_s->state_save_area.dr6 = 0;
vmcb_s->state_save_area.cpl = 0;
vmcb_s->control_area.intercept_exceptions = 0x0;
vmcb_s->control_area.intercept_instruction1 |= 0x20; // intercept int1
vmcb_s->control_area.intercept_wr_crX |= 0xff; // intercept write to cr
vmcb_s->control_area.intercept_rd_crX |= 0xff; // intercept reads to cr
vmcb_s->control_area.exitcode = 0;
vmcb_s->control_area.clean_bits = 0;
}
void
Svm::set_rax(l4_umword_t rax)
{
vmcb_s->state_save_area.rax = rax;
}
void
Svm::set_rsp(l4_umword_t rsp)
{
vmcb_s->state_save_area.rsp = rsp;
}
void
Svm::set_rflags(l4_umword_t rflags)
{
vmcb_s->state_save_area.rflags = rflags;
}
void
Svm::set_rip(l4_umword_t rip)
{
vmcb_s->state_save_area.rip = rip;
}
void
Svm::set_cr0(l4_umword_t cr0)
{
vmcb_s->state_save_area.cr0 = cr0;
}
void
Svm::set_cr3(l4_umword_t cr3)
{
vmcb_s->state_save_area.cr3 = cr3;
}
void
Svm::set_cr4(l4_umword_t cr4)
{
vmcb_s->state_save_area.cr4 = cr4;
}
void
Svm::set_dr7(l4_umword_t dr7)
{
vmcb_s->state_save_area.dr7 = dr7;
}
l4_umword_t
Svm::get_rax()
{
return vmcb_s->state_save_area.rax;
}
void
Svm::enable_npt()
{
vmcb_s->control_area.np_enable = 1;
}
void
Svm::disable_npt()
{
vmcb_s->control_area.np_enable = 0;
}
void
Svm::set_efer(l4_umword_t efer)
{
vmcb_s->state_save_area.efer = efer;
}
unsigned
Svm::handle_vmexit()
{
printf("# exit code=%llx rip=0x%llx intercept_exceptions=0x%x\n",
vmcb_s->control_area.exitcode, vmcb_s->state_save_area.rip,
vmcb_s->control_area.intercept_exceptions);
switch (vmcb_s->control_area.exitcode)
{
case 0x4:
printf("# Read of cr4 intercepted.\n");
vmcb_s->state_save_area.rip += 1;
return 1;
case 0x14:
printf("# Write to cr4 intercepted.\n");
vmcb_s->state_save_area.rip += 1;
return 1;
case 0x41:
printf("# divide by zero exception encountered\n");
vmcb_s->state_save_area.rip += 1;
return 1;
case 0x43:
printf("# Software interrupt\n");
vmcb_s->state_save_area.rip += 1;
return 1;
case 0x46:
printf("# undefined instruction\n");
vmcb_s->state_save_area.rip += 2;
return 1;
case 0x4d:
printf("# General protection fault. Bye\n");
printf("# cs=%08x attrib=%x, limit=%x, base=%llx\n",
vmcb_s->state_save_area.cs.selector,
vmcb_s->state_save_area.cs.attrib,
vmcb_s->state_save_area.cs.limit,
vmcb_s->state_save_area.cs.base);
printf("# ss=%08x attrib=%x, limit=%x, base=%llx\n",
vmcb_s->state_save_area.ss.selector,
vmcb_s->state_save_area.ss.attrib,
vmcb_s->state_save_area.ss.limit,
vmcb_s->state_save_area.ss.base);
printf("# np_enabled=%lld\n", vmcb_s->control_area.np_enable);
printf("# cr0=%llx cr4=%llx\n", vmcb_s->state_save_area.cr0,
vmcb_s->state_save_area.cr4);
printf("# interrupt_ctl=%llx\n", vmcb_s->control_area.interrupt_ctl);
printf("# rip=%llx, rsp=%llx, cpl=%d\n", vmcb_s->state_save_area.rip,
vmcb_s->state_save_area.rsp, vmcb_s->state_save_area.cpl);
printf("# exitinfo1=%llx\n", vmcb_s->control_area.exitinfo1);
return 0;
case 0x4e:
printf("# page fault; error code=%llx, pfa=%llx\n",
vmcb_s->control_area.exitinfo1, vmcb_s->control_area.exitinfo2);
return 0;
case 0x51:
printf("# alignment check exception encountered.\n");
vmcb_s->state_save_area.rsp += 3;
return Alignment_check_intercept;
case 0x7f:
printf("# shutdown.\n");
return 0;
case 0x400:
printf("# host-level page fault; error code=%llx, gpa=%llx\n",
vmcb_s->control_area.exitinfo1, vmcb_s->control_area.exitinfo2);
return 0;
case 0x81:
printf("# VMMCALL ecx = %ld edx=%lx\n", (long)(vcpu->r.cx & 0xffffffff), vcpu->r.dx);
vmcb_s->state_save_area.rip += 3;
return VMCALL;
case l4_uint64_t(~0):
printf("# Invalid guest state.\n");
return 0;
default:
printf("# Unhandled vm exit.\n");
return 0;
}
return 1;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
#pragma once
#include "vm.h"
class Svm: public Vm
{
public:
Svm()
{
setup_vm();
vmcb_s = (l4_vm_svm_vmcb_t *)vcpu_ext_state;
is_vmx = 0;
}
protected:
bool cpu_virt_capable();
bool npt_available();
void initialize_ext_state(unsigned long_mode);
unsigned handle_vmexit();
void set_rax(l4_umword_t rax);
void set_rsp(l4_umword_t rsp);
void set_rflags(l4_umword_t rflags);
void set_rip(l4_umword_t rip);
void set_cr0(l4_umword_t cr0);
void set_cr3(l4_umword_t cr3);
void set_cr4(l4_umword_t cr4);
void set_dr7(l4_umword_t dr7);
void set_efer(l4_umword_t efer);
l4_umword_t get_rax();
void enable_npt();
void disable_npt();
private:
l4_vm_svm_vmcb_t *vmcb_s;
};

View File

@@ -0,0 +1,522 @@
/*
* Copyright (C) 2014-2016, 2018, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
#include "vm.h"
#include <l4/util/util.h>
#include <l4/re/env>
#include <l4/re/util/cap_alloc>
#include <l4/sys/factory>
#include <l4/sys/thread>
#include <l4/vcpu/vcpu.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
// code to be run inside virtual machine
extern char guestcode32[], guestcode64[];
extern char interrupt_handlers[]; // implemented in guest.S
extern char irq_dz[], irq_db[], irq_bp[], irq_ac[];
// valid in 32bit only
class Idt_entry
{
private:
l4_uint16_t _offset_low;
l4_uint16_t _segment_selector;
l4_uint8_t _ist;
l4_uint8_t _access;
l4_uint16_t _offset_high;
public:
Idt_entry() {}
void set(char *isr, l4_uint16_t selector);
} __attribute__((packed));
void
Idt_entry::set(char *isr, l4_uint16_t selector)
{
// calculate isr guest address
isr = isr - interrupt_handlers + (char*)Interrupt_handler;
_offset_low = (unsigned long)isr & 0x0000ffff;
_segment_selector = selector;
_ist = 0;
_access = 0x8f;
_offset_high= ((unsigned long)isr & 0xffff0000)>>16;
}
class Gdt_entry
{
private:
unsigned short _segment_limit_low;
unsigned short _base_address_low;
unsigned char _base_address_middle;
unsigned short _flags;
unsigned char _base_address_upper;
public:
enum{
Shift_segment_type = 0,
Shift_descriptor_type = 4,
Shift_dpl = 5,
Shift_present = 7,
Shift_segment_limit = 8,
Shift_avl = 12,
Shift_long = 13,
Shift_size = 14,
Shift_granularity = 15,
};
enum Segment_type{ // relative to _flags
Data_ro= 0x0,
Data_roa= 0x1,
Data_rw= 0x2,
Data_rwa=0x3,
Data_ro_expand_down=0x4,
Data_roa_expand_down=0x5,
Data_rw_expand_down=0x6,
Data_rwa_expand_down=0x7,
Code_xo = 0x8,
Code_xoa = 0x9,
Code_xr = 0xa,
Code_xra = 0xb,
Code_xo_conforming = 0xc,
Code_xo_conforming_a = 0xd,
Code_xr_conforming = 0xe,
Code_xr_conforming_a = 0xf,
};
enum Dpl{
User = 0x3,
System = 0x0
};
Gdt_entry() {}
void set(
unsigned long base_address,
unsigned long segment_limit,
Dpl dpl,
Segment_type type);
void set_flat(Dpl dpl, Segment_type type)
{
set(0, 0xffffffff, dpl, type);
}
void set_null()
{
set_flat(Gdt_entry::System, Gdt_entry::Data_ro);
}
} __attribute__((packed));
void
Gdt_entry::set(
unsigned long base_address,
unsigned long segment_limit,
Dpl dpl,
Segment_type type)
{
if (segment_limit)
_flags |= 1 << Shift_present;
_segment_limit_low = 0x0000ffff & segment_limit;
_flags |= ((0x000f0000 & segment_limit) >> 16) << Shift_segment_limit;
_base_address_low = 0x0000ffff & base_address;
_base_address_middle = (0x00ff0000 & base_address) >> 16;
_base_address_upper = (0xff000000 & base_address) >> 24;
_flags |= type;
_flags |= dpl << Shift_dpl;
if (dpl != User)
_flags |= 1 << Shift_avl;
_flags |= 1 << Shift_size; // 32bit
_flags |= 0 << Shift_long;
_flags |= 1 << Shift_granularity;
_flags |= 1 << Shift_descriptor_type;
}
static Idt_entry idt[32] __attribute__((aligned(4096)));
static Gdt_entry gdt[32] __attribute__((aligned(4096)));
static char guest_stack[STACK_SIZE] __attribute__((aligned(4096)));
static char handler_stack[STACK_SIZE];
l4_uint64_t pde __attribute__((aligned(4096)));
l4_uint64_t pdpte __attribute__((aligned(4096)));
l4_uint64_t pml4e __attribute__((aligned(4096)));
void
Vm::setup_vm()
{
l4_addr_t ext_state;
l4_msgtag_t msg;
int ret;
printf("# Checking if CPU is capable of virtualization: ");
if (!cpu_virt_capable())
{
printf("CPU does not support virtualization. Bye.\n");
exit(1);
}
printf("# It is.\n");
printf("# Allocating a VM capability: ");
vm_cap = L4Re::Util::cap_alloc.alloc<L4::Vm>();
if (!vm_cap.is_valid())
{
printf("Failure.\n");
exit(1);
}
printf("Success.\n");
printf("# Creating a VM kernel object: ");
msg = L4Re::Env::env()->factory()->create(vm_cap);
if (l4_error(msg))
{
printf("Failure.\n");
exit(1);
}
printf("Success.\n");
printf("# Trying to allocate vCPU extended state: ");
ret = l4vcpu_ext_alloc(&vcpu, &ext_state, L4_BASE_TASK_CAP,
L4Re::Env::env()->rm().cap());
if (ret)
{
printf("Could not find vCPU extended state mem: %d\n", ret);
exit(1);
}
printf("Success.\n");
vcpu_ext_state = (void *)ext_state;
if (vcpu_ext_state == 0)
{
printf("# vCPU extended state @NULL. Exit.\n");
exit (1);
}
vcpu->state = L4_VCPU_F_FPU_ENABLED;
vcpu->saved_state = L4_VCPU_F_USER_MODE
| L4_VCPU_F_FPU_ENABLED
| L4_VCPU_F_IRQ;
vcpu->entry_ip = (l4_umword_t)&handler;
vcpu->entry_sp = (l4_umword_t)(handler_stack + STACK_SIZE);
vcpu->user_task = vm_cap.cap();
printf("# Trying to switch vCPU to extended operation: ");
msg = l4_thread_vcpu_control_ext(L4_INVALID_CAP, (l4_addr_t)vcpu);
ret = l4_error(msg); if (ret)
{
printf("Could not enable ext vCPU: %d\n", ret);
exit(1);
}
printf("Success.\n");
printf("# Clearing guest stack.\n");
memset(guest_stack, 0, sizeof(guest_stack));
printf("# Clearing handler stack.\n");
memset(handler_stack, 0, sizeof(handler_stack));
printf("# done.\n");
l4_touch_ro((void*)guestcode32, 2);
printf("# Mapping code from %p to %p: ", (void*)guestcode32, (void*)Code);
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)guestcode32 & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RWX),
l4_map_control((l4_umword_t)Code, 0, L4_MAP_ITEM_MAP));
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
l4_touch_ro((void*)interrupt_handlers, 2);
printf("# Mapping interrupt handler from %p to %p: ", (void*)interrupt_handlers, (void*)Interrupt_handler);
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)interrupt_handlers & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RWX),
l4_map_control((l4_umword_t)Interrupt_handler, 0, L4_MAP_ITEM_MAP));
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
l4_touch_ro(&is_vmx, 2);
printf("# Mapping flags from %p to %p: ", &is_vmx, (void*)Flags);
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)&is_vmx & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RX),
l4_map_control((l4_umword_t)Flags, 0, L4_MAP_ITEM_MAP));
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
printf("# Mapping stack: \n");
for (l4_umword_t ofs = 0; ofs < STACK_SIZE; ofs += L4_PAGESIZE)
{
printf("# %p -> %p\n", (void*)((l4_umword_t)guest_stack + ofs), (void*)((l4_umword_t)Stack + ofs));
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage(((l4_umword_t)(guest_stack) + ofs) & L4_PAGEMASK,
L4_PAGESHIFT, L4_FPAGE_RWX),
l4_map_control(Stack + ofs, 0,
L4_MAP_ITEM_MAP));
}
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
idt[ 0].set(irq_dz, 0x8); // #DZ divide by zero
idt[ 1].set(irq_db, 0x8); // #DB debug exception
idt[ 3].set(irq_bp, 0x8); // #BP breakpoint (int3)
idt[17].set(irq_ac, 0x8); // #AC alignment check
gdt[0].set_null();
gdt[1].set_flat(Gdt_entry::System, Gdt_entry::Code_xra); // 0x8
gdt[2].set_flat(Gdt_entry::System, Gdt_entry::Data_rwa); // 0x10
gdt[3].set_flat(Gdt_entry::User, Gdt_entry::Code_xra); // 0x18
gdt[4].set_flat(Gdt_entry::User, Gdt_entry::Data_rwa); // 0x20
gdt[5].set_flat(Gdt_entry::System, Gdt_entry::Data_rwa); // 0x28
gdt[6].set_flat(Gdt_entry::User, Gdt_entry::Data_rwa); // 0x30
printf("# Mapping idt from %p to %p: ", (void*)idt, (void*)Idt);
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_addr_t)idt & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RW),
l4_map_control((l4_addr_t)Idt, 0, L4_MAP_ITEM_MAP));
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
printf("# Mapping gdt from %p to %p: ", (void*)gdt, (void*)Gdt);
msg = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_addr_t)gdt & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RW),
l4_map_control((l4_addr_t)Gdt, 0, L4_MAP_ITEM_MAP));
if ((ret = l4_error(msg)))
{
printf("failure: %d\n", ret);
exit(1);
}
else
printf("success\n");
}
unsigned
Vm::vm_resume()
{
int ret;
l4_msgtag_t msg;
msg = l4_thread_vcpu_resume_commit(L4_INVALID_CAP,
l4_thread_vcpu_resume_start());
ret = l4_error(msg);
if (ret)
{
printf("# vm_resume failed: %s (%d)\n", l4sys_errtostr(ret), ret);
return ret;
}
return ret;
}
void
Vm::run_tests()
{
int ret;
l4_umword_t rflags;
initialize_ext_state(0);
set_rip(Code);
set_cr3(0);
set_dr7(0xffffffff);
set_efer(0x1000);
// now configure CPU
#if 0
if (npt)
enable_npt();
else
disable_npt();
#endif
asm volatile("pushf \n"
"pop %0 \n"
: "=r" (rflags));
// clear interrupt flag
rflags &= 0xfffffdff;
// add AC (alignment check) flag
rflags |= (1 << 18);
l4_uint32_t cr0 = 0x1003b;
// add AM flag
cr0 |= (1 << 18);
set_rsp((l4_umword_t)Stack + STACK_SIZE - 1);
set_rflags(rflags);
set_cr0(cr0);
set_cr4(0x6b0);
set_dr7(0xffffffff);
vcpu->r.dx = vcpu->r.cx = vcpu->r.bx = vcpu->r.bp = vcpu->r.si = vcpu->r.di = 0;
set_rax(0);
// switch to VM
vm_resume();
ret = handle_vmexit();
if (ret == VMCALL)
{
if (vcpu->r.dx == 0x22446688)
printf("ok - vm running\n");
}
else
printf("not ok - vm not running\n");
vm_resume();
ret = handle_vmexit();
if (ret == VMCALL)
{
if (vcpu->r.dx == 0x42)
printf("ok - divide by zero exception handled correctly by the guest\n");
else
printf("not ok - wrong interrupt handler for #dz\n");
}
else if (ret == Exception_intercept) // svm intercepts #dz even when configured to not intercept
printf("ok - #dz intercepted\n");
else
printf("not ok - #dz not handled correctly\n");
vm_resume();
ret = handle_vmexit();
if (ret == Alignment_check_intercept)
printf("ok - alignment check exception intercepted\n");
else
printf("not ok - alignment check exception not intercepted\n");
#if __x86_64__
// test long mode if host on ia32e host
set_efer(0x1500);
set_dr7(0x700);
set_cr4(0x6b0);
set_cr3(make_ia32e_cr3());
set_cr0(0x8001003b);
set_rip((guestcode64 - guestcode32) + Code);
vm_resume();
ret = handle_vmexit();
if (ret == VMCALL && vcpu->r.dx == 0x4242)
printf("ok - long mode guest works\n");
else
printf("not ok - long mode guest not working\n");
#endif
}
void
Vm::handler()
{
printf("Received an IRQ. Considering this as ERROR.\n");
exit(1);
}
/**
* Create a very primitive PAE pagetable that establishes an identity mapping
* of the first 2MB of guest address space.
*
* \returns The cr3 to use when running the guest in ia32e mode.
*/
l4_umword_t
Vm::make_ia32e_cr3()
{
l4_umword_t cr3;
l4_msgtag_t tag;
int ret;
cr3 = (unsigned long)&pml4e;
// prepare pml4e
pml4e = (unsigned long)&pdpte
| 0x4 /*user allowed*/
| 0x2 /*write allowed*/
| 0x1 /*present*/;
// prepare pdpte
pdpte = (unsigned long)&pde
| 0x4 /*user allowed*/
| 0x2 /*write enabled*/
| 0x1 /*present*/;
// prepare pte
pde = 0x0
| (1 << 7) /*2mb page*/
| 0x4 /*user allowed*/
| 0x2 /*write enabled*/
| 0x1 /*present*/;
if ((l4_umword_t)&pml4e < 0x80000000)
printf("# Page tables reside below 4G.\n");
// map pml4e
printf("# Mapping pml4e from %p to %p ", &pml4e, &pml4e);
tag = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)&pml4e,
L4_PAGESHIFT, L4_FPAGE_RW),
l4_map_control((l4_umword_t)&pml4e, 0,
L4_MAP_ITEM_MAP));
if ((ret = l4_error(tag)))
{
printf("error %d\n", ret);
return ~0;
}
else
printf("success\n");
// map pdpte
printf("# Mapping pdpte from %p to %p ", &pdpte, &pdpte);
tag = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)&pdpte,
L4_PAGESHIFT, L4_FPAGE_RW),
l4_map_control((l4_umword_t)&pdpte, 0,
L4_MAP_ITEM_MAP));
if ((ret = l4_error(tag)))
{
printf("error %d\n", ret);
return ~0;
}
else
printf("success\n");
// map pde
printf("# Mapping pde from %p to %p ", &pde, &pde);
tag = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage((l4_umword_t)&pde,
L4_PAGESHIFT, L4_FPAGE_RW),
l4_map_control((l4_umword_t)&pde, 0,
L4_MAP_ITEM_MAP));
if ((ret = l4_error(tag)))
{
printf("error %d.\n", ret);
return ~0;
}
else
printf("success\n");
return cr3;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
#pragma once
#include <l4/sys/capability>
#include <l4/sys/vm>
#include <l4/sys/types.h>
#include <l4/sys/vcpu.h>
enum { STACK_SIZE = 8 << 10 }; // 8kb
// used to distinguish between intel and amd cpus
// reason: vmcall is vmmcall on amd
extern long is_vmx; // defined in guest.S, shared with the vm
enum Memory_layout
{
Code = 0x1000,
Interrupt_handler = 0x2000,
Flags = 0x3000, // contains is_vmx
Stack = 0x4000,
Idt = 0x6000,
Gdt = 0x7000,
};
class Vm
{
public:
void run_tests();
private:
static void handler();
virtual unsigned vm_resume();
l4_umword_t make_ia32e_cr3();
l4_umword_t make_ia32_cr3();
protected:
void setup_vm();
// To be implemented by SVM/VMX classes
virtual bool cpu_virt_capable() = 0;
virtual bool npt_available() = 0;
virtual void initialize_ext_state(unsigned long_mode) = 0;
virtual unsigned handle_vmexit() = 0;
virtual void set_rax(l4_umword_t rax) = 0;
virtual void set_rsp(l4_umword_t rsp) = 0;
virtual void set_rflags(l4_umword_t rflags) = 0;
virtual void set_rip(l4_umword_t rip) = 0;
virtual void set_cr0(l4_umword_t cr0) = 0;
virtual void set_cr3(l4_umword_t cr3) = 0;
virtual void set_cr4(l4_umword_t cr4) = 0;
virtual void set_dr7(l4_umword_t dr7) = 0;
virtual void set_efer(l4_umword_t efer) = 0;
virtual l4_umword_t get_rax() = 0;
virtual void enable_npt() = 0;
virtual void disable_npt() = 0;
l4_vcpu_state_t *vcpu;
// Virtual Machine Control Block for SVM.
// Virtual Machine Control Structure for VMX.
void *vcpu_ext_state;
L4::Cap<L4::Vm> vm_cap;
enum exit_reason
{
Legacy = 0x1,
VMCALL = 0x2,
Alignment_check_intercept,
Exception_intercept
};
};

View File

@@ -0,0 +1,169 @@
/*
* vmcs.h - VMCS definitions for X86 virtualization interface
*
* (c) 2011 Matthias Lange <mlange@sec.t-labs.tu-berlin.de>,
* (c) 2011 Janis Danisevskis <janis@sec.t-labs.tu-berlin.de>
*
* This file is part of the Karma VMM and distributed under the terms of the
* GNU General Public License, version 2.
*
* Please see the file COPYING-GPL-2 for details.
*/
#pragma once
// 16 bit width
enum
{
VMX_VPID = 0x0,
VMX_GUEST_ES_SEL = 0x800,
VMX_GUEST_CS_SEL = 0x802,
VMX_GUEST_SS_SEL = 0x804,
VMX_GUEST_DS_SEL = 0x806,
VMX_GUEST_FS_SEL = 0x808,
VMX_GUEST_GS_SEL = 0x80a,
VMX_GUEST_LDTR_SEL = 0x80c,
VMX_GUEST_TR_SEL = 0x80e,
VMX_HOST_ES_SEL = 0xc00,
VMX_HOST_CS_SEL = 0xc02,
VMX_HOST_SS_SEL = 0xc04,
VMX_HOST_DS_SEL = 0xc06,
VMX_HOST_FS_SEL = 0xc08,
VMX_HOST_GS_SEL = 0xc0a,
VMX_HOST_TR_SEL = 0xc0c,
};
// 64 bit width
enum
{
VMX_IO_BITMAP_ADDR_A = 0x2000,
VMX_IO_BITMAP_ADDR_B = 0x2002,
VMX_MSR_BITMAPS = 0x2004,
VMX_EXIT_MSR_STORE_ADDR = 0x2006,
VMX_EXIT_MSR_LOAD_ADDR = 0x2008,
VMX_ENTRY_MSR_LOAD_ADDR = 0x200a,
VMX_EXEC_VMCS_PTR = 0x200c,
VMX_TSC_OFFSET = 0x2010,
VMX_VIRT_APIC_ADDR = 0x2012,
VMX_APIC_ACCESS_ADDR = 0x2014,
VMX_EPT_PTR = 0x201a,
VMX_GUEST_PHYS_ADDR = 0x2400,
VMX_VMCS_LINK_PTR = 0x2800,
VMX_GUEST_IA32_DEBUGCTL = 0x2802,
VMX_GUEST_IA32_PAT = 0x2804,
VMX_GUEST_IA32_EFER = 0x2806,
VMX_GUEST_IA32_PERF_GLOBAL_CTRL = 0x2808,
VMX_GUEST_PDPTE0 = 0x280a,
VMX_GUEST_PDPTE1 = 0x280c,
VMX_GUEST_PDPTE2 = 0x280e,
VMX_GUEST_PDPTE3 = 0x2810,
VMX_HOST_IA32_PAT = 0x2c00,
VMX_HOST_IA32_EFER = 0x2c02,
VMX_HOST_IA32_PERF_GLOBAL_CTRL = 0x2c04,
};
// 32 bit width
enum
{
VMX_PIN_EXEC_CTRL = 0x4000,
VMX_PRIMARY_EXEC_CTRL = 0x4002,
VMX_EXCEPTION_BITMAP = 0x4004,
VMX_PF_ERROR_CODE_MASK = 0x4006,
VMX_PF_ERROR_CODE_MATCH = 0x4008,
VMX_CR3_TARGET_COUNT = 0x400a,
VMX_EXIT_CTRL = 0x400c,
VMX_EXIT_MSR_STORE_COUNT = 0x400e,
VMX_EXIT_MSR_LOAD_COUNT = 0x4010,
VMX_ENTRY_CTRL = 0x4012,
VMX_ENTRY_MSR_LOAD_COUNT = 0x4014,
VMX_ENTRY_INTERRUPT_INFO = 0x4016,
VMX_ENTRY_EXCEPTION_ERROR = 0x4018,
VMX_ENTRY_INSTRUCTION_LENGTH = 0x401a,
VMX_TPR_THRESHOLD = 0x401c,
VMX_SECOND_EXEC_CTRL = 0x401e,
VMX_PLE_GAP = 0x4020,
VMX_PLE_WINDOW = 0x4022,
VMX_INSTRUCTION_ERROR = 0x4400,
VMX_EXIT_REASON = 0x4402,
VMX_EXIT_INTERRUPT_INFO = 0x4404,
VMX_EXIT_INTERRUPT_ERROR = 0x4406,
VMX_IDT_VECTORING_INFO_FIELD = 0x4408,
VMX_IDT_VECTORING_ERROR = 0x440a,
VMX_EXIT_INSTRUCTION_LENGTH = 0x440c,
VMX_EXIT_INSTRUCTION_INFO = 0x440e,
VMX_GUEST_ES_LIMIT = 0x4800,
VMX_GUEST_CS_LIMIT = 0x4802,
VMX_GUEST_SS_LIMIT = 0x4804,
VMX_GUEST_DS_LIMIT = 0x4806,
VMX_GUEST_FS_LIMIT = 0x4808,
VMX_GUEST_GS_LIMIT = 0x480a,
VMX_GUEST_LDTR_LIMIT = 0x480c,
VMX_GUEST_TR_LIMIT = 0x480e,
VMX_GUEST_GDTR_LIMIT = 0x4810,
VMX_GUEST_IDTR_LIMIT = 0x4812,
VMX_GUEST_ES_ACCESS_RIGHTS = 0x4814,
VMX_GUEST_CS_ACCESS_RIGHTS = 0x4816,
VMX_GUEST_SS_ACCESS_RIGHTS = 0x4818,
VMX_GUEST_DS_ACCESS_RIGHTS = 0x481a,
VMX_GUEST_FS_ACCESS_RIGHTS = 0x481c,
VMX_GUEST_GS_ACCESS_RIGHTS = 0x481e,
VMX_GUEST_LDTR_ACCESS_RIGHTS = 0x4820,
VMX_GUEST_TR_ACCESS_RIGHTS = 0x4822,
VMX_GUEST_INTERRUPTIBILITY_STATE = 0x4824,
VMX_GUEST_ACTIVITY_STATE = 0x4826,
VMX_GUEST_SMBASE = 0x4828,
VMX_GUEST_IA32_SYSENTER_CS = 0x482a,
VMX_PREEMPTION_TIMER_VALUE = 0x482e,
VMX_HOST_IA32_SYSENTER_CS = 0x4C00,
};
// natural width
enum
{
VMX_CR0_MASK = 0x6000,
VMX_CR4_MASK = 0x6002,
VMX_CR0_READ_SHADOW = 0x6004,
VMX_CR4_READ_SHADOW = 0x6006,
VMX_CR3_TARGET_VALUE0 = 0x6008,
VMX_CR3_TARGET_VALUE1 = 0x600a,
VMX_CR3_TARGET_VALUE2 = 0x600c,
VMX_CR3_TARGET_VALUE3 = 0x600e,
VMX_EXIT_QUALIFICATION = 0x6400,
VMX_IO_RCX = 0x6402,
VMX_IO_RSI = 0x6404,
VMX_IO_RDI = 0x6406,
VMX_IO_RIP = 0x6408,
VMX_GUEST_LINEAR_ADDR = 0x640a,
VMX_GUEST_CR0 = 0x6800,
VMX_GUEST_CR3 = 0x6802,
VMX_GUEST_CR4 = 0x6804,
VMX_GUEST_ES_BASE = 0x6806,
VMX_GUEST_CS_BASE = 0x6808,
VMX_GUEST_SS_BASE = 0x680a,
VMX_GUEST_DS_BASE = 0x680c,
VMX_GUEST_FS_BASE = 0x680e,
VMX_GUEST_GS_BASE = 0x6810,
VMX_GUEST_LDTR_BASE = 0x6812,
VMX_GUEST_TR_BASE = 0x6814,
VMX_GUEST_GDTR_BASE = 0x6816,
VMX_GUEST_IDTR_BASE = 0x6818,
VMX_GUEST_DR7 = 0x681a,
VMX_GUEST_RSP = 0x681c,
VMX_GUEST_RIP = 0x681e,
VMX_GUEST_RFLAGS = 0x6820,
VMX_GUEST_PENDING_DEBUG_EXCEPTIONS = 0x6822,
VMX_GUEST_SYSENTER_ESP = 0x6824,
VMX_GUEST_SYSENTER_EIP = 0x6826,
VMX_HOST_CR0 = 0x6c00,
VMX_HOST_CR3 = 0x6c02,
VMX_HOST_CR4 = 0x6c04,
VMX_HOST_FS_BASE = 0x6c06,
VMX_HOST_GS_BASE = 0x6c08,
VMX_HOST_TR_BASE = 0x6c0a,
VMX_HOST_GDTR_BASE = 0x6c0c,
VMX_HOST_IDTR_BASE = 0x6c0e,
VMX_HOST_SYSENTER_ESP = 0x6c10,
VMX_HOST_SYSENTER_EIP = 0x6c12,
VMX_HOST_RSP = 0x6c14,
VMX_HOST_RIP = 0x6c16,
};

View File

@@ -0,0 +1,414 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*/
/*
* Author(s): Adam Lackorzynski
* Alexander Warg
* Matthias Lange <mlange@sec.t-labs.tu-berlin.de>
*
* (c) 2008-2012 Technische Universität Dresden
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include "vmx.h"
#include "vmcs.h"
#include <cstdio>
#include <cstdlib>
#include <l4/util/util.h>
#include <l4/re/env>
#include <l4/re/util/cap_alloc>
#include <l4/sys/factory>
#include <l4/sys/vm.h>
#include <l4/util/cpu.h>
#include <l4/sys/thread.h>
#include <l4/sys/kdebug.h>
static bool verbose = false;
Vmx::Vmx()
{
setup_vm();
vmcs = reinterpret_cast<l4_vm_vmx_vcpu_vmcs_t *>(vcpu_ext_state);
printf("# Allocating a hardware VMCS capability: ");
vmcs_cap = L4Re::Util::cap_alloc.alloc<L4::Vcpu_context>();
if (!vmcs_cap.is_valid())
{
printf("Failure.\n");
exit(1);
}
printf("Success.\n");
printf("# Creating a hardware VMCS kernel object: ");
l4_msgtag_t msg = L4Re::Env::env()->factory()->create(vmcs_cap);
if (l4_error(msg))
{
printf("Failure.\n");
exit(1);
}
printf("Success.\n");
l4_vm_vmx_set_hw_vmcs(vmcs, vmcs_cap.cap());
is_vmx = 1;
}
bool
Vmx::cpu_virt_capable()
{
l4_umword_t ax, bx, cx, dx;
if (!l4util_cpu_has_cpuid())
return false;
l4util_cpu_cpuid(0x1, &ax, &bx, &cx, &dx);
if (!(cx & (1 << 5)))
{
printf("# CPU does not support VMX.\n");
return false;
}
return true;
}
void
Vmx::initialize_ext_state(unsigned long_mode)
{
vmwrite(VMX_GUEST_CS_SEL, 0x8);
vmwrite(VMX_GUEST_CS_ACCESS_RIGHTS, 0xd09b);
vmwrite(VMX_GUEST_CS_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_CS_BASE, 0);
vmwrite(VMX_GUEST_SS_SEL, 0x28);
vmwrite(VMX_GUEST_SS_ACCESS_RIGHTS, 0xc093);
vmwrite(VMX_GUEST_SS_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_SS_BASE, 0);
vmwrite(VMX_GUEST_DS_SEL, 0x10);
vmwrite(VMX_GUEST_DS_ACCESS_RIGHTS, 0xc093);
vmwrite(VMX_GUEST_DS_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_DS_BASE, 0);
vmwrite(VMX_GUEST_ES_SEL, 0x0);
vmwrite(VMX_GUEST_ES_ACCESS_RIGHTS, 0x14003);
vmwrite(VMX_GUEST_ES_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_ES_BASE, 0);
vmwrite(VMX_GUEST_FS_SEL, 0x0);
vmwrite(VMX_GUEST_FS_ACCESS_RIGHTS, 0x1c0f3);
vmwrite(VMX_GUEST_FS_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_FS_BASE, 0);
vmwrite(VMX_GUEST_GS_SEL, 0x0);
vmwrite(VMX_GUEST_GS_ACCESS_RIGHTS, 0x1c0f3);
vmwrite(VMX_GUEST_GS_LIMIT, 0xffffffff);
vmwrite(VMX_GUEST_GS_BASE, 0);
vmwrite(VMX_GUEST_GDTR_LIMIT, 0x3f);
vmwrite(VMX_GUEST_GDTR_BASE, Gdt);
vmwrite(VMX_GUEST_LDTR_SEL, 0x0);
vmwrite(VMX_GUEST_LDTR_ACCESS_RIGHTS, 0x10000);
vmwrite(VMX_GUEST_LDTR_LIMIT, 0);
vmwrite(VMX_GUEST_LDTR_BASE, 0);
vmwrite(VMX_GUEST_IDTR_LIMIT, 0x3f);
vmwrite(VMX_GUEST_IDTR_BASE, Idt);
vmwrite(VMX_GUEST_TR_SEL, 0x28);
vmwrite(VMX_GUEST_TR_ACCESS_RIGHTS, 0x108b);
vmwrite(VMX_GUEST_TR_LIMIT, 67);
vmwrite(VMX_GUEST_TR_BASE, 0);
vmwrite(VMX_VMCS_LINK_PTR, 0xffffffffffffffffULL);
enum exceptions
{
de = 1<<0,
db = 1<<1,
bp = 1<<3,
gp = 1<<13,
pf = 1<<14,
ac = 1<<17
};
/* In 32 bits we also want to test for interrupt handling */
if (long_mode)
vmwrite(VMX_EXCEPTION_BITMAP, bp|gp|pf);
else
vmwrite(VMX_EXCEPTION_BITMAP, gp|pf|ac);
vmwrite(VMX_PF_ERROR_CODE_MATCH, 0);
vmwrite(VMX_PF_ERROR_CODE_MASK, 0);
vmwrite(VMX_ENTRY_CTRL, vmread(VMX_ENTRY_CTRL) | (1 << 15)); // load efer
vmwrite(VMX_EXIT_CTRL, vmread(VMX_EXIT_CTRL) | (1 << 20)); // save guest efer
vmwrite(VMX_EXIT_CTRL, vmread(VMX_EXIT_CTRL) | (1 << 21)); // load host efer
vmwrite(VMX_ENTRY_CTRL,
vmread(VMX_ENTRY_CTRL) &~ (1 << 9)); // enable long mode
vmwrite(VMX_PRIMARY_EXEC_CTRL,
vmread(VMX_PRIMARY_EXEC_CTRL) &~ (1 << 23)); // do not intercept dr reads/writes
}
void
Vmx::set_rax(l4_umword_t rax)
{
vcpu->r.ax = rax;
}
void
Vmx::set_rsp(l4_umword_t rsp)
{
vmwrite(VMX_GUEST_RSP, rsp);
}
void
Vmx::set_rflags(l4_umword_t rflags)
{
vmwrite(VMX_GUEST_RFLAGS, rflags);
}
void
Vmx::set_rip(l4_umword_t rip)
{
vmwrite(VMX_GUEST_RIP, rip);
}
void
Vmx::set_cr0(l4_umword_t cr0)
{
vmwrite(VMX_GUEST_CR0, cr0);
}
void
Vmx::set_cr3(l4_umword_t cr3)
{
vmwrite(VMX_GUEST_CR3, cr3);
}
void
Vmx::set_cr4(l4_umword_t cr4)
{
vmwrite(VMX_GUEST_CR4, cr4 | 0x2000); // vmx requires cr4.vmxe
vmwrite(VMX_CR4_READ_SHADOW, cr4 | 0x2000); // let the guest see the real value
}
void
Vmx::set_dr7(l4_umword_t dr7)
{
vmwrite(VMX_GUEST_DR7, dr7);
}
l4_umword_t
Vmx::get_rax()
{
return vcpu->r.ax;
}
void
Vmx::enable_npt()
{}
void
Vmx::disable_npt()
{}
void
Vmx::set_efer(l4_umword_t efer)
{
vmwrite(VMX_GUEST_IA32_EFER, efer & 0xF01);
if (efer & 0x100) // do we want long mode?
vmwrite(VMX_ENTRY_CTRL,
vmread(VMX_ENTRY_CTRL) | (1 << 9)); // enable long mode
// Apparently vmx needs the enable long mode entry control set if the vmm
// switches the guest to long mode. We need to know if the entry_control
// needs to be set if the guest switches to long mode on its own.
}
void
Vmx::jump_over_current_insn(unsigned bytes)
{
l4_umword_t l = vmread_32(VMX_EXIT_INSTRUCTION_LENGTH);
if (bytes)
l = bytes;
l4_umword_t ip = vmread_nat(VMX_GUEST_RIP);
vmwrite(VMX_GUEST_RIP, ip + l);
}
unsigned
Vmx::handle_vmexit()
{
l4_msgtag_t tag;
l4_uint32_t interrupt_info;
l4_uint32_t exit_reason = vmread_32(VMX_EXIT_REASON);
printf("# exit_code=%d rip = 0x%lx rsp = 0x%lx cs = %x ds = %x ss = %x\n",
exit_reason, vmread_nat(VMX_GUEST_RIP), vmread_nat(VMX_GUEST_RSP),
vmread_16(VMX_GUEST_CS_SEL), vmread_16(VMX_GUEST_DS_SEL),
vmread_16(VMX_GUEST_SS_SEL));
switch (exit_reason & 0xffff)
{
case 0:
if (verbose)
printf("# Exception or NMI at guest IP 0x%lx, checking interrupt info\n",
vmread_nat(VMX_GUEST_RIP));
interrupt_info = vmread_32(VMX_EXIT_INTERRUPT_INFO);
// check valid bit
if (!(interrupt_info & (1 << 31)))
printf("# Interrupt info not valid\n");
if (verbose)
printf("# interrupt vector=%d, type=%d, error code valid=%d\n",
(interrupt_info & 0xFF), ((interrupt_info & 0x700) >> 8),
((interrupt_info & 0x800) >> 11));
if ((interrupt_info & 0x800) >> 11) {
unsigned long error = vmread_32(VMX_EXIT_INTERRUPT_ERROR);
printf("# error code: %lx\n", error);
printf("# idt vectoring info field: %x\n",
vmread_32(VMX_IDT_VECTORING_INFO_FIELD));
printf("# idt vectoring error code: %x\n",
vmread_32(VMX_IDT_VECTORING_ERROR));
}
switch ((interrupt_info & 0x700) >> 8)
{
case 0x6:
printf("# Software interrupt\n");
break;
case 0x3:
if (verbose)
printf("# Hardware exception\n");
if ((interrupt_info & 0xff) == 0x6)
{
printf("# undefined instruction\n");
jump_over_current_insn(2);
return 1;
}
else if ((interrupt_info & 0xff) == 0xe)
{
printf("# Pagefault\n");
printf("# EFER = %llx\n", vmread_64(VMX_GUEST_IA32_EFER));
return 0;
}
else if ((interrupt_info & 0xff) == 0x8)
{
printf("# Double fault. VERY BAD.\n");
return 0;
}
else if ((interrupt_info & 0xff) == 17)
{
// this is expected
// make the stack aligned again and restart insn
printf("# Fetched an alignment check exception.\n");
vmwrite(VMX_GUEST_RSP, vmread_nat(VMX_GUEST_RSP) + 3);
return Alignment_check_intercept;
}
break;
case 0x2:
printf("# NMI\n");
return 0;
default:
printf("# Unknown\n");
return 0;
}
if ((interrupt_info & (1 << 11))) // interrupt error code valid?
if (verbose)
printf("# interrupt error=0x%x\n",
vmread_32(VMX_EXIT_INTERRUPT_ERROR));
if (((interrupt_info & 0x700) >> 8) == 3
&& (interrupt_info & 0xff) == 14)
{
if (0)
{
l4_umword_t fault_addr = vmread_nat(VMX_EXIT_QUALIFICATION);
tag = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage(fault_addr & L4_PAGEMASK, L4_PAGESHIFT,
L4_FPAGE_RW),
l4_map_control(fault_addr, 0, L4_MAP_ITEM_MAP));
if (l4_error(tag))
printf("# Error mapping page\n");
}
break;
}
jump_over_current_insn(0);
break;
case 1:
printf("# External interrupt\n");
break;
case 18:
if (verbose)
printf("# vmcall ecx = %ld edx = %lx\n", vcpu->r.cx, vcpu->r.dx);
jump_over_current_insn(0);
return VMCALL;
#if 0
if (vcpu->r.dx == 0x42) {
printf("ok - #dz exception handled - interrupt handling works\n");
}
if (vcpu->r.dx == 0x44) {
printf("#DB exception handled in-guest without involvement of VMM. Must not happen!\n");
test_ok = false;
return 0;
}
if (vcpu->r.dx == 0x50) {
printf("#AC exception handled in-guest without involvement of VMM. Must not happen!\n");
test_ok = false;
return 0;
}
vcpu->r.ax = vcpu->r.cx;
#endif
break;
case 28:
printf("# Control register access.\n");
jump_over_current_insn(0);
return 1;
case 29:
printf("# mov dr procbased_ctls=%llx\n", vmread(VMX_PRIMARY_EXEC_CTRL));
jump_over_current_insn(0);
return 1;
case 31:
printf("# rdmsr\n");
return 0;
case 33:
printf("# Invalid guest state.\n");
return 0;
case 48: // EPT violation
{
printf("# EPT violation\n");
l4_umword_t q = vmread_nat(VMX_EXIT_QUALIFICATION);
printf("# exit qualification: %lx\n", q);
printf("# guest phys = %llx, guest linear: %lx\n",
vmread_64(0x2400), vmread_nat(0x640a));
printf("# guest cr0 = %lx\n",
vmread_nat(VMX_GUEST_CR0));
if (0)
{
l4_umword_t fault_addr = vmread_64(0x2400);
printf("# detected pagefault @ %lx\n", fault_addr);
tag = vm_cap->map(L4Re::Env::env()->task(),
l4_fpage(fault_addr & L4_PAGEMASK,
L4_PAGESHIFT, L4_FPAGE_RWX),
l4_map_control(fault_addr, 0, L4_MAP_ITEM_MAP));
if (l4_error(tag))
printf("# Error mapping page\n");
}
return 0;
}
break;
default:
printf("# Unhandled exit reason %d\n", exit_reason);
return 0;
}
return 1;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2014-2016, 2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* This file is distributed under the terms of the GNU General Public
* License, version 2. Please see the COPYING-GPL-2 file for details.
*/
#pragma once
#include <l4/sys/capability>
#include <l4/sys/types.h>
#include <l4/sys/vcpu_context>
#include "vm.h"
class Vmx: public Vm
{
public:
Vmx();
protected:
bool npt_available() { return true; }
bool cpu_virt_capable();
void initialize_ext_state(unsigned long_mode);
unsigned handle_vmexit();
void set_rax(l4_umword_t rax);
void set_rsp(l4_umword_t rsp);
void set_rflags(l4_umword_t rflags);
void set_rip(l4_umword_t rip);
void set_cr0(l4_umword_t cr0);
void set_cr3(l4_umword_t cr3);
void set_cr4(l4_umword_t cr4);
void set_dr7(l4_umword_t dr7);
void set_efer(l4_umword_t efer);
l4_umword_t get_rax();
void enable_npt();
void disable_npt();
private:
void vmwrite(unsigned int field, l4_uint64_t val)
{
l4_vm_vmx_write(vmcs, field, val);
}
l4_uint64_t vmread(unsigned int field)
{
return l4_vm_vmx_read(vmcs, field);
}
l4_uint16_t vmread_16(unsigned int field)
{
return l4_vm_vmx_read_16(vmcs, field);
}
l4_uint64_t vmread_64(unsigned int field)
{
return l4_vm_vmx_read_64(vmcs, field);
}
l4_uint32_t vmread_32(unsigned int field)
{
return l4_vm_vmx_read_32(vmcs, field);
}
l4_umword_t vmread_nat(unsigned int field)
{
return l4_vm_vmx_read_nat(vmcs, field);
}
void jump_over_current_insn(unsigned bytes);
l4_vm_vmx_vcpu_vmcs_t *vmcs;
L4::Cap<L4::Vcpu_context> vmcs_cap;
};