ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_usbh_seam.c
Go to the documentation of this file.
1
15
16#include "emu_usbh_seam.h"
17
18#include <stdio.h>
19#include <string.h>
20
21#include "emu_elf.h"
22#include "emu_exc.h"
24#include "emu_memory_access.h"
25#include "emu_trace.h"
26
28typedef enum : uint32_t {
32
33/* ============================================================================
34 * Virtual USB host-mode device: a HID boot keyboard behind the ra8_usb_host_*
35 * seam.
36 *
37 * board_usb.c models the USBFS controller in DEVICE mode (a virtual host drives
38 * the firmware's device stack). The inverse case -- the firmware acting as USB
39 * HOST -- drives the USBHS controller (0x40351000, unmodelled) through the
40 * first-party `ra8_usb_host_*` primitives, and with no peer the control
41 * transfer wedges (SUREQ never acked -> k_ra8_err_busy). Rather than model a
42 * second controller register-by-register, seam those primitives the same way
43 * ra8_eth_* is seamed to a virtual net peer (the register model "cannot
44 * satisfy" that sequence either): present a virtual boot keyboard that answers
45 * chapter-9 enumeration and streams interrupt-IN reports. This lets a host
46 * example (usb_host_keyboard) enumerate + read reports end to end with no
47 * hardware -- validating the host stack's control/data logic, not silicon
48 * timing.
49 * ==========================================================================*/
50
66
69 0x12,
70 0x01,
71 0x00,
72 0x02,
73 0x00,
74 0x00,
75 0x00,
76 0x40, /* len,DEVICE,bcdUSB2.00,class0,MPS64 */
77 0x6A,
78 0x1A,
79 0x88,
80 0x42,
81 0x00,
82 0x01,
83 0x00,
84 0x00, /* idVendor 0x1A6A, idProduct 0x4288 */
85 0x00,
86 0x01, /* bcdDevice, iM/iP/iS=0, 1 config */
87};
88
92 0x09, 0x02, 0x22, 0x00, 0x01, 0x01, 0x00, 0xA0, 0x32, /* CONFIG: wTotalLen 34, 1 iface */
93 0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x01, 0x00, /* IFACE: HID, boot, keyboard */
94 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x3F, 0x00, /* HID: report desc len 0x3F */
95 0x07, 0x05, 0x81, 0x03, 0x40, 0x00, 0x01, /* EP1 IN, interrupt, MPS64, 1ms
96 */
97};
98
101static const uint8_t s_k_vkbd_report_desc[63] = {
102 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01,
103 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01,
104 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06,
105 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0,
106};
107
109typedef enum : uint8_t {
110 k_vkbd_key_r = 0x15U,
111 k_vkbd_key_a = 0x04U,
112 k_vkbd_key_8 = 0x25U,
113 k_vkbd_key_d = 0x07U,
114 k_vkbd_key_2 = 0x1FU,
116
123
124static uint8_t s_vkbd_seq = 0U;
125static uint32_t s_vkbd_ctrl_serviced = 0U;
126static uint32_t s_vkbd_reports_sent = 0U;
127
139RA8_INTERNAL static uint32_t internal_usbh_arg5(uc_engine* uc)
140{
141 uint32_t sp = 0U;
142 uint32_t p = 0U;
143 (void)uc_reg_read(uc, UC_ARM_REG_SP, &sp);
144 (void)emu_mem_read(uc, (uint64_t)sp, &p, sizeof(p));
145 return p;
146}
147
160RA8_INTERNAL static void
161internal_on_usbh_ok(uc_engine* uc, uint64_t address, uint32_t size, void* user)
162{
163 (void)address;
164 (void)size;
165 (void)user;
166 eth_hook_return(uc, 0U); /* k_ra8_ok */
167}
168
181RA8_INTERNAL static void
182internal_on_usbh_line_state(uc_engine* uc, uint64_t address, uint32_t size, void* user)
183{
184 (void)address;
185 (void)size;
186 (void)user;
188}
189
202RA8_INTERNAL static void
203internal_on_usbh_control_xfer(uc_engine* uc, uint64_t address, uint32_t size, void* user)
204{
205 (void)address;
206 (void)size;
207 (void)user;
208 uint32_t setup_ptr = 0U;
209 uint32_t data_ptr = 0U;
210 uint32_t data_len = 0U;
211 (void)uc_reg_read(uc, UC_ARM_REG_R1, &setup_ptr);
212 (void)uc_reg_read(uc, UC_ARM_REG_R2, &data_ptr);
213 (void)uc_reg_read(uc, UC_ARM_REG_R3, &data_len);
214 const uint32_t out_ptr = internal_usbh_arg5(uc);
215
216 uint8_t s[8] = {};
217 (void)emu_mem_read(uc, (uint64_t)setup_ptr, s, sizeof(s));
218 const uint8_t b_request = s[1];
219 const uint8_t desc_type = s[3]; /* wValue high byte = descriptor type. */
220 const uint8_t* src = nullptr;
221 uint16_t src_len = 0U;
222 if (b_request == (uint8_t)k_vkbd_breq_get_descriptor) {
223 if (desc_type == (uint8_t)k_vkbd_dt_device) {
225 src_len = (uint16_t)k_vkbd_dev_desc_len;
226 } else if (desc_type == (uint8_t)k_vkbd_dt_config) {
228 src_len = (uint16_t)k_vkbd_cfg_desc_len;
229 } else if (desc_type == (uint8_t)k_vkbd_dt_hid_report) {
231 src_len = (uint16_t)sizeof(s_k_vkbd_report_desc);
232 }
233 }
234 uint16_t n = 0U;
235 if ((src != nullptr) && (data_ptr != 0U)) {
236 n = (src_len < (uint16_t)data_len) ? src_len : (uint16_t)data_len;
237 (void)emu_mem_write(uc, (uint64_t)data_ptr, src, n);
238 }
239 /* No-data control writes (SET_ADDRESS / SET_CONFIGURATION / SET_IDLE /
240 * SET_PROTOCOL) just leave n = 0; the host treats that as a successful ack.
241 */
242 if (out_ptr != 0U) {
243 (void)emu_mem_write(uc, (uint64_t)out_ptr, &n, sizeof(n));
244 }
246 eth_hook_return(uc, 0U); /* k_ra8_ok */
247}
248
261RA8_INTERNAL static void
262internal_on_usbh_bulk_in(uc_engine* uc, uint64_t address, uint32_t size, void* user)
263{
264 (void)address;
265 (void)size;
266 (void)user;
267 uint32_t buf = 0U;
268 uint32_t max_len = 0U;
269 (void)uc_reg_read(uc, UC_ARM_REG_R2, &buf);
270 (void)uc_reg_read(uc, UC_ARM_REG_R3, &max_len);
271 const uint32_t out_ptr = internal_usbh_arg5(uc);
272 /* Boot-keyboard report: [seq][reserved 0][keycodes R A 8 D 2][0]. The host
273 * ignores byte 0 and pattern-checks bytes 1.. -- it streams "RA8D2". */
274 uint8_t rep[k_vkbd_report_len] = {};
275 rep[0] = s_vkbd_seq++;
276 for (uint32_t i = 0U; i < (uint32_t)k_vkbd_num_keys; i++) {
277 rep[2U + i] = s_k_vkbd_keycodes[i];
278 }
279 uint16_t n = (uint16_t)k_vkbd_report_len;
280 if ((uint32_t)n > max_len) {
281 n = (uint16_t)max_len;
282 }
283 if (buf != 0U) {
284 (void)emu_mem_write(uc, (uint64_t)buf, rep, n);
285 }
286 if (out_ptr != 0U) {
287 (void)emu_mem_write(uc, (uint64_t)out_ptr, &n, sizeof(n));
288 }
290 eth_hook_return(uc, 0U); /* k_ra8_ok */
291}
292
293/* ----------------------------------------------------------------------------
294 * Virtual USB host-mode MSC device: a read-only FAT16 disk whose one file
295 * MRAM.BIN is the 1 MiB MRAM code window. Seams the first-party
296 * `ra8_usb_hmsc_*` class API (one level above the BOT/SCSI bulk transport) so a
297 * host MSC app (usb_host_msc_browse) enumerates, READ_CAPACITYs, mounts the
298 * FAT16, browses the root directory, and content-verifies MRAM.BIN -- all with
299 * no peer device. The boot/FAT/root sectors are a byte-identical replica of the
300 * device's selftest_fat_fill_sector; the data region is read live from emulated
301 * MRAM, so it matches the host's own MRAM compare byte-for-byte.
302 * --------------------------------------------------------------------------*/
303
320
366
367static const uint8_t s_k_vmsc_oem[8] = {'R', 'A', '8', 'D', '2', 'F', 'W', ' '};
368static const uint8_t s_k_vmsc_label[11] = {'R', 'A', '8', 'D', '2', ' ', 'M', 'R', 'A', 'M', ' '};
369static const uint8_t s_k_vmsc_fstype[8] = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '};
370static const uint8_t s_k_vmsc_fname[11] = {'M', 'R', 'A', 'M', ' ', ' ', ' ', ' ', 'B', 'I', 'N'};
371
375static bool s_vmsc_write_seen = false;
376
380static bool s_vmsc_writable = false;
381
390typedef struct {
391 uint32_t lba;
392 bool valid;
395
399
412RA8_INTERNAL static bool internal_vmsc_overlay_get(uint32_t lba, uint8_t* out)
413{
414 for (uint32_t i = 0U; i < (uint32_t)(sizeof(s_vmsc_overlay) / sizeof(s_vmsc_overlay[0])); i++) {
415 if (s_vmsc_overlay[i].valid && (s_vmsc_overlay[i].lba == lba)) {
416 (void)memcpy(out, s_vmsc_overlay[i].data, (size_t)k_vmsc_block_size);
417 return true;
418 }
419 }
420 return false;
421}
422
432RA8_INTERNAL static void internal_vmsc_overlay_put(uint32_t lba, const uint8_t* in)
433{
434 const uint32_t slots = (uint32_t)(sizeof(s_vmsc_overlay) / sizeof(s_vmsc_overlay[0]));
435 for (uint32_t i = 0U; i < slots; i++) {
436 if (s_vmsc_overlay[i].valid && (s_vmsc_overlay[i].lba == lba)) {
437 (void)memcpy(s_vmsc_overlay[i].data, in, (size_t)k_vmsc_block_size);
438 return;
439 }
440 }
441 for (uint32_t i = 0U; i < slots; i++) {
442 if (!s_vmsc_overlay[i].valid) {
443 s_vmsc_overlay[i].lba = lba;
444 s_vmsc_overlay[i].valid = true;
445 (void)memcpy(s_vmsc_overlay[i].data, in, (size_t)k_vmsc_block_size);
446 return;
447 }
448 }
449}
450
461RA8_INTERNAL static void internal_vmsc_put16(uint8_t* p, uint16_t v)
462{
463 p[0] = (uint8_t)(v & (uint16_t)k_byte_mask);
464 p[1] = (uint8_t)((v >> (uint16_t)k_bpb_shift8) & (uint16_t)k_byte_mask);
465}
466
477RA8_INTERNAL static void internal_vmsc_put32(uint8_t* p, uint32_t v)
478{
479 p[0] = (uint8_t)(v & (uint32_t)k_byte_mask);
480 p[1] = (uint8_t)((v >> (uint32_t)k_bpb_shift8) & (uint32_t)k_byte_mask);
481 p[2] = (uint8_t)((v >> (uint32_t)k_bpb_shift16) & (uint32_t)k_byte_mask);
482 p[3] = (uint8_t)((v >> (uint32_t)k_bpb_shift24) & (uint32_t)k_byte_mask);
483}
484
494RA8_INTERNAL static void internal_vmsc_fill_boot(uint8_t* out)
495{
496 out[0] = (uint8_t)k_bpb_jmp0;
497 out[1] = (uint8_t)k_bpb_jmp1;
498 out[2] = (uint8_t)k_bpb_jmp2; /* jmp + nop */
499 (void)memcpy(&out[k_bpb_off_oem], s_k_vmsc_oem, sizeof(s_k_vmsc_oem));
501 out[k_bpb_off_secperclus] = (uint8_t)k_bpb_secperclus_1; /* sectors/cluster */
503 (uint16_t)k_bpb_rsvdseccnt_1); /* reserved sectors */
504 out[k_bpb_off_numfats] = (uint8_t)k_bpb_numfats_1; /* number of FATs */
506 (uint16_t)k_bpb_rootentcnt_512); /* root entries */
508 out[k_bpb_off_media] = (uint8_t)k_bpb_media_f8; /* media descriptor */
509 internal_vmsc_put16(&out[k_bpb_off_fatsz16], (uint16_t)k_bpb_fatsz16_17); /* sectors per FAT */
511 (uint16_t)k_bpb_secpertrk_32); /* sectors per track */
512 internal_vmsc_put16(&out[k_bpb_off_numheads], (uint16_t)k_bpb_numheads_16); /* heads */
513 out[k_bpb_off_drvnum] = (uint8_t)k_bpb_drvnum_80; /* drive number */
514 out[k_bpb_off_bootsig] = (uint8_t)k_bpb_bootsig_29; /* boot signature */
518 out[k_bpb_off_sig0] = (uint8_t)k_bpb_sig0_55;
519 out[k_bpb_off_sig1] = (uint8_t)k_bpb_sig1_aa;
520}
521
532RA8_INTERNAL static void internal_vmsc_fill_fat(uint32_t fat_sector, uint8_t* out)
533{
534 const uint32_t first = fat_sector * (uint32_t)k_vmsc_entries_per_fs;
535 for (uint32_t j = 0U; j < (uint32_t)k_vmsc_entries_per_fs; j++) {
536 const uint32_t entry = first + j;
537 uint16_t value = 0U;
538 if (entry == 0U) {
539 value = (uint16_t)k_vmsc_fat_entry0;
540 } else if ((entry == 1U) || (entry == (uint32_t)k_vmsc_last_mram_clus)) {
541 /* Entry 1 is the reserved media/EOC marker and the last cluster ends the
542 * chain -- both hold the same EOC word. */
543 value = (uint16_t)k_vmsc_fat_eoc;
544 } else if (entry < (uint32_t)k_vmsc_last_mram_clus) {
545 value = (uint16_t)(entry + 1U);
546 }
547 internal_vmsc_put16(&out[(size_t)j * 2U], value);
548 }
549}
550
561RA8_INTERNAL static void internal_vmsc_fill_root(uint32_t root_sector, uint8_t* out)
562{
563 if (root_sector != 0U) {
564 return;
565 }
566 (void)memcpy(&out[0], s_k_vmsc_label, sizeof(s_k_vmsc_label));
567 out[k_dir_off_attr] = (uint8_t)k_dir_attr_vollabel; /* volume-label attribute */
568 uint8_t* e = &out[k_dir_off_entry];
569 (void)memcpy(e, s_k_vmsc_fname, sizeof(s_k_vmsc_fname));
570 e[k_dir_off_attr] = (uint8_t)k_dir_attr_readonly; /* read-only attribute */
573}
574
586RA8_INTERNAL static void internal_vmsc_fill_sector(uc_engine* uc, uint32_t lba, uint8_t* out)
587{
588 (void)memset(out, 0, (size_t)k_vmsc_block_size);
589 if (lba == 0U) {
591 } else if (lba < (uint32_t)k_vmsc_root_lba) {
592 internal_vmsc_fill_fat(lba - 1U, out);
593 } else if (lba < (uint32_t)k_vmsc_data_lba) {
594 internal_vmsc_fill_root(lba - (uint32_t)k_vmsc_root_lba, out);
595 } else {
596 const uint32_t cluster = (lba - (uint32_t)k_vmsc_data_lba) + (uint32_t)k_vmsc_first_cluster;
597 if (cluster <= (uint32_t)k_vmsc_last_mram_clus) {
598 const uint32_t off = (cluster - (uint32_t)k_vmsc_first_cluster) * (uint32_t)k_vmsc_block_size;
599 (void)emu_mem_read(uc,
600 (uint64_t)k_vmsc_mram_base + (uint64_t)off,
601 out,
602 (size_t)k_vmsc_block_size);
603 }
604 }
605}
606
619RA8_INTERNAL static void
620internal_on_hmsc_ok(uc_engine* uc, uint64_t address, uint32_t size, void* user)
621{
622 (void)address;
623 (void)size;
624 (void)user;
625 eth_hook_return(uc, 0U); /* k_ra8_ok */
626}
627
630typedef enum : uint32_t {
637} hmsc_dev_t;
638
651RA8_INTERNAL static void
652internal_on_hmsc_enumerate(uc_engine* uc, uint64_t address, uint32_t size, void* user)
653{
654 (void)address;
655 (void)size;
656 (void)user;
657 uint32_t dev_ptr = 0U;
658 (void)uc_reg_read(uc, UC_ARM_REG_R0, &dev_ptr);
659 if (dev_ptr != 0U) {
660 /* ra8_usb_hmsc_device_t: addr,bin_ep,bout_ep,max_lun,iface,[pad],in_mps,
661 * out_mps,vid,pid. */
662 uint8_t d[k_hmsc_dev_bytes] = {};
663 d[0] = 1U; /* device_address */
664 d[1] = 1U; /* bulk_in_ep */
665 d[2] = 2U; /* bulk_out_ep */
666 internal_vmsc_put16(&d[6], (uint16_t)k_hmsc_bulk_mps); /* bulk_in_max_packet */
667 internal_vmsc_put16(&d[8], (uint16_t)k_hmsc_bulk_mps); /* bulk_out_max_packet */
668 internal_vmsc_put16(&d[k_hmsc_off_vid], (uint16_t)k_hmsc_vendor_id); /* vendor_id */
669 internal_vmsc_put16(&d[k_hmsc_off_pid], (uint16_t)k_hmsc_product_id); /* product_id */
670 (void)emu_mem_write(uc, (uint64_t)dev_ptr, d, sizeof(d));
671 }
672 eth_hook_return(uc, 0U); /* k_ra8_ok */
673}
674
687RA8_INTERNAL static void
688internal_on_hmsc_read_capacity(uc_engine* uc, uint64_t address, uint32_t size, void* user)
689{
690 (void)address;
691 (void)size;
692 (void)user;
693 uint32_t bc_ptr = 0U;
694 uint32_t bs_ptr = 0U;
695 (void)uc_reg_read(uc, UC_ARM_REG_R1, &bc_ptr);
696 (void)uc_reg_read(uc, UC_ARM_REG_R2, &bs_ptr);
697 const uint32_t block_count = (uint32_t)k_vmsc_total_sectors;
698 const uint32_t block_size = (uint32_t)k_vmsc_block_size;
699 if (bc_ptr != 0U) {
700 (void)emu_mem_write(uc, (uint64_t)bc_ptr, &block_count, sizeof(block_count));
701 }
702 if (bs_ptr != 0U) {
703 (void)emu_mem_write(uc, (uint64_t)bs_ptr, &block_size, sizeof(block_size));
704 }
705 eth_hook_return(uc, 0U); /* k_ra8_ok */
706}
707
720RA8_INTERNAL static void
721internal_on_hmsc_read10(uc_engine* uc, uint64_t address, uint32_t size, void* user)
722{
723 (void)address;
724 (void)size;
725 (void)user;
726 uint32_t lba = 0U;
727 uint32_t count = 0U;
728 uint32_t buf = 0U;
729 (void)uc_reg_read(uc, UC_ARM_REG_R1, &lba);
730 (void)uc_reg_read(uc, UC_ARM_REG_R2, &count);
731 (void)uc_reg_read(uc, UC_ARM_REG_R3, &buf);
732 count &= (uint32_t)k_lo16_mask; /* block_count is a uint16_t argument. */
733 for (uint32_t i = 0U; (i < count) && (buf != 0U); i++) {
734 uint8_t sec[k_vmsc_block_size];
735 if (!internal_vmsc_overlay_get(lba + i, sec)) { /* a host WRITE(10) wins over the synthesis. */
736 internal_vmsc_fill_sector(uc, lba + i, sec);
737 }
738 (void)emu_mem_write(uc,
739 (uint64_t)buf + ((uint64_t)i * (uint64_t)k_vmsc_block_size),
740 sec,
741 sizeof(sec));
742 }
743 eth_hook_return(uc, 0U); /* k_ra8_ok */
744}
745
758RA8_INTERNAL static void
759internal_on_hmsc_write10(uc_engine* uc, uint64_t address, uint32_t size, void* user)
760{
761 (void)address;
762 (void)size;
763 (void)user;
764 if (!s_vmsc_writable) {
765 /* Read-only disk (usb_host_msc_browse): reject. This is that app's last
766 * step before PASS, so flag it for the host early-stop. */
767 s_vmsc_write_seen = true;
768 eth_hook_return(uc, (uint32_t)k_ra8_err_inval_st); /* write protected */
769 return;
770 }
771 /* Writable disk (usb_host_file_ops): stash the written sectors in the overlay
772 * so the host's read-back sees them, and accept. The write is mid-ladder
773 * here, so do NOT trip the write-seen early-stop -- that app stops on its
774 * banner. */
775 uint32_t lba = 0U;
776 uint32_t count = 0U;
777 uint32_t buf = 0U;
778 (void)uc_reg_read(uc, UC_ARM_REG_R1, &lba);
779 (void)uc_reg_read(uc, UC_ARM_REG_R2, &count);
780 (void)uc_reg_read(uc, UC_ARM_REG_R3, &buf);
781 count &= (uint32_t)k_lo16_mask;
782 for (uint32_t i = 0U; (i < count) && (buf != 0U); i++) {
783 uint8_t sec[k_vmsc_block_size];
784 (void)emu_mem_read(uc,
785 (uint64_t)buf + ((uint64_t)i * (uint64_t)k_vmsc_block_size),
786 sec,
787 sizeof(sec));
788 internal_vmsc_overlay_put(lba + i, sec);
789 }
790 eth_hook_return(uc, 0U); /* k_ra8_ok -- write accepted */
791}
792
818bool usbh_seam_install(uc_engine* uc, const emu_elf_source_t* elf)
819{
820 const uint32_t msc = elf_sym_addr(elf, "ra8_usb_hmsc_read10", nullptr);
821 if (msc != 0U) {
822 /* usb_host_file_ops creates a file (it links fileops_backend_write) -> the
823 * virtual disk is writable; usb_host_msc_browse tests a read-only LUN. */
824 s_vmsc_writable = (elf_sym_addr(elf, "fileops_backend_write", nullptr) != 0U);
825 eth_seam_hook(uc, elf, "ra8_usb_hmsc_init", (void*)internal_on_hmsc_ok);
826 eth_seam_hook(uc, elf, "ra8_usb_hmsc_enumerate", (void*)internal_on_hmsc_enumerate);
827 eth_seam_hook(uc, elf, "ra8_usb_hmsc_read_capacity", (void*)internal_on_hmsc_read_capacity);
828 eth_seam_hook(uc, elf, "ra8_usb_hmsc_read10", (void*)internal_on_hmsc_read10);
829 eth_seam_hook(uc, elf, "ra8_usb_hmsc_write10", (void*)internal_on_hmsc_write10);
830 eth_seam_hook(uc, elf, "ra8_usb_hmsc_close", (void*)internal_on_hmsc_ok);
831 (void)priv_emu_io_errf(" usb-host seam : hmsc=0x%08X (virtual MSC FAT16 "
832 "disk, file MRAM.BIN, %s)\n",
833 msc,
834 s_vmsc_writable ? "read-write" : "read-only");
835 return true;
836 }
837 const uint32_t cx = elf_sym_addr(elf, "ra8_usb_host_control_xfer", nullptr);
838 if (cx == 0U) {
839 return false; /* not a USB-host-capable firmware -- nothing to seam. */
840 }
841 eth_seam_hook(uc, elf, "ra8_usb_host_line_state", (void*)internal_on_usbh_line_state);
842 eth_seam_hook(uc, elf, "ra8_usb_host_control_xfer", (void*)internal_on_usbh_control_xfer);
843 eth_seam_hook(uc, elf, "ra8_usb_host_bulk_in", (void*)internal_on_usbh_bulk_in);
844 eth_seam_hook(uc, elf, "ra8_usb_host_bus_reset", (void*)internal_on_usbh_ok);
845 eth_seam_hook(uc, elf, "ra8_usb_host_set_uact", (void*)internal_on_usbh_ok);
846 eth_seam_hook(uc, elf, "ra8_usb_host_set_target", (void*)internal_on_usbh_ok);
847 eth_seam_hook(uc, elf, "ra8_usb_host_pipe_setup", (void*)internal_on_usbh_ok);
848 (void)priv_emu_io_errf(" usb-host seam : control=0x%08X (virtual HID boot "
849 "keyboard \"RA8D2\")\n",
850 cx);
851 return true;
852}
853
857{
859}
ELF32 image services for the board emulator (load / symbols / vectors).
uint32_t elf_sym_addr(const emu_elf_source_t *elf, const char *name, uint32_t *size_out)
Resolve a function symbol's entry address from the ELF .symtab.
Cortex-M exception model constants and interfaces for ra8_emulator.
@ k_lo16_mask
Low halfword of a 32-bit fetch.
Definition emu_exc.h:119
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
Central first-party Unicorn memory access seam.
uc_err emu_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t count)
Read guest memory through the central access seam.
uc_err emu_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t count)
Write guest memory through the central access seam.
Function-entry seam glue + the –trace-sym instrument.
void eth_seam_hook(uc_engine *uc, const emu_elf_source_t *elf, const char *name, void *cb)
Hook one symbol (if present) to cb; record it for the report.
Definition emu_trace.c:43
void eth_hook_return(uc_engine *uc, uint32_t r0)
Emulate "return r0;" from a hooked function: set R0, branch to LR.
Definition emu_trace.c:32
static RA8_INTERNAL void internal_on_usbh_control_xfer(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on USB host control xfer for the emu USB host seam model.
usbh_err_code_t
ra8_err_t values the virtual devices return to the host stack.
@ k_ra8_err_inval_st
ra8_err_t value: invalid state.
static RA8_INTERNAL void internal_vmsc_put16(uint8_t *p, uint16_t v)
Little-endian 16-bit store into a sector buffer.
vkbd_const_t
bRequest / descriptor-type / sizing constants for the virtual device.
@ k_vkbd_cfg_desc_len
Full CONFIGURATION descriptor length.
@ k_vkbd_num_keys
Keycodes typed ("R A 8 D 2").
@ k_vkbd_dt_config
CONFIGURATION descriptor.
@ k_vkbd_dev_desc_len
DEVICE descriptor length.
@ k_vkbd_stop_reports
Reports streamed before USB_STOP fires.
@ k_vkbd_lnst_attached
SYSSTS0.LNST J-state (device on bus).
@ k_vkbd_dt_hid_report
HID REPORT descriptor.
@ k_vkbd_report_len
Boot-keyboard input report width.
@ k_vkbd_dt_device
DEVICE descriptor (wValue hi byte).
@ k_vkbd_breq_get_descriptor
Standard GET_DESCRIPTOR bRequest.
@ k_vkbd_dt_string
STRING descriptor.
static RA8_INTERNAL void internal_vmsc_fill_boot(uint8_t *out)
Synthesize the FAT16 boot sector (BPB), mirroring the device side.
static RA8_INTERNAL void internal_on_usbh_line_state(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on USB host line state for the emu USB host seam model.
static const uint8_t s_k_vkbd_config_desc[k_vkbd_cfg_desc_len]
34-byte CONFIGURATION: 1 HID boot-keyboard iface, 1 interrupt-IN EP1.
static RA8_INTERNAL void internal_vmsc_fill_root(uint32_t root_sector, uint8_t *out)
Synthesize root-directory sector 0: volume label + MRAM.BIN entry.
static bool s_vmsc_write_seen
Set once the host attempts a WRITE(10) into the READ-ONLY disk – the last host step before usb_host_m...
static const uint8_t s_k_vmsc_fstype[8]
static bool s_vmsc_writable
True when the virtual disk is writable (usb_host_file_ops links fileops_backend_write); else the disk...
static const uint8_t s_k_vmsc_fname[11]
static RA8_INTERNAL void internal_vmsc_fill_fat(uint32_t fat_sector, uint8_t *out)
Synthesize one FAT sector: MRAM.BIN chains clusters 2..2049.
static uint32_t s_vkbd_ctrl_serviced
Control transfers answered.
vkbd_keycode_t
HID Usage-Table keycodes (Usage Page 0x07) for the typed string.
@ k_vkbd_key_d
HID usage for 'D'.
@ k_vkbd_key_2
HID usage for '2'.
@ k_vkbd_key_8
HID usage for '8'.
@ k_vkbd_key_a
HID usage for 'A'.
@ k_vkbd_key_r
HID usage for 'R'.
bool usbh_seam_install(uc_engine *uc, const emu_elf_source_t *elf)
Install the virtual USB host-mode device seam if the host stack is linked.
vmsc_bpb_t
FAT16 BPB byte offsets, fixed field values, and store shifts.
@ k_bpb_numfats_1
1 FAT copy.
@ k_bpb_secpertrk_32
32 sectors per track.
@ k_bpb_shift8
Byte 1 store shift.
@ k_bpb_off_vollab
BS_VolLab offset.
@ k_bpb_drvnum_80
Drive number (first fixed disk).
@ k_bpb_media_f8
Fixed-disk media descriptor.
@ k_dir_off_fstcluslo
DIR_FstClusLO offset within entry.
@ k_bpb_sig1_aa
Boot-sector signature byte 1.
@ k_bpb_numheads_16
16 heads.
@ k_bpb_sig0_55
Boot-sector signature byte 0.
@ k_bpb_bootsig_29
Extended boot signature.
@ k_bpb_rsvdseccnt_1
1 reserved sector.
@ k_bpb_jmp2
BS_jmpBoot[2]: NOP.
@ k_bpb_off_sig0
0x55 signature byte.
@ k_dir_attr_vollabel
ATTR_VOLUME_ID.
@ k_bpb_secperclus_1
1 sector per cluster.
@ k_bpb_shift16
Byte 2 store shift.
@ k_dir_attr_readonly
ATTR_READ_ONLY.
@ k_bpb_off_secpertrk
BPB_SecPerTrk offset.
@ k_bpb_off_filsystype
BS_FilSysType offset.
@ k_bpb_jmp0
BS_jmpBoot[0]: short jump opcode.
@ k_dir_off_entry
Second 32-byte directory entry.
@ k_bpb_rootentcnt_512
512 root-directory entries.
@ k_bpb_fatsz16_17
17 sectors per FAT.
@ k_dir_off_filesize
DIR_FileSize offset within entry.
@ k_bpb_jmp1
BS_jmpBoot[1]: jump displacement.
@ k_bpb_off_numheads
BPB_NumHeads offset.
@ k_bpb_off_sig1
0xAA signature byte.
@ k_bpb_shift24
Byte 3 store shift.
static const uint8_t s_k_vmsc_label[11]
static const uint8_t s_k_vmsc_oem[8]
static RA8_INTERNAL bool internal_vmsc_overlay_get(uint32_t lba, uint8_t *out)
Return an overwritten sector if lba is in the overlay.
static RA8_INTERNAL void internal_on_hmsc_ok(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on hmsc ok for the emu USB host seam model.
static vmsc_overlay_t s_vmsc_overlay[k_vmsc_overlay_slots]
Write overlay for the writable disk (file_ops touches only a handful).
static uint8_t s_vkbd_seq
Rolling report seq (report byte 0).
static const uint8_t s_k_vkbd_device_desc[k_vkbd_dev_desc_len]
18-byte DEVICE descriptor: class defined at interface, EP0 MPS 64.
static RA8_INTERNAL void internal_vmsc_put32(uint8_t *p, uint32_t v)
Little-endian 32-bit store into a sector buffer.
static const uint8_t s_k_vkbd_keycodes[k_vkbd_num_keys]
HID Usage-Table keycodes the virtual keyboard "types": R A 8 D 2.
static const uint8_t s_k_vkbd_report_desc[63]
Standard boot-keyboard HID REPORT descriptor (63 bytes, USB HID 1.11 E.6).
hmsc_dev_t
ra8_usb_hmsc_device_t field offsets + reported bulk EP packet/VID/PID.
@ k_hmsc_off_vid
vid offset in ra8_usb_hmsc_device_t.
@ k_hmsc_vendor_id
Reported USB vendor_id.
@ k_hmsc_bulk_mps
Reported bulk-endpoint max packet size.
@ k_hmsc_product_id
Reported USB product_id.
@ k_hmsc_dev_bytes
Marshalled ra8_usb_hmsc_device_t size.
@ k_hmsc_off_pid
pid offset in ra8_usb_hmsc_device_t.
static RA8_INTERNAL void internal_on_hmsc_write10(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on hmsc write10 for the emu USB host seam model.
static RA8_INTERNAL void internal_vmsc_overlay_put(uint32_t lba, const uint8_t *in)
Record an overwritten sector (update existing slot or take a free one).
static RA8_INTERNAL void internal_on_usbh_bulk_in(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on USB host bulk in for the emu USB host seam model.
static RA8_INTERNAL uint32_t internal_usbh_arg5(uc_engine *uc)
Read the 5th (stack-passed) argument of an AAPCS call: mem32[SP].
static uint32_t s_vkbd_reports_sent
Interrupt-IN reports streamed.
vmsc_const_t
FAT16 geometry + boot/dir layout for the virtual MSC volume.
@ k_vmsc_entries_per_fs
FAT16 entries per 512-byte sector.
@ k_vmsc_first_cluster
FAT data area starts at cluster 2.
@ k_vmsc_file_bytes
MRAM.BIN size: 1 MiB.
@ k_vmsc_mram_base
MRAM window base (MRAM.BIN data).
@ k_vmsc_fat_eoc
End-of-chain marker.
@ k_vmsc_block_size
Logical block size.
@ k_vmsc_last_mram_clus
Last cluster of MRAM.BIN.
@ k_vmsc_data_lba
First data-region LBA (cluster 2).
@ k_vmsc_overlay_slots
Overwritten sectors the overlay holds.
@ k_vmsc_total_sectors
1 reserved + 17 FAT + 32 root + 4096.
@ k_vmsc_fat_entry0
FAT[0]: media F8 + filler.
@ k_vmsc_root_lba
First root-directory LBA.
@ k_vmsc_volid
Boot-sector volume serial.
bool emu_usbh_done(void)
Implementation of emu_usbh_done() – the USBH early-stop predicate.
static RA8_INTERNAL void internal_on_hmsc_read10(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on hmsc read10 for the emu USB host seam model.
static RA8_INTERNAL void internal_vmsc_fill_sector(uc_engine *uc, uint32_t lba, uint8_t *out)
Fill one 512-byte volume sector (boot / FAT / root / live MRAM data).
static RA8_INTERNAL void internal_on_hmsc_read_capacity(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on hmsc read capacity for the emu USB host seam model.
static RA8_INTERNAL void internal_on_hmsc_enumerate(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on hmsc enumerate for the emu USB host seam model.
static RA8_INTERNAL void internal_on_usbh_ok(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform on USB host ok for the emu USB host seam model.
Virtual USB host-mode devices behind the ra8_usb_host_* / hmsc seams.
@ k_bpb_off_bytspersec
BPB bytes/sector offset.
Definition mkfontimg.c:35
@ k_bpb_off_secperclus
BPB sectors/cluster offset.
Definition mkfontimg.c:36
@ k_bpb_off_rootentcnt
BPB root-entry offset.
Definition mkfontimg.c:39
@ k_bpb_off_numfats
BPB FAT-count offset.
Definition mkfontimg.c:38
@ k_bpb_off_rsvdseccnt
BPB reserved-sector offset.
Definition mkfontimg.c:37
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
@ k_dir_off_attr
MS FAT spec sec 6 "DIR_Attr".
@ k_byte_mask
Byte mask.
One independently owned immutable raw-descriptor ELF source.
Definition emu_elf.h:91
One overwritten sector of the otherwise-synthesized FAT16 volume.
uint8_t data[k_vmsc_block_size]
The written 512-byte sector.
uint32_t lba
Overwritten LBA.
bool valid
Slot in use.
@ k_bpb_off_media
Media descriptor.
@ k_bpb_off_oem
OEM name (8 bytes).
@ k_bpb_off_volid
Volume serial (4 bytes).
@ k_bpb_off_bootsig
Extended boot signature.
@ k_bpb_off_fatsz16
Sectors per FAT.
@ k_bpb_off_drvnum
Drive number.
@ k_bpb_off_totsec16
Total sectors (16-bit).