]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/ex/trap+emulate/vcpu.cc
update
[l4.git] / l4 / pkg / plr / ex / trap+emulate / vcpu.cc
1 /*
2  * vcpu.cc --
3  * 
4  * VCPU test for comparing different strategies of emulating
5  * write accesses to shared memory.
6  *
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.
13  */
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>
21 #include <l4/re/env>
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>
26
27 #include <l4/re/error_helper>
28
29 #include <l4/sys/task>
30 #include <l4/sys/irq>
31 #include <l4/sys/vcpu.h>
32
33 #include <cstdlib>
34 #include <cstdio>
35 #include <cstring>
36 #include <iostream>
37
38 #include "log"
39 #include "exceptions"
40 #include "emulation"
41
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;
45
46 using L4Re::chksys;
47 using L4Re::chkcap;
48
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;
53
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;
58
59 #define DATASIZE (1 << 19)
60 asm
61 (
62   ".p2align 12                      \t\n"
63   ".global my_super_code            \t\n"
64   ".global my_super_code_excp       \t\n"
65   ".global my_super_code_excp_after \t\n"
66   "my_super_code:                   \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
70   "   jnz 1b                        \t\n"
71  // "   sub $4096, %eax               \t\n"
72  // "   loop 1b                       \t\n"
73   "   ud2a                          \t\n"
74 );
75
76
77 static char __attribute__((aligned(4096))) data_area[DATASIZE];
78
79
80 static void setup_user_state_arch(L4vcpu::Vcpu *v)
81 {
82   asm volatile ("mov %%fs, %0" : "=r"(fs));
83   asm volatile ("mov %%ds, %0" : "=r"(ds));
84   v->r()->ss = ds;
85 }
86
87 static void handler_prolog()
88 {
89   asm volatile ("mov %0, %%es \t\n"
90                 "mov %0, %%ds \t\n"
91                 "mov %1, %%fs \t\n"
92                 : : "r"(ds), "r"(fs));
93 }
94
95 static void pf_info(L4vcpu::Vcpu *v)
96 {
97         std::cout << "PF: ";
98         if (v->r()->err & 0x2)
99                 std::cout << "write ";
100         else
101                 std::cout << "read  ";
102         std::cout << "@ " << std::hex << v->r()->pfa;
103         std::cout << std::endl;
104 }
105
106 /*
107  * Option 1: Direct mapping to vCPU task
108  */
109 #define VAR_DIRECT  0
110 static inline void map_direct(L4vcpu::Vcpu *v)
111 {
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),
115                                    v->r()->pfa);
116         v->saved_state()->add(L4_VCPU_F_PAGE_FAULTS);
117 }
118
119 /*
120  * Option 2: Trap & Emulate
121  */
122 #define VAR_EMULATE 0
123
124 struct MyTranslator : public Romain::AddressTranslator
125 {
126         l4_addr_t translate(l4_addr_t a) const
127         {
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
136                 } else {
137                         enter_kdebug("strange address");
138                 }
139                 return ~0U;
140         }
141 };
142
143
144 static inline void emulate_write(L4vcpu::Vcpu *)
145 {
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();
150 }
151
152 /*
153  * Option 3: Single-step instruction locally
154  */
155 #define VAR_STEP    1
156 char instruction_buffer[32];
157 static __attribute__((noinline))
158 void local_singlestep(L4vcpu::Vcpu *v)
159 {
160         l4_addr_t ip = v->r()->ip;
161         ip -= super_code_map_addr;
162         ip += (l4_addr_t)my_super_code;
163
164         ud_t ud;
165         ud_init(&ud);
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);
170
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
174         v->r()->ip += num;
175
176 #if 0
177         for (unsigned i = 0; i < sizeof(instruction_buffer); ++i) {
178                 printf("%02x ", (unsigned char)instruction_buffer[i]);
179         }
180         printf("\n");
181 #endif
182
183         asm volatile ("call *%1"
184                       :
185                       : "A" (v->r()->ax),
186                                     "r"(instruction_buffer));
187 }
188
189 static void handle_pf(L4vcpu::Vcpu *v)
190 {
191         if (0) pf_info(v);
192
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?");
196 #if VAR_DIRECT
197                 map_direct(v);
198 #elif VAR_EMULATE
199                 emulate_write(v);
200 #elif VAR_STEP
201                 local_singlestep(v);
202 #endif
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);
209         }
210 }
211
212
213 static struct timeval tv_start;
214 static struct timeval tv_fault;
215
216 static void handler(void)
217 {
218         handler_prolog();
219
220 #if 0
221         l4_cpu_time_t t = l4_rdtsc();
222         std::cout << std::dec << t << std::endl;
223 #endif
224
225         vcpu->state()->clear(L4_VCPU_F_EXCEPTIONS | L4_VCPU_F_DEBUG_EXC);
226
227         if (0) {
228                 std::cout << "-------------- VCPU Fault --------------" << std::endl;
229                 vcpu->print_state();
230         }
231
232         /*
233          * We only expect to see page faults here.
234          */
235         if (vcpu->is_page_fault_entry()) {
236                 handle_pf(vcpu);
237         } else {
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");
242         }
243
244 #if 0
245         t = l4_rdtsc();
246         std::cout << std::dec << t << std::endl;
247         std::cout << "Resuming @ " << std::hex << vcpu->r()->ip << std::endl;
248 #endif
249         L4::Cap<L4::Thread> self;
250         self->vcpu_resume_commit(self->vcpu_resume_start());
251         while(1)
252                 ;
253 }
254
255 static void vcpu_thread(void)
256 {
257         printf("Hello vCPU\n");
258
259         memset(hdl_stack, 0, sizeof(hdl_stack));
260
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
265                                                          | L4_VCPU_F_IRQ);
266
267         /* 
268          * Protocol issue: EAX contains the address to which the remote
269          * vCPU should write. See ASM code above.
270          */
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
278
279         L4::Cap<L4::Thread> self;
280
281         gettimeofday(&tv_start, NULL);
282         printf("IRET\n");
283
284         vcpu->task(vcpu_task);
285         self->vcpu_resume_commit(self->vcpu_resume_start());
286
287         printf("IRET: failed!\n");
288         while (1)
289                 ;
290 }
291
292 static int run(void)
293 {
294   L4::Cap<L4::Thread> vcpu_cap;
295
296   printf("vCPU example\n");
297
298   l4_debugger_set_object_name(l4re_env()->main_thread, "vcpu_tne");
299
300   // new task
301   vcpu_task = chkcap(L4Re::Util::cap_alloc.alloc<L4::Task>(),
302                      "Task cap alloc");
303
304   chksys(L4Re::Env::env()->factory()->create_task(vcpu_task,
305                                                   l4_fpage_invalid()),
306          "create task");
307   l4_debugger_set_object_name(vcpu_task.cap(), "vcpu 'user' task");
308
309   /* new thread/vCPU */
310   vcpu_cap = chkcap(L4Re::Util::cap_alloc.alloc<L4::Thread>(),
311                     "vCPU cap alloc");
312
313   l4_touch_rw(thread_stack, sizeof(thread_stack));
314
315   chksys(L4Re::Env::env()->factory()->create_thread(vcpu_cap), "create thread");
316   l4_debugger_set_object_name(vcpu_cap.cap(), "vcpu thread");
317
318   // get memory for vCPU state
319   l4_addr_t kumem;
320   if (0)
321     kumem = (l4_addr_t)l4re_env()->first_free_utcb;
322   else
323     {
324       if (L4Re::Util::kumem_alloc(&kumem, 0))
325         exit(1);
326     }
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);
331
332   printf("VCPU: utcb = %p, vcpu = %p\n", vcpu_utcb, vcpu);
333
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");
341
342   chksys(vcpu_cap->ex_regs((l4_umword_t)vcpu_thread,
343                            (l4_umword_t)thread_stack + sizeof(thread_stack),
344                            0));
345
346   chksys(L4Re::Env::env()->scheduler()->run_thread(vcpu_cap,
347                                                    l4_sched_param(2)));
348
349   l4_sleep_forever();
350   return 0;
351 }
352
353 int main()
354 {
355   try { return run(); }
356   catch (L4::Runtime_error &e)
357     {
358       std::cerr << "FATAL uncought exception: " << e.str()
359                << "\nterminating...\n";
360     }
361   catch (...)
362     {
363       std::cerr << "FATAL uncought exception of unknown type\n"
364                << "terminating...\n";
365     }
366   return 1;
367 }