diff options
Diffstat (limited to 'utils/hwstub/stub/main.c')
-rw-r--r-- | utils/hwstub/stub/main.c | 188 |
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 | ||
403 | static void *last_read_addr = 0; | ||
404 | static uint16_t last_read_id = 0xffff; | ||
405 | static size_t last_read_max_size = 0; | ||
406 | |||
403 | static void handle_read(struct usb_ctrlrequest *req) | 407 | static 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 | ||
571 | static uint32_t rw_cp0_inst_buffer[3]; | ||
572 | typedef uint32_t (*read_cp0_inst_buffer_fn_t)(void); | ||
573 | typedef void (*write_cp0_inst_buffer_fn_t)(uint32_t); | ||
574 | |||
575 | uint32_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 | |||
594 | void 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 */ | ||
616 | int 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 */ | ||
642 | int 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 */ | ||
668 | int 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 | |||
685 | Lerr: | ||
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 | |||
697 | static 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 | |||
565 | static void handle_class_intf_req(struct usb_ctrlrequest *req) | 727 | static 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 | } |