Redesign of our initialization path for libs (XCC)
[akaros.git] / tests / vmm / virtiostress.c
1 #include <stdio.h> 
2 #include <pthread.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <parlib/arch/arch.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <dirent.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ros/syscall.h>
13 #include <sys/mman.h>
14 #include <vmm/virtio.h>
15
16
17 struct u16_pool *id;
18 int *mmap_blob;
19 void *stack;
20 volatile int shared = 0;
21 int mcp = 1;
22 #define V(x, t) (*((volatile t*)(x)))
23 // NOTE: p is both our virtual and guest physical.
24 void *p;
25 int debug = 0;
26 struct virtqueue *head, *consin, *consout;
27 struct page {uint8_t d[4096];};
28 void *ringpages;
29 struct page *datapages;
30 pthread_t *my_threads;
31 void **my_retvals;
32 int nr_threads = 2;
33         char *line, *consline, *outline;
34         struct scatterlist iov[512];
35         unsigned int inlen, outlen, conslen;
36         /* unlike Linux, this shared struct is for both host and guest. */
37 //      struct virtqueue *constoguest = 
38 //              vring_new_virtqueue(0, 512, 8192, 0, inpages, NULL, NULL, "test");
39 struct virtqueue *guesttocons;
40 struct scatterlist io[512];
41 int iter = 1;
42 volatile int done = 0;
43 volatile int cnt;
44 volatile int failed, done, added, badput, badget, bogus;
45 volatile struct page *usedhead;
46 volatile int fstate;
47 volatile int failiter;
48
49 #define MAX_U16_POOL_SZ (1 << 16)
50
51 /* IDS is the stack of 16 bit integers we give out.  TOS is the top of stack -
52  * it is the index of the next slot that can be popped, if there are any.  It's
53  * a u32 so it can be greater than a u16.
54  *
55  * All free slots in ids will be below the TOS, ranging from indexes [0, TOS),
56  * where if TOS == 0, then there are no free slots to push.
57  *
58  * We can hand out u16s in the range [0, 65535].
59  *
60  * The check array is used instead of a bitfield because these architectures
61  * suck at those. */
62
63 struct u16_pool {
64         uint32_t tos;
65         uint16_t *ids;
66         uint8_t *check;
67         int size;
68 };
69
70 struct u16_pool *create_u16_pool(unsigned int num)
71 {
72         struct u16_pool *id;
73         int tot = sizeof(*id) + sizeof(uint16_t) * num + num;
74         /* We could have size be a u16, but this might catch bugs where users
75          * tried to ask for more than 2^16 and had it succeed. */
76         if (num > MAX_U16_POOL_SZ)
77                 return NULL;
78         /* ids and check are alloced and aligned right after the id struct */
79         id = mmap((int*)4096, tot, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0);
80         if (id == MAP_FAILED) {
81                 perror("create_u16_pool: Unable to mmap");
82                 exit(1);
83         }
84
85         id->size = num;
86         id->ids = (void *)&id[1];
87         id->check = (void *)&id->ids[id->size];
88         for (int i = 0; i < id->size; i++) {
89                 id->ids[i] = i;
90                 // fe rhymes with "free"
91                 id->check[i] = 0xfe;
92         }
93         id->tos = 0;
94         return id;
95 }
96
97 /* Returns an unused u16, or -1 on failure (pool full or corruption).
98  *
99  * The invariant is that the stackpointer (TOS) will always point to the next
100  * slot that can be popped, if there are any.  All free slots will be below the
101  * TOS, ranging from indexes [0, TOS), where if TOS == 0, then there are no free
102  * slots to push.  The last valid slot is when TOS == size - 1. */
103 int get_u16(struct u16_pool *id)
104 {
105         uint16_t v;
106
107         if (id->tos == id->size) {
108                 return -1;
109         }
110         v = id->ids[id->tos++];
111         /* v is ours, we can freely read and write its check field */
112         if (id->check[v] != 0xfe) {
113                 badget++;
114                 return -1;
115         }
116         id->check[v] = 0x5a;
117         return v;
118 }
119
120 void put_u16(struct u16_pool *id, int v)
121 {
122         /* we could check for if v is in range before dereferencing. */
123         if (id->check[v] != 0x5a) {
124                 badput;
125                 return;
126         }
127         id->check[v] = 0xfe;
128         id->ids[--id->tos] = v;
129 }
130
131 static void *fail(void *arg)
132 {
133         uint16_t head = 0;
134
135         int i, j, v, ret;
136
137         for(i = 0; i < 4096;) {
138                 /* get all the free ones you can, and add them all */
139                 fstate = 1;
140                 for(cnt = 0, v = get_u16(id); v >= 0; v = get_u16(id), cnt++) {
141                         failiter++;
142                         added++;
143                         /* 1:1 mapping of iovs to data pages for now */
144                         io[cnt].v = &datapages[v];
145                 }
146
147                 fstate++;
148                 if (! cnt)
149                         continue;
150                 if (virtqueue_add_inbuf_avail(guesttocons, io, cnt, datapages, 0)) {
151                         failed = 1;
152                         break;
153                 }
154                 fstate++;
155
156                 while (cnt > 0) {
157                         if ((usedhead = virtqueue_get_buf_used(guesttocons, &conslen))) {
158                                 if (usedhead != datapages) { failed = 1; goto verybad;}
159                                 for(j = 0; j < conslen; j++) {          
160                                         int idx = (struct page *)io[j].v - usedhead;
161                                         put_u16(id, idx);
162                                         cnt--;
163                                 }
164                         } else bogus++;
165                 }
166                 i++;
167                 fstate++;
168         }
169
170         done = 1;
171 verybad:
172         __asm__ __volatile__("vmcall");
173 }
174
175 unsigned long long *p512, *p1, *p2m;
176
177 void *talk_thread(void *arg)
178 {
179         fprintf(stderr, "talk thread ..\n");
180         uint16_t head;
181         int i;
182         int num;
183         int tot = 0;
184         for(;;) {
185                 /* host: use any buffers we should have been sent. */
186                 head = wait_for_vq_desc(guesttocons, iov, &outlen, &inlen);
187                 if (debug)
188                         printf("vq desc head %d\n", head);
189                 if ((outlen == 0) && (inlen == 0)) // EOF
190                         break;
191                 tot += outlen + inlen;
192                 for(i = 0; debug && i < outlen + inlen; i++)
193                         printf("v[%d/%d] v %p len %d\n", i, outlen + inlen, iov[i].v, iov[i].length);
194
195                 if (debug) printf("outlen is %d; inlen is %d\n", outlen, inlen);
196                 if (0)
197                 { printf("BEFORE ADD USED cnt %d conslen %d badget %d badput %d usedhead %p bogus %d\n", cnt, conslen, badget, badput, usedhead, bogus); showvq(guesttocons); getchar();}
198                 /* host: now ack that we used them all. */
199                 add_used(guesttocons, head, outlen+inlen);
200                 if (0)
201                 { printf("cnt %d conslen %d badget %d badput %d usedhead %p bogus %d\n", cnt, conslen, badget, badput, usedhead, bogus); showvq(guesttocons); getchar();} 
202                 if (debug)
203                         printf("LOOP fstate %d \n", fstate);
204         }
205         fprintf(stderr, "All done, tot %d, failed %d, failiter %d\n", tot, failed, failiter);
206         return NULL;
207 }
208
209 int main(int argc, char **argv)
210 {
211         int nr_gpcs = 1;
212         int fd = open("#c/sysctl", O_RDWR), ret;
213         void * x;
214         static char cmd[512];
215         debug = argc > 1;
216         if (fd < 0) {
217                 perror("#c/sysctl");
218                 exit(1);
219         }
220
221         if (ros_syscall(SYS_setup_vmm, nr_gpcs, 0, 0, 0, 0, 0) != nr_gpcs) {
222                 perror("Guest pcore setup failed");
223                 exit(1);
224         }
225         /* blob that is faulted in from the EPT first.  we need this to be in low
226          * memory (not above the normal mmap_break), so the EPT can look it up.
227          * Note that we won't get 4096.  The min is 1MB now, and ld is there. */
228         mmap_blob = mmap((int*)4096, PGSIZE, PROT_READ | PROT_WRITE,
229                          MAP_ANONYMOUS, -1, 0);
230         if (mmap_blob == MAP_FAILED) {
231                 perror("Unable to mmap");
232                 exit(1);
233         }
234
235
236         p512 = mmap((int*)4096, (3 + 256 + 512)*4096, PROT_READ | PROT_WRITE,
237                          MAP_ANONYMOUS, -1, 0);
238         if (ringpages == MAP_FAILED) {
239                 perror("Unable to mmap");
240                 exit(1);
241         }
242         ringpages = p512 + 3*4096;
243         datapages = ringpages + 256*4096;
244         
245         stack = mmap((int*)4096, 8192, PROT_READ | PROT_WRITE,
246                          MAP_ANONYMOUS, -1, 0);
247         if (stack == MAP_FAILED) {
248                 perror("Unable to mmap");
249                 exit(1);
250         }
251
252         my_threads = calloc(sizeof(pthread_t) , nr_threads);
253         my_retvals = calloc(sizeof(void *) , nr_threads);
254         if (!(my_retvals && my_threads))
255                 perror("Init threads/malloc");
256
257         pthread_mcp_init();     /* gives us one vcore */
258         vcore_request(nr_threads - 1);  /* ghetto incremental interface */
259         for (int i = 0; i < nr_threads; i++) {
260                 x = __procinfo.vcoremap;
261                 fprintf(stderr, "%p\n", __procinfo.vcoremap);
262                 fprintf(stderr, "Vcore %d mapped to pcore %d\n", i,
263                            __procinfo.vcoremap[i].pcoreid);
264         }
265         id = create_u16_pool(512); //1<<16);
266
267         guesttocons = vring_new_virtqueue(0, 512, 8192, 0, ringpages, NULL, NULL, "test");
268         fprintf(stderr, "guesttocons is %p\n", guesttocons);
269
270         if (mcp) {
271                 if (pthread_create(&my_threads[0], NULL, &talk_thread, NULL))
272                         perror("pth_create failed");
273 //      if (pthread_create(&my_threads[1], NULL, &fail, NULL))
274 //          perror("pth_create failed");
275         }
276         fprintf(stderr, "threads started\n");
277         
278         ret = syscall(33, 1);
279         if (ret < 0) {
280                 perror("vm setup");
281                 exit(1);
282         }
283
284         p1 = &p512[512];
285         p2m = &p512[1024];
286         p512[0] = (unsigned long long)p1 | 7;
287         p1[0] = /*0x87; */ (unsigned long long)p2m | 7;
288         p2m[0] = 0x87;
289         p2m[1] = 0x200000 | 0x87;
290         p2m[2] = 0x400000 | 0x87;
291         p2m[3] = 0x600000 | 0x87;
292         
293         fprintf(stderr, "p512 %p p512[0] is 0x%lx p1 %p p1[0] is 0x%x\n", p512, p512[0], p1,
294                p1[0]);
295         sprintf(cmd, "V 0x%x 0x%x 0x%x", (unsigned long long)fail,
296                 (unsigned long long)stack+8192, (unsigned long long)p512);
297         showvq(guesttocons);
298         //showdesc(guesttocons, 0);
299         if (debug)
300                 fprintf(stderr, "Writing command :%s:\n", cmd);
301         ret = write(fd, cmd, strlen(cmd));
302         if (ret != strlen(cmd)) {
303                 perror(cmd);
304         }
305         sprintf(cmd, "V 0 0 0");
306         while (! done && ! failed) {
307                 if (debug)
308                         fprintf(stderr, "RESUME\n");
309                 ret = write(fd, cmd, strlen(cmd));
310                 if (ret != strlen(cmd)) {
311                         perror(cmd);
312                 }
313         }
314
315         printf("VM DONE, done %d failed %d\n", done, failed);
316         virtqueue_close(guesttocons);
317         if (debug)
318                 fprintf(stderr, "shared is %d\n", shared);
319
320         for (int i = 0; i < nr_threads - 1; i++) {
321                 int ret;
322                 if (pthread_join(my_threads[i], &my_retvals[i]))
323                         perror("pth_join failed");
324                 fprintf(stderr, "%d %d\n", i, ret);
325         }
326         
327         return 0;
328 }