4 * VCPU test for comparing different strategies of emulating
5 * write accesses to shared memory.
7 * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
8 * Adam Lackorzynski <adam@os.inf.tu-dresden.de>
9 * economic rights: Technische Universität Dresden (Germany)
10 * This file is part of TUD:OS and distributed under the terms of the
11 * GNU General Public License 2.
12 * Please see the COPYING-GPL-2 file for details.
14 #include <l4/sys/thread>
15 #include <l4/sys/factory>
16 #include <l4/sys/scheduler>
17 #include <l4/sys/utcb.h>
18 #include <l4/sys/kdebug.h>
19 #include <l4/util/util.h>
20 #include <l4/util/rdtsc.h>
22 #include <l4/re/util/cap_alloc>
23 #include <l4/re/util/kumem_alloc>
24 #include <l4/sys/debugger.h>
25 #include <l4/vcpu/vcpu>
27 #include <l4/re/error_helper>
29 #include <l4/sys/task>
31 #include <l4/sys/vcpu.h>
42 Romain::Log::LogLevel Romain::Log::maxLog = Romain::Log::INFO;
43 l4_umword_t Romain::Log::logFlags = Romain::Log::None;
44 bool Romain::Log::withtime = true;
49 static char thread_stack[8 << 10];
50 static char hdl_stack[8 << 10];
51 static L4::Cap<L4::Task> vcpu_task;
52 static L4vcpu::Vcpu *vcpu;
54 const l4_addr_t super_code_map_addr = 0x10000;
55 extern char my_super_code[];
56 static unsigned long fs;
57 static unsigned long ds;
59 #define DATASIZE (1 << 19)
63 ".global my_super_code \t\n"
64 ".global my_super_code_excp \t\n"
65 ".global my_super_code_excp_after \t\n"
67 "1: movl $0xDEADBEEF, (%eax) \t\n" // write word 0xdeadbeef to *eax
68 " add $4, %eax \t\n" // eax += 4
69 " cmp %eax, %ebx \t\n" // break on limit
71 // " sub $4096, %eax \t\n"
77 static char __attribute__((aligned(4096))) data_area[DATASIZE];
80 static void setup_user_state_arch(L4vcpu::Vcpu *v)
82 asm volatile ("mov %%fs, %0" : "=r"(fs));
83 asm volatile ("mov %%ds, %0" : "=r"(ds));
87 static void handler_prolog()
89 asm volatile ("mov %0, %%es \t\n"
92 : : "r"(ds), "r"(fs));
95 static void pf_info(L4vcpu::Vcpu *v)
98 if (v->r()->err & 0x2)
99 std::cout << "write ";
101 std::cout << "read ";
102 std::cout << "@ " << std::hex << v->r()->pfa;
103 std::cout << std::endl;
107 * Option 1: Direct mapping to vCPU task
110 static inline void map_direct(L4vcpu::Vcpu *v)
112 std::cout << "Mapping data" << std::endl;
113 vcpu_task->map(L4Re::This_task, l4_fpage((l4_addr_t)data_area,
114 L4_PAGESHIFT << 2, L4_FPAGE_RWX),
116 v->saved_state()->add(L4_VCPU_F_PAGE_FAULTS);
120 * Option 2: Trap & Emulate
122 #define VAR_EMULATE 0
124 struct MyTranslator : public Romain::AddressTranslator
126 l4_addr_t translate(l4_addr_t a) const
128 DEBUG() << "TRANSLATE: " << std::hex << a;
129 if ((a >= super_code_map_addr) &&
130 (a < super_code_map_addr + L4_PAGESIZE)) {
131 unsigned ip = a - super_code_map_addr;
132 return (l4_addr_t)my_super_code + ip;
133 } else if ((a >= (l4_addr_t)data_area) &&
134 (a < (l4_addr_t)data_area + DATASIZE)) {
135 return a; // data mapped identically
137 enter_kdebug("strange address");
144 static inline void emulate_write(L4vcpu::Vcpu *)
146 //unsigned ip = v->r()->ip - super_code_map_addr;
147 //Romain::InstructionPrinter((l4_addr_t)my_super_code + ip, v->r()->ip);
148 static MyTranslator t;
149 Romain::WriteEmulator(vcpu, &t).emulate();
153 * Option 3: Single-step instruction locally
156 char instruction_buffer[32];
157 static __attribute__((noinline))
158 void local_singlestep(L4vcpu::Vcpu *v)
160 l4_addr_t ip = v->r()->ip;
161 ip -= super_code_map_addr;
162 ip += (l4_addr_t)my_super_code;
166 ud_set_mode(&ud, 32);
167 ud_set_pc(&ud, v->r()->ip);
168 ud_set_input_buffer(&ud, (unsigned char*)ip, 32);
169 int num = ud_disassemble(&ud);
171 //memset(instruction_buffer, 0x90, 32);
172 memcpy(instruction_buffer, v->r()->ip - super_code_map_addr + my_super_code, num);
173 instruction_buffer[num] = 0xc3; // RET
177 for (unsigned i = 0; i < sizeof(instruction_buffer); ++i) {
178 printf("%02x ", (unsigned char)instruction_buffer[i]);
183 asm volatile ("call *%1"
186 "r"(instruction_buffer));
189 static void handle_pf(L4vcpu::Vcpu *v)
193 if (v->r()->err & 0x2) {
194 _check(v->r()->pfa < (l4_addr_t)data_area, "outside data area?");
195 _check(v->r()->pfa >= ((l4_addr_t)data_area + DATASIZE), "outside data area?");
203 } else if (v->r()->pfa == super_code_map_addr) {
204 std::cout << "Mapping code" << std::endl;
205 vcpu_task->map(L4Re::This_task, l4_fpage((l4_addr_t)my_super_code,
206 L4_PAGESHIFT, L4_FPAGE_RWX),
207 super_code_map_addr);
208 v->saved_state()->add(L4_VCPU_F_PAGE_FAULTS);
213 static struct timeval tv_start;
214 static struct timeval tv_fault;
216 static void handler(void)
221 l4_cpu_time_t t = l4_rdtsc();
222 std::cout << std::dec << t << std::endl;
225 vcpu->state()->clear(L4_VCPU_F_EXCEPTIONS | L4_VCPU_F_DEBUG_EXC);
228 std::cout << "-------------- VCPU Fault --------------" << std::endl;
233 * We only expect to see page faults here.
235 if (vcpu->is_page_fault_entry()) {
238 gettimeofday(&tv_fault, NULL);
239 printf("[1] %03ld.%06ld\n", tv_start.tv_sec, tv_start.tv_usec);
240 printf("[2] %03ld.%06ld\n", tv_fault.tv_sec, tv_fault.tv_usec);
241 enter_kdebug("fault");
246 std::cout << std::dec << t << std::endl;
247 std::cout << "Resuming @ " << std::hex << vcpu->r()->ip << std::endl;
249 L4::Cap<L4::Thread> self;
250 self->vcpu_resume_commit(self->vcpu_resume_start());
255 static void vcpu_thread(void)
257 printf("Hello vCPU\n");
259 memset(hdl_stack, 0, sizeof(hdl_stack));
261 setup_user_state_arch(vcpu);
262 vcpu->saved_state()->set(L4_VCPU_F_USER_MODE
263 | L4_VCPU_F_EXCEPTIONS
264 | L4_VCPU_F_PAGE_FAULTS
268 * Protocol issue: EAX contains the address to which the remote
269 * vCPU should write. See ASM code above.
271 memset(data_area, 0, sizeof(data_area));
272 printf("data %p %d\n", data_area, sizeof(data_area));
273 vcpu->r()->ax = (l4_umword_t)data_area;
274 vcpu->r()->bx = (l4_umword_t)data_area + DATASIZE;
275 vcpu->r()->ip = super_code_map_addr;
276 vcpu->r()->sp = 0x40000; // actually doesn't matter, we're not using any
277 // stack memory in our code
279 L4::Cap<L4::Thread> self;
281 gettimeofday(&tv_start, NULL);
284 vcpu->task(vcpu_task);
285 self->vcpu_resume_commit(self->vcpu_resume_start());
287 printf("IRET: failed!\n");
294 L4::Cap<L4::Thread> vcpu_cap;
296 printf("vCPU example\n");
298 l4_debugger_set_object_name(l4re_env()->main_thread, "vcpu_tne");
301 vcpu_task = chkcap(L4Re::Util::cap_alloc.alloc<L4::Task>(),
304 chksys(L4Re::Env::env()->factory()->create_task(vcpu_task,
307 l4_debugger_set_object_name(vcpu_task.cap(), "vcpu 'user' task");
309 /* new thread/vCPU */
310 vcpu_cap = chkcap(L4Re::Util::cap_alloc.alloc<L4::Thread>(),
313 l4_touch_rw(thread_stack, sizeof(thread_stack));
315 chksys(L4Re::Env::env()->factory()->create_thread(vcpu_cap), "create thread");
316 l4_debugger_set_object_name(vcpu_cap.cap(), "vcpu thread");
318 // get memory for vCPU state
321 kumem = (l4_addr_t)l4re_env()->first_free_utcb;
324 if (L4Re::Util::kumem_alloc(&kumem, 0))
327 l4_utcb_t *vcpu_utcb = (l4_utcb_t *)kumem;
328 vcpu = L4vcpu::Vcpu::cast(kumem + L4_UTCB_OFFSET);
329 vcpu->entry_sp((l4_umword_t)hdl_stack + sizeof(hdl_stack));
330 vcpu->entry_ip((l4_umword_t)handler);
332 printf("VCPU: utcb = %p, vcpu = %p\n", vcpu_utcb, vcpu);
334 // Create and start vCPU thread
335 L4::Thread::Attr attr;
336 attr.pager(L4::cap_reinterpret_cast<L4::Thread>(L4Re::Env::env()->rm()));
337 attr.exc_handler(L4Re::Env::env()->main_thread());
338 attr.bind(vcpu_utcb, L4Re::This_task);
339 chksys(vcpu_cap->control(attr), "control");
340 chksys(vcpu_cap->vcpu_control((l4_addr_t)vcpu), "enable VCPU");
342 chksys(vcpu_cap->ex_regs((l4_umword_t)vcpu_thread,
343 (l4_umword_t)thread_stack + sizeof(thread_stack),
346 chksys(L4Re::Env::env()->scheduler()->run_thread(vcpu_cap,
355 try { return run(); }
356 catch (L4::Runtime_error &e)
358 std::cerr << "FATAL uncought exception: " << e.str()
359 << "\nterminating...\n";
363 std::cerr << "FATAL uncought exception of unknown type\n"
364 << "terminating...\n";