summaryrefslogtreecommitdiff
path: root/utils/hwstub/stub/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/stub/main.c')
-rw-r--r--utils/hwstub/stub/main.c188
1 files changed, 176 insertions, 12 deletions
diff --git a/utils/hwstub/stub/main.c b/utils/hwstub/stub/main.c
index c35872f320..3eee8b6a18 100644
--- a/utils/hwstub/stub/main.c
+++ b/utils/hwstub/stub/main.c
@@ -400,10 +400,12 @@ static bool read_atomic(void *dst, void *src, size_t sz)
400 } 400 }
401} 401}
402 402
403static void *last_read_addr = 0;
404static uint16_t last_read_id = 0xffff;
405static size_t last_read_max_size = 0;
406
403static void handle_read(struct usb_ctrlrequest *req) 407static void handle_read(struct usb_ctrlrequest *req)
404{ 408{
405 static uint32_t last_addr = 0;
406 static uint16_t last_id = 0xffff;
407 uint16_t id = req->wValue; 409 uint16_t id = req->wValue;
408 410
409 if(req->bRequest == HWSTUB_READ) 411 if(req->bRequest == HWSTUB_READ)
@@ -413,26 +415,29 @@ static void handle_read(struct usb_ctrlrequest *req)
413 return usb_drv_stall(EP_CONTROL, true, true); 415 return usb_drv_stall(EP_CONTROL, true, true);
414 asm volatile("nop" : : : "memory"); 416 asm volatile("nop" : : : "memory");
415 struct hwstub_read_req_t *read = (void *)usb_buffer; 417 struct hwstub_read_req_t *read = (void *)usb_buffer;
416 last_addr = read->dAddress; 418 last_read_addr = (void *)read->dAddress;
417 last_id = id; 419 last_read_max_size = usb_buffer_size;
420 last_read_id = id;
418 usb_drv_send(EP_CONTROL, NULL, 0); 421 usb_drv_send(EP_CONTROL, NULL, 0);
419 } 422 }
420 else 423 else
421 { 424 {
422 if(id != last_id) 425 /* NOTE: READ2 is also called after a coprocessor operation */
426 if(id != last_read_id)
423 return usb_drv_stall(EP_CONTROL, true, true); 427 return usb_drv_stall(EP_CONTROL, true, true);
428 size_t len = MIN(req->wLength, last_read_max_size);
424 429
425 if(req->bRequest == HWSTUB_READ2_ATOMIC) 430 if(req->bRequest == HWSTUB_READ2_ATOMIC)
426 { 431 {
427 if(set_data_abort_jmp() == 0) 432 if(set_data_abort_jmp() == 0)
428 { 433 {
429 if(!read_atomic(usb_buffer, (void *)last_addr, req->wLength)) 434 if(!read_atomic(usb_buffer, last_read_addr, len))
430 return usb_drv_stall(EP_CONTROL, true, true); 435 return usb_drv_stall(EP_CONTROL, true, true);
431 } 436 }
432 else 437 else
433 { 438 {
434 logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, 439 logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
435 last_addr + req->wLength); 440 last_read_addr + len);
436 return usb_drv_stall(EP_CONTROL, true, true); 441 return usb_drv_stall(EP_CONTROL, true, true);
437 } 442 }
438 } 443 }
@@ -440,19 +445,19 @@ static void handle_read(struct usb_ctrlrequest *req)
440 { 445 {
441 if(set_data_abort_jmp() == 0) 446 if(set_data_abort_jmp() == 0)
442 { 447 {
443 memcpy(usb_buffer, (void *)last_addr, req->wLength); 448 memcpy(usb_buffer, last_read_addr, len);
444 asm volatile("nop" : : : "memory"); 449 asm volatile("nop" : : : "memory");
445 } 450 }
446 else 451 else
447 { 452 {
448 logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, 453 logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr,
449 last_addr + req->wLength); 454 last_read_addr + len);
450 return usb_drv_stall(EP_CONTROL, true, true); 455 return usb_drv_stall(EP_CONTROL, true, true);
451 } 456 }
452 457
453 } 458 }
454 459
455 usb_drv_send(EP_CONTROL, usb_buffer, req->wLength); 460 usb_drv_send(EP_CONTROL, usb_buffer, len);
456 usb_drv_recv(EP_CONTROL, NULL, 0); 461 usb_drv_recv(EP_CONTROL, NULL, 0);
457 } 462 }
458} 463}
@@ -562,6 +567,163 @@ static void handle_exec(struct usb_ctrlrequest *req)
562 } 567 }
563} 568}
564 569
570#ifdef CPU_MIPS
571static uint32_t rw_cp0_inst_buffer[3];
572typedef uint32_t (*read_cp0_inst_buffer_fn_t)(void);
573typedef void (*write_cp0_inst_buffer_fn_t)(uint32_t);
574
575uint32_t mips_read_cp0(unsigned reg, unsigned sel)
576{
577 /* ok this is tricky because the coprocessor read instruction encoding
578 * contains the register and select, so we need to generate the instruction
579 * on the fly, we generate a "function like" buffer with three instructions:
580 * mfc0 v0, reg, sel
581 * jr ra
582 * nop
583 */
584 rw_cp0_inst_buffer[0] = 0x40000000 | /*v0*/2 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
585 rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
586 rw_cp0_inst_buffer[2] = 0; /* nop */
587#ifdef CONFIG_FLUSH_CACHES
588 target_flush_caches();
589#endif
590 read_cp0_inst_buffer_fn_t fn = (read_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
591 return fn();
592}
593
594void mips_write_cp0(unsigned reg, unsigned sel, uint32_t val)
595{
596 /* ok this is tricky because the coprocessor write instruction encoding
597 * contains the register and select, so we need to generate the instruction
598 * on the fly, we generate a "function like" buffer with three instructions:
599 * mtc0 a0, reg, sel
600 * jr ra
601 * nop
602 */
603 rw_cp0_inst_buffer[0] = 0x40800000 | /*a0*/4 << 16 | (sel & 0x7) | (reg & 0x1f) << 11;
604 rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */
605 rw_cp0_inst_buffer[2] = 0; /* nop */
606#ifdef CONFIG_FLUSH_CACHES
607 target_flush_caches();
608#endif
609 write_cp0_inst_buffer_fn_t fn = (write_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer;
610 fn(val);
611}
612#endif
613
614/* coprocessor read: return <0 on error (-2 for dull dump), or size to return
615 * to host otherwise */
616int cop_read(uint8_t args[HWSTUB_COP_ARGS], void *out_data, size_t out_max_sz)
617{
618 /* virtually all targets do register-based operation, so 32-bit */
619 if(out_max_sz < 4)
620 {
621 logf("cop read failed: output buffer is too small\n");
622 return -1;
623 }
624#ifdef CPU_MIPS
625 if(args[HWSTUB_COP_MIPS_COP] != 0)
626 {
627 logf("cop read failed: only mips cp0 is supported\n");
628 return -2;
629 }
630 *(uint32_t *)out_data = mips_read_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL]);
631 return 4;
632#else
633 (void) args;
634 (void) out_data;
635 (void) out_max_sz;
636 logf("cop read failed: unsupported cpu\n");
637 return -1;
638#endif
639}
640
641/* coprocessor write: return <0 on error (-2 for dull dump), or 0 on success */
642int cop_write(uint8_t args[HWSTUB_COP_ARGS], const void *in_data, size_t in_sz)
643{
644 /* virtually all targets do register-based operation, so 32-bit */
645 if(in_sz != 4)
646 {
647 logf("cop read failed: input buffer has wrong size\n");
648 return -1;
649 }
650#ifdef CPU_MIPS
651 if(args[HWSTUB_COP_MIPS_COP] != 0)
652 {
653 logf("cop read failed: only mips cp0 is supported\n");
654 return -2;
655 }
656 mips_write_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL], *(uint32_t *)in_data);
657 return 0;
658#else
659 (void) args;
660 (void) in_data;
661 (void) in_sz;
662 logf("cop write failed: unsupported cpu\n");
663 return -1;
664#endif
665}
666
667/* return size to return to host or <0 on error */
668int do_cop_op(struct hwstub_cop_req_t *cop, void *in_data, size_t in_sz,
669 void *out_data, size_t out_max_sz)
670{
671 int ret = -2; /* -2 means full debug dump */
672 /* handle operations */
673 if(cop->bOp == HWSTUB_COP_READ)
674 {
675 /* read cannot have extra data */
676 if(in_sz > 0)
677 goto Lerr;
678 ret = cop_read(cop->bArgs, out_data, out_max_sz);
679 }
680 else if(cop->bOp == HWSTUB_COP_WRITE)
681 {
682 ret = cop_write(cop->bArgs, in_data, in_sz);
683 }
684
685Lerr:
686 if(ret == -2)
687 {
688 /* debug output */
689 logf("invalid cop op: %d, ", cop->bOp);
690 for(int i = 0; i < HWSTUB_COP_ARGS; i++)
691 logf("%c0x%x", i == 0 ? '[' : ',', cop->bArgs[i]);
692 logf("] in:%d\n", in_sz);
693 }
694 return ret;
695}
696
697static void handle_cop(struct usb_ctrlrequest *req)
698{
699 int size = usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength);
700 int hdr_sz = sizeof(struct hwstub_cop_req_t);
701 asm volatile("nop" : : : "memory");
702 struct hwstub_cop_req_t *cop = (void *)usb_buffer;
703 /* request should at least contain the header */
704 if(size < hdr_sz)
705 return usb_drv_stall(EP_CONTROL, true, true);
706 /* perform coprocessor operation: put output buffer after the input one,
707 * limit output buffer size to maximum buffer size */
708 uint8_t *in_buf = usb_buffer + hdr_sz;
709 size_t in_sz = req->wLength - hdr_sz;
710 uint8_t *out_buf = in_buf + in_sz;
711 size_t out_max_sz = usb_buffer_size - req->wLength;
712 int ret = do_cop_op(cop, in_buf, in_sz, out_buf, out_max_sz);
713 /* STALL on error */
714 if(ret < 0)
715 return usb_drv_stall(EP_CONTROL, true, true);
716 /* acknowledge */
717 usb_drv_send(EP_CONTROL, NULL, 0);
718 /* if there is a read stage, prepare everything for the READ2 */
719 if(ret > 0)
720 {
721 last_read_id = req->wValue;
722 last_read_addr = out_buf;
723 last_read_max_size = ret;
724 }
725}
726
565static void handle_class_intf_req(struct usb_ctrlrequest *req) 727static void handle_class_intf_req(struct usb_ctrlrequest *req)
566{ 728{
567 unsigned intf = req->wIndex & 0xff; 729 unsigned intf = req->wIndex & 0xff;
@@ -581,6 +743,8 @@ static void handle_class_intf_req(struct usb_ctrlrequest *req)
581 return handle_write(req); 743 return handle_write(req);
582 case HWSTUB_EXEC: 744 case HWSTUB_EXEC:
583 return handle_exec(req); 745 return handle_exec(req);
746 case HWSTUB_COPROCESSOR_OP:
747 return handle_cop(req);
584 default: 748 default:
585 usb_drv_stall(EP_CONTROL, true, true); 749 usb_drv_stall(EP_CONTROL, true, true);
586 } 750 }