1 /*
2 * Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <lk/debug.h>
10 #include <lk/err.h>
11 #include <rand.h>
12 #include <string.h>
13 #include <lk/trace.h>
14
15 #include <kernel/event.h>
16 #include <kernel/port.h>
17 #include <kernel/thread.h>
18
19 #include <platform.h>
20
21 #include <app/tests.h>
22
23 #define LOCAL_TRACE 0
24
25 void *context1 = (void *) 0x53;
26
dump_port_result(const port_result_t * result)27 static void dump_port_result(const port_result_t *result) {
28 const port_packet_t *p = &result->packet;
29 LTRACEF("[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
30 p->value[0], p->value[1], p->value[2], p->value[3],
31 p->value[4], p->value[5], p->value[6], p->value[7]);
32 }
33
single_thread_basic(void)34 static int single_thread_basic(void) {
35 port_t w_port;
36 status_t st = port_create("sh_prt1", PORT_MODE_UNICAST, &w_port);
37 if (st < 0) {
38 printf("could not create port, status = %d\n", st);
39 return __LINE__;
40 }
41
42 port_t r_port;
43 st = port_open("sh_prt0", context1, &r_port);
44 if (st != ERR_NOT_FOUND) {
45 printf("expected not to find port, status = %d\n", st);
46 return __LINE__;
47 }
48
49 st = port_open("sh_prt1", context1, &r_port);
50 if (st < 0) {
51 printf("could not open port, status = %d\n", st);
52 return __LINE__;
53 }
54
55 port_packet_t packet[3] = {
56 {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}},
57 {{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11}},
58 {{0x33, 0x66, 0x99, 0xcc, 0x33, 0x66, 0x99, 0xcc}},
59 };
60
61 st = port_write(w_port, &packet[0], 1);
62 if (st < 0) {
63 printf("could not write port, status = %d\n", st);
64 return __LINE__;
65 }
66
67 printf("reading from port:\n");
68
69 port_result_t res = {0};
70
71 st = port_read(r_port, 0, &res);
72 if (st < 0) {
73 printf("could not read port, status = %d\n", st);
74 return __LINE__;
75 }
76 if (res.ctx != context1) {
77 printf("bad context! = %p\n", res.ctx);
78 return __LINE__;
79 }
80
81 st = port_read(r_port, 0, &res);
82 if (st != ERR_TIMED_OUT) {
83 printf("expected timeout, status = %d\n", st);
84 return __LINE__;
85 }
86
87 st = port_write(w_port, &packet[1], 1);
88 if (st < 0) {
89 printf("could not write port, status = %d\n", st);
90 return __LINE__;
91 }
92
93 st = port_write(w_port, &packet[0], 1);
94 if (st < 0) {
95 printf("could not write port, status = %d\n", st);
96 return __LINE__;
97 }
98
99 st = port_write(w_port, &packet[2], 1);
100 if (st < 0) {
101 printf("could not write port, status = %d\n", st);
102 return __LINE__;
103 }
104
105 int expected_count = 3;
106 while (true) {
107 st = port_read(r_port, 0, &res);
108 if (st < 0)
109 break;
110 dump_port_result(&res);
111 --expected_count;
112 }
113
114 if (expected_count != 0) {
115 printf("invalid read count = %d\n", expected_count);
116 return __LINE__;
117 }
118
119 printf("\n");
120
121 // port should be empty. should be able to write 8 packets.
122 expected_count = 8;
123 while (true) {
124 st = port_write(w_port, &packet[1], 1);
125 if (st < 0)
126 break;
127 --expected_count;
128 st = port_write(w_port, &packet[2], 1);
129 if (st < 0)
130 break;
131 --expected_count;
132 }
133
134 if (expected_count != 0) {
135 printf("invalid write count = %d\n", expected_count);
136 return __LINE__;
137 }
138
139 // tod(cpu) fix this possibly wrong error.
140 if (st != ERR_PARTIAL_WRITE) {
141 printf("expected buffer error, status =%d\n", st);
142 return __LINE__;
143 }
144
145 // read 3 packets.
146 for (int ix = 0; ix != 3; ++ix) {
147 st = port_read(r_port, 0, &res);
148 if (st < 0) {
149 printf("could not read port, status = %d\n", st);
150 return __LINE__;
151 }
152 }
153
154 // there are 5 packets, now we add another 3.
155 st = port_write(w_port, packet, 3);
156 if (st < 0) {
157 printf("could not write port, status = %d\n", st);
158 return __LINE__;
159 }
160
161 expected_count = 8;
162 while (true) {
163 st = port_read(r_port, 0, &res);
164 if (st < 0)
165 break;
166 dump_port_result(&res);
167 --expected_count;
168 }
169
170 if (expected_count != 0) {
171 printf("invalid read count = %d\n", expected_count);
172 return __LINE__;
173 }
174
175 // attempt to use the wrong port.
176 st = port_write(r_port, &packet[1], 1);
177 if (st != ERR_BAD_HANDLE) {
178 printf("expected bad handle error, status = %d\n", st);
179 return __LINE__;
180 }
181
182 st = port_read(w_port, 0, &res);
183 if (st != ERR_BAD_HANDLE) {
184 printf("expected bad handle error, status = %d\n", st);
185 return __LINE__;
186 }
187
188 st = port_close(r_port);
189 if (st < 0) {
190 printf("could not close read port, status = %d\n", st);
191 return __LINE__;
192 }
193
194 st = port_close(w_port);
195 if (st < 0) {
196 printf("could not close write port, status = %d\n", st);
197 return __LINE__;
198 }
199
200 st = port_close(r_port);
201 if (st != ERR_BAD_HANDLE) {
202 printf("expected bad handle error, status = %d\n", st);
203 return __LINE__;
204 }
205
206 st = port_close(w_port);
207 if (st != ERR_BAD_HANDLE) {
208 printf("expected bad handle error, status = %d\n", st);
209 return __LINE__;
210 }
211
212 st = port_destroy(w_port);
213 if (st < 0) {
214 printf("could not destroy port, status = %d\n", st);
215 return __LINE__;
216 }
217
218 printf("single_thread_basic : ok\n");
219 return 0;
220 }
221
ping_pong_thread(void * arg)222 static int ping_pong_thread(void *arg) {
223 port_t r_port;
224 status_t st = port_open("ping_port", NULL, &r_port);
225 if (st < 0) {
226 printf("thread: could not open port, status = %d\n", st);
227 return __LINE__;
228 }
229
230 bool should_dispose_pong_port = true;
231 port_t w_port;
232 st = port_create("pong_port", PORT_MODE_UNICAST, &w_port);
233 if (st == ERR_ALREADY_EXISTS) {
234 // won the race to create the port.
235 should_dispose_pong_port = false;
236 } else if (st < 0) {
237 printf("thread: could not open port, status = %d\n", st);
238 return __LINE__;
239 }
240
241 port_result_t pr;
242
243 // the loop is read-mutate-write until the write port
244 // is closed by the master thread.
245 while (true) {
246 st = port_read(r_port, INFINITE_TIME, &pr);
247
248 if (st == ERR_CANCELLED) {
249 break;
250 } else if (st < 0) {
251 printf("thread: could not read port, status = %d\n", st);
252 return __LINE__;
253 }
254
255 pr.packet.value[0]++;
256 pr.packet.value[5]--;
257
258 st = port_write(w_port, &pr.packet, 1);
259 if (st < 0) {
260 printf("thread: could not write port, status = %d\n", st);
261 return __LINE__;
262 }
263 }
264
265 port_close(r_port);
266
267 if (should_dispose_pong_port) {
268 port_close(w_port);
269 port_destroy(w_port);
270 }
271
272 return 0;
273
274 bail:
275 return __LINE__;
276 }
277
278
two_threads_basic(void)279 static int two_threads_basic(void) {
280 port_t w_port;
281 status_t st = port_create("ping_port", PORT_MODE_BROADCAST, &w_port);
282 if (st < 0) {
283 printf("could not create port, status = %d\n", st);
284 return __LINE__;
285 }
286
287 thread_t *t1 = thread_create(
288 "worker1", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
289 thread_t *t2 = thread_create(
290 "worker2", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
291 thread_resume(t1);
292 thread_resume(t2);
293
294 // wait for the pong port to be created, the two threads race to do it.
295 port_t r_port;
296 while (true) {
297 st = port_open("pong_port", NULL, &r_port);
298 if (st == NO_ERROR) {
299 break;
300 } else if (st == ERR_NOT_FOUND) {
301 thread_sleep(100);
302 } else {
303 printf("could not open port, status = %d\n", st);
304 return __LINE__;
305 }
306 }
307
308 // We have two threads listening to the ping port. Which both reply
309 // on the pong port, so we get two packets in per packet out.
310 const int passes = 256;
311 printf("two_threads_basic test, %d passes\n", passes);
312
313 port_packet_t packet_out = {{0xaf, 0x77, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
314
315 port_result_t pr;
316 for (int ix = 0; ix != passes; ++ix) {
317 const size_t count = 1 + ((unsigned int)rand() % 3);
318
319 for (size_t jx = 0; jx != count; ++jx) {
320 st = port_write(w_port, &packet_out, 1);
321 if (st < 0) {
322 printf("could not write port, status = %d\n", st);
323 return __LINE__;
324 }
325 }
326
327 packet_out.value[0]++;
328 packet_out.value[5]--;
329
330 for (size_t jx = 0; jx != count * 2; ++jx) {
331 st = port_read(r_port, INFINITE_TIME, &pr);
332 if (st < 0) {
333 printf("could not read port, status = %d\n", st);
334 return __LINE__;
335 }
336
337 if ((pr.packet.value[0] != packet_out.value[0]) ||
338 (pr.packet.value[5] != packet_out.value[5])) {
339 printf("unexpected data in packet, loop %d", ix);
340 return __LINE__;
341 }
342 }
343 }
344
345 thread_sleep(100);
346
347 // there should be no more packets to read.
348 st = port_read(r_port, 0, &pr);
349 if (st != ERR_TIMED_OUT) {
350 printf("unexpected packet, status = %d\n", st);
351 return __LINE__;
352 }
353
354 printf("two_threads_basic master shutdown\n");
355
356 st = port_close(r_port);
357 if (st < 0) {
358 printf("could not close port, status = %d\n", st);
359 return __LINE__;
360 }
361
362 st = port_close(w_port);
363 if (st < 0) {
364 printf("could not close port, status = %d\n", st);
365 return __LINE__;
366 }
367
368 st = port_destroy(w_port);
369 if (st < 0) {
370 printf("could not destroy port, status = %d\n", st);
371 return __LINE__;
372 }
373
374 int retcode = -1;
375 thread_join(t1, &retcode, INFINITE_TIME);
376 if (retcode)
377 goto fail;
378
379 thread_join(t2, &retcode, INFINITE_TIME);
380 if (retcode)
381 goto fail;
382
383 return 0;
384
385 fail:
386 printf("child thread exited with %d\n", retcode);
387 return __LINE__;
388 }
389
390 #define CMD_PORT_CTX ((void*) 0x77)
391 #define TS1_PORT_CTX ((void*) 0x11)
392 #define TS2_PORT_CTX ((void*) 0x12)
393
394 typedef enum {
395 ADD_PORT,
396 QUIT
397 } action_t;
398
399 typedef struct {
400 action_t what;
401 port_t port;
402 } watcher_cmd;
403
send_watcher_cmd(port_t cmd_port,action_t action,port_t port)404 static status_t send_watcher_cmd(port_t cmd_port, action_t action, port_t port) {
405 watcher_cmd _cmd = {action, port};
406 return port_write(cmd_port, ((port_packet_t *) &_cmd), 1);;
407 }
408
group_watcher_thread(void * arg)409 static int group_watcher_thread(void *arg) {
410 port_t watched[8] = {0};
411 status_t st = port_open("grp_ctrl", CMD_PORT_CTX, &watched[0]);
412 if (st < 0) {
413 printf("could not open port, status = %d\n", st);
414 return __LINE__;
415 }
416
417 size_t count = 1;
418 port_t group;
419 int ctx_count = -1;
420
421 while (true) {
422 st = port_group(watched, count, &group);
423 if (st < 0) {
424 printf("could not make group, status = %d\n", st);
425 return __LINE__;
426 }
427
428 port_result_t pr;
429 while (true) {
430 st = port_read(group, INFINITE_TIME, &pr);
431 if (st < 0) {
432 printf("could not read port, status = %d\n", st);
433 return __LINE__;
434 }
435
436 if (pr.ctx == CMD_PORT_CTX) {
437 break;
438 } else if (pr.ctx == TS1_PORT_CTX) {
439 ctx_count += 1;
440 } else if (pr.ctx == TS2_PORT_CTX) {
441 ctx_count += 2;
442 } else {
443 printf("unknown context %p\n", pr.ctx);
444 return __LINE__;
445 }
446 }
447
448 // Either adding a port or exiting; either way close the
449 // existing group port and create a new one if needed
450 // at the top of the loop.
451
452 port_close(group);
453 watcher_cmd *wc = (watcher_cmd *) &pr.packet;
454
455 if (wc->what == ADD_PORT) {
456 watched[count++] = wc->port;
457 } else if (wc->what == QUIT) {
458 break;
459 } else {
460 printf("unknown command %d\n", wc->what);
461 return __LINE__;
462 }
463 }
464
465 if (ctx_count != 2) {
466 printf("unexpected context count %d", ctx_count);
467 return __LINE__;
468 }
469
470 printf("group watcher shutdown\n");
471
472 for (size_t ix = 0; ix != count; ++ix) {
473 st = port_close(watched[ix]);
474 if (st < 0) {
475 printf("failed to close read port, status = %d\n", st);
476 return __LINE__;
477 }
478 }
479
480 return 0;
481 }
482
make_port_pair(const char * name,void * ctx,port_t * write,port_t * read)483 static status_t make_port_pair(const char *name, void *ctx, port_t *write, port_t *read) {
484 status_t st = port_create(name, PORT_MODE_UNICAST, write);
485 if (st < 0)
486 return st;
487 return port_open(name,ctx, read);
488 }
489
group_basic(void)490 static int group_basic(void) {
491 // we spin a thread that connects to a well known port, then we
492 // send two ports that it will add to a group port.
493 port_t cmd_port;
494 status_t st = port_create("grp_ctrl", PORT_MODE_UNICAST, &cmd_port);
495 if (st < 0 ) {
496 printf("could not create port, status = %d\n", st);
497 return __LINE__;
498 }
499
500 thread_t *wt = thread_create(
501 "g_watcher", &group_watcher_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
502 thread_resume(wt);
503
504 port_t w_test_port1, r_test_port1;
505 st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
506 if (st < 0)
507 return __LINE__;
508
509 port_t w_test_port2, r_test_port2;
510 st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
511 if (st < 0)
512 return __LINE__;
513
514 st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port1);
515 if (st < 0)
516 return __LINE__;
517
518 st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port2);
519 if (st < 0)
520 return __LINE__;
521
522 thread_sleep(50);
523
524 port_packet_t pp = {{0}};
525 st = port_write(w_test_port1, &pp, 1);
526 if (st < 0)
527 return __LINE__;
528
529 st = port_write(w_test_port2, &pp, 1);
530 if (st < 0)
531 return __LINE__;
532
533 st = send_watcher_cmd(cmd_port, QUIT, 0);
534 if (st < 0)
535 return __LINE__;
536
537 int retcode = -1;
538 thread_join(wt, &retcode, INFINITE_TIME);
539 if (retcode) {
540 printf("child thread exited with %d\n", retcode);
541 return __LINE__;
542 }
543
544 st = port_close(w_test_port1);
545 if (st < 0)
546 return __LINE__;
547 st = port_close(w_test_port2);
548 if (st < 0)
549 return __LINE__;
550 st = port_close(cmd_port);
551 if (st < 0)
552 return __LINE__;
553 st = port_destroy(w_test_port1);
554 if (st < 0)
555 return __LINE__;
556 st = port_destroy(w_test_port2);
557 if (st < 0)
558 return __LINE__;
559 st = port_destroy(cmd_port);
560 if (st < 0)
561 return __LINE__;
562
563 return 0;
564 }
565
group_dynamic(void)566 static int group_dynamic(void) {
567 status_t st;
568
569 port_t w_test_port1, r_test_port1;
570 st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
571 if (st < 0)
572 return __LINE__;
573
574 port_t w_test_port2, r_test_port2;
575 st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
576 if (st < 0)
577 return __LINE__;
578
579 port_t pg;
580 st = port_group(&r_test_port1, 1, &pg);
581 if (st < 0)
582 return __LINE__;
583
584 port_packet_t pkt = { { 0 } };
585 st = port_write(w_test_port2, &pkt, 1);
586 if (st < 0)
587 return __LINE__;
588
589 port_result_t rslt;
590 st = port_read(pg, 0, &rslt);
591 if (st != ERR_TIMED_OUT)
592 return __LINE__;
593
594 // Attach the port that has been written to to the port group and ensure
595 // that we can read from it.
596 st = port_group_add(pg, r_test_port2);
597 if (st < 0)
598 return __LINE__;
599
600 st = port_read(pg, 0, &rslt);
601 if (st < 0)
602 return __LINE__;
603
604 // Write some data to a port then remove it from the port group and ensure
605 // that we can't read from it.
606 st = port_write(w_test_port1, &pkt, 1);
607 if (st < 0)
608 return __LINE__;
609
610 st = port_group_remove(pg, r_test_port1);
611 if (st < 0)
612 return __LINE__;
613
614 st = port_read(pg, 0, &rslt);
615 if (st != ERR_TIMED_OUT)
616 return __LINE__;
617
618 st = port_close(w_test_port1);
619 if (st < 0)
620 return __LINE__;
621 st = port_close(w_test_port2);
622 if (st < 0)
623 return __LINE__;
624 st = port_destroy(w_test_port1);
625 if (st < 0)
626 return __LINE__;
627 st = port_destroy(w_test_port2);
628 if (st < 0)
629 return __LINE__;
630
631 return 0;
632 }
633
634 static event_t group_waiting_sync_evt;
635
receive_thread(void * arg)636 static int receive_thread(void *arg) {
637 port_t pg = (port_t)arg;
638
639 // Try to read from an empty port group. When the other thread adds a port
640 // to this port group, we should wake up and
641 port_result_t rslt;
642 status_t st = port_read(pg, 500, &rslt);
643 if (st == ERR_TIMED_OUT)
644 return __LINE__;
645
646 event_signal(&group_waiting_sync_evt, true);
647
648 return 0;
649 }
650
651 /* Test the edge case where a read port with data available is added to a port
652 * group that has a read-blocked receiver.
653 */
group_waiting(void)654 static int group_waiting(void) {
655 status_t st;
656
657 event_init(&group_waiting_sync_evt, false, EVENT_FLAG_AUTOUNSIGNAL);
658
659 port_t w_test_port1, r_test_port1;
660 st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
661 if (st < 0)
662 return __LINE__;
663
664 // Write something to this port group that currently has no receivers.
665 port_packet_t pkt = { { 0 } };
666 st = port_write(w_test_port1, &pkt, 1);
667 if (st < 0)
668 return __LINE__;
669
670 // Create an empty port group.
671 port_t pg;
672 st = port_group(NULL, 0, &pg);
673 if (st < 0)
674 return __LINE__;
675
676
677 thread_t *t1 = thread_create(
678 "receiver",
679 &receive_thread,
680 (void *)pg,
681 DEFAULT_PRIORITY,
682 DEFAULT_STACK_SIZE
683 );
684
685 thread_resume(t1);
686
687 // Wait for the other thread to block on the read.
688 thread_sleep(20);
689
690 // Adding a port that has data available to the port group should wake any
691 // threads waiting on that port group.
692 port_group_add(pg, r_test_port1);
693
694 if (event_wait_timeout(&group_waiting_sync_evt, 500) != NO_ERROR)
695 return __LINE__;
696
697 st = port_close(w_test_port1);
698 if (st < 0)
699 return __LINE__;
700
701 st = port_destroy(w_test_port1);
702 if (st < 0)
703 return __LINE__;
704
705 return 0;
706 }
707
708 #define RUN_TEST(t) result = t(); if (result) goto fail
709
port_tests(int argc,const console_cmd_args * argv)710 int port_tests(int argc, const console_cmd_args *argv) {
711 int result;
712 int count = 3;
713 while (count--) {
714 RUN_TEST(single_thread_basic);
715 RUN_TEST(two_threads_basic);
716 RUN_TEST(group_basic);
717 RUN_TEST(group_dynamic);
718 }
719
720 printf("all tests passed\n");
721 return 0;
722 fail:
723 printf("test failed at line %d\n", result);
724 return 1;
725 }
726
727 #undef RUN_TEST
728