From 6bc870d899c474bce82457699ec4b753d1538123 Mon Sep 17 00:00:00 2001
From: Ryan <fauxpark@gmail.com>
Date: Mon, 27 Dec 2021 21:10:07 +1100
Subject: [PATCH] Refactor `bootloader_jump()` implementations (#15450)

* Refactor `bootloader_jump()` implementations

* Fix tests?

* Rename `atmel-samba` to `md-boot`
---
 build_test.mk                                 |   1 +
 builddefs/bootloader.mk                       |  40 ++-
 data/schemas/keyboard.jsonschema              |   2 +-
 keyboards/handwired/onekey/teensy_32/rules.mk |   3 +
 keyboards/handwired/onekey/teensy_lc/rules.mk |   3 +
 keyboards/massdrop/alt/rules.mk               |   3 +
 keyboards/massdrop/ctrl/rules.mk              |   3 +
 keyboards/mechlovin/olly/jf/rules.mk          |   4 +-
 keyboards/rocketboard_16/rules.mk             |   2 +-
 keyboards/tgr/jane/v2ce/rules.mk              |   2 +-
 .../{bootloader.c => bootloaders/md_boot.c}   |  47 +--
 platforms/avr/bootloader.c                    | 293 ------------------
 platforms/avr/bootloaders/bootloadhid.c       |  34 ++
 platforms/avr/bootloaders/caterina.c          |  39 +++
 platforms/avr/bootloaders/dfu.c               |  53 ++++
 platforms/avr/bootloaders/halfkay.c           | 129 ++++++++
 platforms/avr/bootloaders/none.c              |  20 ++
 platforms/avr/bootloaders/usbasploader.c      |  57 ++++
 platforms/chibios/bootloader.c                | 145 ---------
 platforms/chibios/bootloaders/gd32v_dfu.c     |  40 +++
 platforms/chibios/bootloaders/halfkay.c       |  26 ++
 platforms/chibios/bootloaders/kiibohd.c       |  33 ++
 platforms/chibios/bootloaders/none.c          |  20 ++
 platforms/chibios/bootloaders/stm32_dfu.c     |  96 ++++++
 platforms/chibios/bootloaders/stm32duino.c    |  24 ++
 platforms/chibios/bootloaders/tinyuf2.c       |  35 +++
 platforms/common.mk                           |   2 +-
 .../test/{bootloader.c => bootloaders/none.c} |   2 +-
 .../protocol/arm_atsam/arm_atsam_protocol.h   |   1 -
 tmk_core/protocol/arm_atsam/md_bootloader.h   |  24 --
 tmk_core/protocol/arm_atsam/startup.c         |   6 +-
 tmk_core/protocol/arm_atsam/usb/udc.c         |   5 +-
 32 files changed, 701 insertions(+), 493 deletions(-)
 rename platforms/arm_atsam/{bootloader.c => bootloaders/md_boot.c} (77%)
 delete mode 100644 platforms/avr/bootloader.c
 create mode 100644 platforms/avr/bootloaders/bootloadhid.c
 create mode 100644 platforms/avr/bootloaders/caterina.c
 create mode 100644 platforms/avr/bootloaders/dfu.c
 create mode 100644 platforms/avr/bootloaders/halfkay.c
 create mode 100644 platforms/avr/bootloaders/none.c
 create mode 100644 platforms/avr/bootloaders/usbasploader.c
 delete mode 100644 platforms/chibios/bootloader.c
 create mode 100644 platforms/chibios/bootloaders/gd32v_dfu.c
 create mode 100644 platforms/chibios/bootloaders/halfkay.c
 create mode 100644 platforms/chibios/bootloaders/kiibohd.c
 create mode 100644 platforms/chibios/bootloaders/none.c
 create mode 100644 platforms/chibios/bootloaders/stm32_dfu.c
 create mode 100644 platforms/chibios/bootloaders/stm32duino.c
 create mode 100644 platforms/chibios/bootloaders/tinyuf2.c
 rename platforms/test/{bootloader.c => bootloaders/none.c} (95%)
 delete mode 100644 tmk_core/protocol/arm_atsam/md_bootloader.h

diff --git a/build_test.mk b/build_test.mk
index 136a0455f0..ec6699b4ea 100644
--- a/build_test.mk
+++ b/build_test.mk
@@ -43,6 +43,7 @@ all: elf
 VPATH += $(COMMON_VPATH)
 PLATFORM:=TEST
 PLATFORM_KEY:=test
+BOOTLOADER_TYPE:=none
 
 ifeq ($(strip $(DEBUG)), 1)
 CONSOLE_ENABLE = yes
diff --git a/builddefs/bootloader.mk b/builddefs/bootloader.mk
index ccb92392d9..470febc346 100644
--- a/builddefs/bootloader.mk
+++ b/builddefs/bootloader.mk
@@ -30,6 +30,7 @@
 #     bootloadhid  HIDBootFlash compatible (ATmega32A)
 #     usbasploader USBaspLoader (ATmega328P)
 # ARM:
+#     halfkay      PJRC Teensy
 #     kiibohd      Input:Club Kiibohd bootloader (only used on their boards)
 #     stm32duino   STM32Duino (STM32F103x8)
 #     stm32-dfu    STM32 USB DFU in ROM
@@ -43,6 +44,8 @@
 ifeq ($(strip $(BOOTLOADER)), atmel-dfu)
     OPT_DEFS += -DBOOTLOADER_ATMEL_DFU
     OPT_DEFS += -DBOOTLOADER_DFU
+    BOOTLOADER_TYPE = dfu
+
     ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))
         BOOTLOADER_SIZE = 4096
     endif
@@ -53,6 +56,8 @@ endif
 ifeq ($(strip $(BOOTLOADER)), lufa-dfu)
     OPT_DEFS += -DBOOTLOADER_LUFA_DFU
     OPT_DEFS += -DBOOTLOADER_DFU
+    BOOTLOADER_TYPE = dfu
+
     ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))
         BOOTLOADER_SIZE ?= 4096
     endif
@@ -63,6 +68,8 @@ endif
 ifeq ($(strip $(BOOTLOADER)), qmk-dfu)
     OPT_DEFS += -DBOOTLOADER_QMK_DFU
     OPT_DEFS += -DBOOTLOADER_DFU
+    BOOTLOADER_TYPE = dfu
+
     ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))
         BOOTLOADER_SIZE ?= 4096
     endif
@@ -73,10 +80,14 @@ endif
 ifeq ($(strip $(BOOTLOADER)), qmk-hid)
     OPT_DEFS += -DBOOTLOADER_QMK_HID
     OPT_DEFS += -DBOOTLOADER_HID
+    BOOTLOADER_TYPE = dfu
+
     BOOTLOADER_SIZE ?= 4096
 endif
 ifeq ($(strip $(BOOTLOADER)), halfkay)
     OPT_DEFS += -DBOOTLOADER_HALFKAY
+    BOOTLOADER_TYPE = halfkay
+
     ifeq ($(strip $(MCU)), atmega32u4)
         BOOTLOADER_SIZE = 512
     endif
@@ -86,18 +97,26 @@ ifeq ($(strip $(BOOTLOADER)), halfkay)
 endif
 ifeq ($(strip $(BOOTLOADER)), caterina)
     OPT_DEFS += -DBOOTLOADER_CATERINA
+    BOOTLOADER_TYPE = caterina
+
     BOOTLOADER_SIZE = 4096
 endif
 ifneq (,$(filter $(BOOTLOADER), bootloadhid bootloadHID))
     OPT_DEFS += -DBOOTLOADER_BOOTLOADHID
+    BOOTLOADER_TYPE = bootloadhid
+
     BOOTLOADER_SIZE = 4096
 endif
 ifneq (,$(filter $(BOOTLOADER), usbasploader USBasp))
     OPT_DEFS += -DBOOTLOADER_USBASP
+    BOOTLOADER_TYPE = usbasploader
+
     BOOTLOADER_SIZE = 4096
 endif
 ifeq ($(strip $(BOOTLOADER)), lufa-ms)
     OPT_DEFS += -DBOOTLOADER_MS
+    BOOTLOADER_TYPE = dfu
+
     BOOTLOADER_SIZE ?= 8192
     FIRMWARE_FORMAT = bin
 cpfirmware: lufa_warning
@@ -115,6 +134,7 @@ endif
 
 ifeq ($(strip $(BOOTLOADER)), stm32-dfu)
     OPT_DEFS += -DBOOTLOADER_STM32_DFU
+    BOOTLOADER_TYPE = stm32_dfu
 
     # Options to pass to dfu-util when flashing
     DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
@@ -122,6 +142,7 @@ ifeq ($(strip $(BOOTLOADER)), stm32-dfu)
 endif
 ifeq ($(strip $(BOOTLOADER)), apm32-dfu)
     OPT_DEFS += -DBOOTLOADER_APM32_DFU
+    BOOTLOADER_TYPE = stm32_dfu
 
     # Options to pass to dfu-util when flashing
     DFU_ARGS ?= -d 314B:0106 -a 0 -s 0x08000000:leave
@@ -129,6 +150,7 @@ ifeq ($(strip $(BOOTLOADER)), apm32-dfu)
 endif
 ifeq ($(strip $(BOOTLOADER)), gd32v-dfu)
     OPT_DEFS += -DBOOTLOADER_GD32V_DFU
+    BOOTLOADER_TYPE = gd32v_dfu
 
     # Options to pass to dfu-util when flashing
     DFU_ARGS ?= -d 28E9:0189 -a 0 -s 0x08000000:leave
@@ -136,6 +158,8 @@ ifeq ($(strip $(BOOTLOADER)), gd32v-dfu)
 endif
 ifeq ($(strip $(BOOTLOADER)), kiibohd)
     OPT_DEFS += -DBOOTLOADER_KIIBOHD
+    BOOTLOADER_TYPE = kiibohd
+
     ifeq ($(strip $(MCU_ORIG)), MK20DX128)
         MCU_LDSCRIPT = MK20DX128BLDR4
     endif
@@ -151,8 +175,7 @@ ifeq ($(strip $(BOOTLOADER)), stm32duino)
     OPT_DEFS += -DBOOTLOADER_STM32DUINO
     MCU_LDSCRIPT = STM32F103x8_stm32duino_bootloader
     BOARD = STM32_F103_STM32DUINO
-    # STM32F103 does NOT have an USB bootloader in ROM (only serial), so setting anything here does not make much sense
-    STM32_BOOTLOADER_ADDRESS = 0x80000000
+    BOOTLOADER_TYPE = stm32duino
 
     # Options to pass to dfu-util when flashing
     DFU_ARGS = -d 1EAF:0003 -a 2 -R
@@ -160,4 +183,17 @@ ifeq ($(strip $(BOOTLOADER)), stm32duino)
 endif
 ifeq ($(strip $(BOOTLOADER)), tinyuf2)
     OPT_DEFS += -DBOOTLOADER_TINYUF2
+    BOOTLOADER_TYPE = tinyuf2
+endif
+ifeq ($(strip $(BOOTLOADER)), halfkay)
+    OPT_DEFS += -DBOOTLOADER_HALFKAY
+    BOOTLOADER_TYPE = halfkay
+endif
+ifeq ($(strip $(BOOTLOADER)), md-boot)
+    OPT_DEFS += -DBOOTLOADER_MD_BOOT
+    BOOTLOADER_TYPE = md_boot
+endif
+
+ifeq ($(strip $(BOOTLOADER_TYPE)),)
+    BOOTLOADER_TYPE = none
 endif
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
index 2daeaf04b4..94bbbe7e85 100644
--- a/data/schemas/keyboard.jsonschema
+++ b/data/schemas/keyboard.jsonschema
@@ -57,7 +57,7 @@
         },
         "bootloader": {
             "type": "string",
-            "enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2"],
+            "enum": ["atmel-dfu", "bootloadhid", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "md-boot", "micronucleus", "qmk-dfu", "qmk-hid", "stm32-dfu", "stm32duino", "gd32v-dfu", "wb32-dfu", "unknown", "usbasploader", "USBasp", "tinyuf2"],
         },
         "bootloader_instructions": {
             "type": "string",
diff --git a/keyboards/handwired/onekey/teensy_32/rules.mk b/keyboards/handwired/onekey/teensy_32/rules.mk
index c1b90d0d2b..937c9d5103 100644
--- a/keyboards/handwired/onekey/teensy_32/rules.mk
+++ b/keyboards/handwired/onekey/teensy_32/rules.mk
@@ -1,5 +1,8 @@
 # MCU name
 MCU = MK20DX256
 
+# Bootloader selection
+BOOTLOADER = halfkay
+
 # Enter lower-power sleep mode when on the ChibiOS idle thread
 OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE
diff --git a/keyboards/handwired/onekey/teensy_lc/rules.mk b/keyboards/handwired/onekey/teensy_lc/rules.mk
index b3daabe317..0e3c7edf7a 100644
--- a/keyboards/handwired/onekey/teensy_lc/rules.mk
+++ b/keyboards/handwired/onekey/teensy_lc/rules.mk
@@ -2,5 +2,8 @@
 MCU = MKL26Z64
 USE_CHIBIOS_CONTRIB = yes
 
+# Bootloader selection
+BOOTLOADER = halfkay
+
 # Enter lower-power sleep mode when on the ChibiOS idle thread
 OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE
diff --git a/keyboards/massdrop/alt/rules.mk b/keyboards/massdrop/alt/rules.mk
index 83d4fc590b..f0ff36fa35 100644
--- a/keyboards/massdrop/alt/rules.mk
+++ b/keyboards/massdrop/alt/rules.mk
@@ -5,6 +5,9 @@ SRC += config_led.c
 ARM_ATSAM = SAMD51J18A
 MCU = cortex-m4
 
+# Bootloader selection
+BOOTLOADER = md-boot
+
 # Build Options
 #   change yes to no to disable
 #
diff --git a/keyboards/massdrop/ctrl/rules.mk b/keyboards/massdrop/ctrl/rules.mk
index 667642f83d..a302c82d86 100644
--- a/keyboards/massdrop/ctrl/rules.mk
+++ b/keyboards/massdrop/ctrl/rules.mk
@@ -5,6 +5,9 @@ SRC += config_led.c
 ARM_ATSAM = SAMD51J18A
 MCU = cortex-m4
 
+# Bootloader selection
+BOOTLOADER = md-boot
+
 # Build Options
 #   change yes to no to disable
 #
diff --git a/keyboards/mechlovin/olly/jf/rules.mk b/keyboards/mechlovin/olly/jf/rules.mk
index c0c9b2d7ea..2fafe64f12 100644
--- a/keyboards/mechlovin/olly/jf/rules.mk
+++ b/keyboards/mechlovin/olly/jf/rules.mk
@@ -5,7 +5,7 @@ MCU = atmega32a
 F_CPU = 16000000
 
 # Bootloader selection
-BOOTLOADER = USBasp
+BOOTLOADER = usbasploader
 
 # Build Options
 #   change yes to no to disable
@@ -21,4 +21,4 @@ RGBLIGHT_ENABLE = yes       # Enable keyboard RGB underglow
 AUDIO_ENABLE = no           # Audio output
 CUSTOM_MATRIX = lite
 
-SRC += matrix.c
\ No newline at end of file
+SRC += matrix.c
diff --git a/keyboards/rocketboard_16/rules.mk b/keyboards/rocketboard_16/rules.mk
index d27da2c226..c07ebff427 100644
--- a/keyboards/rocketboard_16/rules.mk
+++ b/keyboards/rocketboard_16/rules.mk
@@ -8,7 +8,7 @@ MCU = STM32F103
 MCU_LDSCRIPT = STM32F103xB_stm32duino_bootloader
 OPT_DEFS += -DBOOTLOADER_STM32DUINO
 BOARD = STM32_F103_STM32DUINO
-STM32_BOOTLOADER_ADDRESS = 0x80000000
+BOOTLOADER_TYPE = stm32duino
 DFU_ARGS = -d 1EAF:0003 -a 2 -R
 DFU_SUFFIX_ARGS = -v 1EAF -p 0003
 
diff --git a/keyboards/tgr/jane/v2ce/rules.mk b/keyboards/tgr/jane/v2ce/rules.mk
index 0a865321a6..8fcc8d911f 100644
--- a/keyboards/tgr/jane/v2ce/rules.mk
+++ b/keyboards/tgr/jane/v2ce/rules.mk
@@ -2,7 +2,7 @@
 MCU = atmega32a
 
 # Bootloader selection
-BOOTLOADER = bootloadHID
+BOOTLOADER = bootloadhid
 
 # Build Options
 #   change yes to no to disable
diff --git a/platforms/arm_atsam/bootloader.c b/platforms/arm_atsam/bootloaders/md_boot.c
similarity index 77%
rename from platforms/arm_atsam/bootloader.c
rename to platforms/arm_atsam/bootloaders/md_boot.c
index 9015b00aab..fe07389487 100644
--- a/platforms/arm_atsam/bootloader.c
+++ b/platforms/arm_atsam/bootloaders/md_boot.c
@@ -15,13 +15,18 @@
  */
 
 #include "bootloader.h"
-#include "samd51j18a.h"
-#include "md_bootloader.h"
 
-// Set watchdog timer to reset. Directs the bootloader to stay in programming mode.
-void bootloader_jump(void) {
+#include "samd51j18a.h"
+
 #ifdef KEYBOARD_massdrop_ctrl
-    // CTRL keyboards released with bootloader version below must use RAM method. Otherwise use WDT method.
+// WARNING: These are only for CTRL bootloader release "v2.18Jun 22 2018 17:28:08" for bootloader_jump support
+extern uint32_t _eram;
+
+#define BOOTLOADER_MAGIC 0x3B9ACA00
+#define MAGIC_ADDR (uint32_t *)((intptr_t)(&_eram) - 4)
+
+// CTRL keyboards released with bootloader version below must use RAM method. Otherwise use WDT method.
+void bootloader_jump(void) {
     uint8_t  ver_ram_method[] = "v2.18Jun 22 2018 17:28:08";  // The version to match (NULL terminated by compiler)
     uint8_t *ver_check        = ver_ram_method;               // Pointer to version match string for traversal
     uint8_t *ver_rom          = (uint8_t *)0x21A0;            // Pointer to address in ROM where this specific bootloader version would exist
@@ -34,24 +39,28 @@ void bootloader_jump(void) {
     if (!*ver_check) {                   // If check version pointer is NULL, all characters have matched
         *MAGIC_ADDR = BOOTLOADER_MAGIC;  // Set magic number into RAM
         NVIC_SystemReset();              // Perform system reset
-        while (1) {
-        }  // Won't get here
-    }
-#endif
 
+        while (1);  // Won't get here
+    }
+}
+
+#else
+
+// Set watchdog timer to reset. Directs the bootloader to stay in programming mode.
+void bootloader_jump(void) {
     WDT->CTRLA.bit.ENABLE = 0;
-    while (WDT->SYNCBUSY.bit.ENABLE) {
-    }
-    while (WDT->CTRLA.bit.ENABLE) {
-    }
+
+    while (WDT->SYNCBUSY.bit.ENABLE);
+    while (WDT->CTRLA.bit.ENABLE);
+
     WDT->CONFIG.bit.WINDOW   = 0;
     WDT->CONFIG.bit.PER      = 0;
     WDT->EWCTRL.bit.EWOFFSET = 0;
     WDT->CTRLA.bit.ENABLE    = 1;
-    while (WDT->SYNCBUSY.bit.ENABLE) {
-    }
-    while (!WDT->CTRLA.bit.ENABLE) {
-    }
-    while (1) {
-    }  // Wait on timeout
+
+    while (WDT->SYNCBUSY.bit.ENABLE);
+    while (!WDT->CTRLA.bit.ENABLE);
+
+    while (1);  // Wait on timeout
 }
+#endif
diff --git a/platforms/avr/bootloader.c b/platforms/avr/bootloader.c
deleted file mode 100644
index c0272903b8..0000000000
--- a/platforms/avr/bootloader.c
+++ /dev/null
@@ -1,293 +0,0 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include <avr/io.h>
-#include <avr/eeprom.h>
-#include <avr/interrupt.h>
-#include <avr/wdt.h>
-#include <util/delay.h>
-#include "bootloader.h"
-#include <avr/boot.h>
-
-#ifdef PROTOCOL_LUFA
-#    include <LUFA/Drivers/USB/USB.h>
-#endif
-
-/** \brief Bootloader Size in *bytes*
- *
- * AVR Boot section size are defined by setting BOOTSZ fuse in fact. Consult with your MCU datasheet.
- * Note that 'Word'(2 bytes) size and address are used in datasheet while TMK uses 'Byte'.
- *
- * Size of Bootloaders in bytes:
- *   Atmel DFU loader(ATmega32U4)   4096
- *   Atmel DFU loader(AT90USB128)   8192
- *   LUFA bootloader(ATmega32U4)    4096
- *   Arduino Caterina(ATmega32U4)   4096
- *   USBaspLoader(ATmega***)        2048
- *   Teensy   halfKay(ATmega32U4)   512
- *   Teensy++ halfKay(AT90USB128)   1024
- *
- * AVR Boot section is located at the end of Flash memory like the followings.
- *
- * byte     Atmel/LUFA(ATMega32u4)          byte     Atmel(AT90SUB128)
- * 0x0000   +---------------+               0x00000  +---------------+
- *          |               |                        |               |
- *          |               |                        |               |
- *          |  Application  |                        |  Application  |
- *          |               |                        |               |
- *          =               =                        =               =
- *          |               | 32KB-4KB               |               | 128KB-8KB
- * 0x7000   +---------------+               0x1E000  +---------------+
- *          |  Bootloader   | 4KB                    |  Bootloader   | 8KB
- * 0x7FFF   +---------------+               0x1FFFF  +---------------+
- *
- *
- * byte     Teensy(ATMega32u4)              byte     Teensy++(AT90SUB128)
- * 0x0000   +---------------+               0x00000  +---------------+
- *          |               |                        |               |
- *          |               |                        |               |
- *          |  Application  |                        |  Application  |
- *          |               |                        |               |
- *          =               =                        =               =
- *          |               | 32KB-512B              |               | 128KB-1KB
- * 0x7E00   +---------------+               0x1FC00  +---------------+
- *          |  Bootloader   | 512B                   |  Bootloader   | 1KB
- * 0x7FFF   +---------------+               0x1FFFF  +---------------+
- */
-#define FLASH_SIZE (FLASHEND + 1L)
-
-#if !defined(BOOTLOADER_SIZE)
-uint16_t bootloader_start;
-#endif
-
-// compatibility between ATMega8 and ATMega88
-#if !defined(MCUCSR)
-#    if defined(MCUSR)
-#        define MCUCSR MCUSR
-#    endif
-#endif
-
-/** \brief Entering the Bootloader via Software
- *
- * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html
- */
-#define BOOTLOADER_RESET_KEY 0xB007B007
-uint32_t reset_key __attribute__((section(".noinit,\"aw\",@nobits;")));
-
-/** \brief initialize MCU status by watchdog reset
- *
- * FIXME: needs doc
- */
-__attribute__((weak)) void bootloader_jump(void) {
-#if !defined(BOOTLOADER_SIZE)
-    uint8_t high_fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
-
-    if (high_fuse & ~(FUSE_BOOTSZ0 & FUSE_BOOTSZ1)) {
-        bootloader_start = (FLASH_SIZE - 512) >> 1;
-    } else if (high_fuse & ~(FUSE_BOOTSZ1)) {
-        bootloader_start = (FLASH_SIZE - 1024) >> 1;
-    } else if (high_fuse & ~(FUSE_BOOTSZ0)) {
-        bootloader_start = (FLASH_SIZE - 2048) >> 1;
-    } else {
-        bootloader_start = (FLASH_SIZE - 4096) >> 1;
-    }
-#endif
-
-    // Something like this might work, but it compiled larger than the block above
-    // bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1));
-
-#if defined(BOOTLOADER_HALFKAY)
-    //  http://www.pjrc.com/teensy/jump_to_bootloader.html
-    cli();
-    // disable watchdog, if enabled (it's not)
-    // disable all peripherals
-    // a shutdown call might make sense here
-    UDCON  = 1;
-    USBCON = (1 << FRZCLK);  // disable USB
-    UCSR1B = 0;
-    _delay_ms(5);
-#    if defined(__AVR_AT90USB162__)  // Teensy 1.0
-    EIMSK  = 0;
-    PCICR  = 0;
-    SPCR   = 0;
-    ACSR   = 0;
-    EECR   = 0;
-    TIMSK0 = 0;
-    TIMSK1 = 0;
-    UCSR1B = 0;
-    DDRB   = 0;
-    DDRC   = 0;
-    DDRD   = 0;
-    PORTB  = 0;
-    PORTC  = 0;
-    PORTD  = 0;
-    asm volatile("jmp 0x3E00");
-#    elif defined(__AVR_ATmega32U4__)   // Teensy 2.0
-    EIMSK  = 0;
-    PCICR  = 0;
-    SPCR   = 0;
-    ACSR   = 0;
-    EECR   = 0;
-    ADCSRA = 0;
-    TIMSK0 = 0;
-    TIMSK1 = 0;
-    TIMSK3 = 0;
-    TIMSK4 = 0;
-    UCSR1B = 0;
-    TWCR   = 0;
-    DDRB   = 0;
-    DDRC   = 0;
-    DDRD   = 0;
-    DDRE   = 0;
-    DDRF   = 0;
-    TWCR   = 0;
-    PORTB  = 0;
-    PORTC  = 0;
-    PORTD  = 0;
-    PORTE  = 0;
-    PORTF  = 0;
-    asm volatile("jmp 0x7E00");
-#    elif defined(__AVR_AT90USB646__)   // Teensy++ 1.0
-    EIMSK  = 0;
-    PCICR  = 0;
-    SPCR   = 0;
-    ACSR   = 0;
-    EECR   = 0;
-    ADCSRA = 0;
-    TIMSK0 = 0;
-    TIMSK1 = 0;
-    TIMSK2 = 0;
-    TIMSK3 = 0;
-    UCSR1B = 0;
-    TWCR   = 0;
-    DDRA   = 0;
-    DDRB   = 0;
-    DDRC   = 0;
-    DDRD   = 0;
-    DDRE   = 0;
-    DDRF   = 0;
-    PORTA  = 0;
-    PORTB  = 0;
-    PORTC  = 0;
-    PORTD  = 0;
-    PORTE  = 0;
-    PORTF  = 0;
-    asm volatile("jmp 0xFC00");
-#    elif defined(__AVR_AT90USB1286__)  // Teensy++ 2.0
-    EIMSK  = 0;
-    PCICR  = 0;
-    SPCR   = 0;
-    ACSR   = 0;
-    EECR   = 0;
-    ADCSRA = 0;
-    TIMSK0 = 0;
-    TIMSK1 = 0;
-    TIMSK2 = 0;
-    TIMSK3 = 0;
-    UCSR1B = 0;
-    TWCR   = 0;
-    DDRA   = 0;
-    DDRB   = 0;
-    DDRC   = 0;
-    DDRD   = 0;
-    DDRE   = 0;
-    DDRF   = 0;
-    PORTA  = 0;
-    PORTB  = 0;
-    PORTC  = 0;
-    PORTD  = 0;
-    PORTE  = 0;
-    PORTF  = 0;
-    asm volatile("jmp 0x1FC00");
-#    endif
-
-#elif defined(BOOTLOADER_CATERINA)
-    // this block may be optional
-    // TODO: figure it out
-
-    uint16_t *const bootKeyPtr = (uint16_t *)0x0800;
-
-    // Value used by Caterina bootloader use to determine whether to run the
-    // sketch or the bootloader programmer.
-    uint16_t bootKey = 0x7777;
-
-    *bootKeyPtr = bootKey;
-
-    // setup watchdog timeout
-    wdt_enable(WDTO_60MS);
-
-    while (1) {
-    }  // wait for watchdog timer to trigger
-
-#elif defined(BOOTLOADER_USBASP)
-    // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c
-    wdt_enable(WDTO_15MS);
-    wdt_reset();
-    asm volatile("cli                    \n\t"
-                 "ldi    r29 ,       %[ramendhi] \n\t"
-                 "ldi    r28 ,       %[ramendlo] \n\t"
-#    if (FLASHEND > 131071)
-                 "ldi    r18 ,       %[bootaddrhi]   \n\t"
-                 "st     Y+,         r18     \n\t"
-#    endif
-                 "ldi    r18 ,       %[bootaddrme]   \n\t"
-                 "st     Y+,     r18     \n\t"
-                 "ldi    r18 ,       %[bootaddrlo]   \n\t"
-                 "st     Y+,     r18     \n\t"
-                 "out    %[mcucsrio],    __zero_reg__    \n\t"
-                 "bootloader_startup_loop%=:         \n\t"
-                 "rjmp bootloader_startup_loop%=     \n\t"
-                 :
-                 : [mcucsrio] "I"(_SFR_IO_ADDR(MCUCSR)),
-#    if (FLASHEND > 131071)
-                   [ramendhi] "M"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff),
-#    else
-                   [ramendhi] "M"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 1) >> 0) & 0xff),
-#    endif
-                   [bootaddrme] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff));
-
-#else  // Assume remaining boards are DFU, even if the flag isn't set
-
-#    if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATtiny85__))  // no USB - maybe BOOTLOADER_BOOTLOADHID instead though?
-    UDCON  = 1;
-    USBCON = (1 << FRZCLK);  // disable USB
-    UCSR1B = 0;
-    _delay_ms(5);  // 5 seems to work fine
-#    endif
-
-#    ifdef BOOTLOADER_BOOTLOADHID
-    // force bootloadHID to stay in bootloader mode, so that it waits
-    // for a new firmware to be flashed
-    eeprom_write_byte((uint8_t *)1, 0x00);
-#    endif
-
-    // watchdog reset
-    reset_key = BOOTLOADER_RESET_KEY;
-    wdt_enable(WDTO_250MS);
-    for (;;)
-        ;
-#endif
-}
-
-/* this runs before main() */
-void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3")));
-void bootloader_jump_after_watchdog_reset(void) {
-#ifndef BOOTLOADER_HALFKAY
-    if ((MCUCSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
-        reset_key = 0;
-
-        // My custom USBasploader requires this to come up.
-        MCUCSR = 0;
-
-        // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog.
-        MCUCSR &= ~(1 << WDRF);
-        wdt_disable();
-
-// This is compled into 'icall', address should be in word unit, not byte.
-#    ifdef BOOTLOADER_SIZE
-        ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))();
-#    else
-        asm("ijmp" ::"z"(bootloader_start));
-#    endif
-    }
-#endif
-}
diff --git a/platforms/avr/bootloaders/bootloadhid.c b/platforms/avr/bootloaders/bootloadhid.c
new file mode 100644
index 0000000000..8d18114613
--- /dev/null
+++ b/platforms/avr/bootloaders/bootloadhid.c
@@ -0,0 +1,34 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <avr/eeprom.h>
+#include <avr/wdt.h>
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    // force bootloadHID to stay in bootloader mode, so that it waits
+    // for a new firmware to be flashed
+    // NOTE: this byte is part of QMK's "magic number" - changing it causes the EEPROM to be re-initialized
+    // thus every time the device is flashed the EEPROM will be wiped
+    eeprom_write_byte((uint8_t *)1, 0x00);
+
+    // watchdog reset
+    wdt_enable(WDTO_250MS);
+    for (;;)
+        ;
+}
diff --git a/platforms/avr/bootloaders/caterina.c b/platforms/avr/bootloaders/caterina.c
new file mode 100644
index 0000000000..1b43ed4e44
--- /dev/null
+++ b/platforms/avr/bootloaders/caterina.c
@@ -0,0 +1,39 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <avr/wdt.h>
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    // this block may be optional
+    // TODO: figure it out
+
+    uint16_t *const bootKeyPtr = (uint16_t *)0x0800;
+
+    // Value used by Caterina bootloader use to determine whether to run the
+    // sketch or the bootloader programmer.
+    uint16_t bootKey = 0x7777;
+
+    *bootKeyPtr = bootKey;
+
+    // setup watchdog timeout
+    wdt_enable(WDTO_60MS);
+
+    // wait for watchdog timer to trigger
+    while (1) { }
+}
diff --git a/platforms/avr/bootloaders/dfu.c b/platforms/avr/bootloaders/dfu.c
new file mode 100644
index 0000000000..bdf5908934
--- /dev/null
+++ b/platforms/avr/bootloaders/dfu.c
@@ -0,0 +1,53 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <avr/wdt.h>
+#include <util/delay.h>
+
+#define FLASH_SIZE (FLASHEND + 1L)
+
+/** \brief Entering the Bootloader via Software
+ *
+ * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html
+ */
+#define BOOTLOADER_RESET_KEY 0xB007B007
+uint32_t reset_key __attribute__((section(".noinit,\"aw\",@nobits;")));
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    UDCON  = 1;
+    USBCON = (1 << FRZCLK);  // disable USB
+    UCSR1B = 0;
+    _delay_ms(5);  // 5 seems to work fine
+
+    // watchdog reset
+    reset_key = BOOTLOADER_RESET_KEY;
+    wdt_enable(WDTO_250MS);
+    for (;;)
+        ;
+}
+
+/* this runs before main() */
+void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3")));
+void bootloader_jump_after_watchdog_reset(void) {
+    if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
+        reset_key = 0;
+
+        ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))();
+    }
+}
diff --git a/platforms/avr/bootloaders/halfkay.c b/platforms/avr/bootloaders/halfkay.c
new file mode 100644
index 0000000000..abef86980b
--- /dev/null
+++ b/platforms/avr/bootloaders/halfkay.c
@@ -0,0 +1,129 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    // http://www.pjrc.com/teensy/jump_to_bootloader.html
+
+    cli();
+    // disable watchdog, if enabled (it's not)
+    // disable all peripherals
+    // a shutdown call might make sense here
+    UDCON  = 1;
+    USBCON = (1 << FRZCLK);  // disable USB
+    UCSR1B = 0;
+    _delay_ms(5);
+
+#if defined(__AVR_AT90USB162__)  // Teensy 1.0
+    EIMSK  = 0;
+    PCICR  = 0;
+    SPCR   = 0;
+    ACSR   = 0;
+    EECR   = 0;
+    TIMSK0 = 0;
+    TIMSK1 = 0;
+    UCSR1B = 0;
+    DDRB   = 0;
+    DDRC   = 0;
+    DDRD   = 0;
+    PORTB  = 0;
+    PORTC  = 0;
+    PORTD  = 0;
+    asm volatile("jmp 0x3E00");
+#elif defined(__AVR_ATmega32U4__)   // Teensy 2.0
+    EIMSK  = 0;
+    PCICR  = 0;
+    SPCR   = 0;
+    ACSR   = 0;
+    EECR   = 0;
+    ADCSRA = 0;
+    TIMSK0 = 0;
+    TIMSK1 = 0;
+    TIMSK3 = 0;
+    TIMSK4 = 0;
+    UCSR1B = 0;
+    TWCR   = 0;
+    DDRB   = 0;
+    DDRC   = 0;
+    DDRD   = 0;
+    DDRE   = 0;
+    DDRF   = 0;
+    TWCR   = 0;
+    PORTB  = 0;
+    PORTC  = 0;
+    PORTD  = 0;
+    PORTE  = 0;
+    PORTF  = 0;
+    asm volatile("jmp 0x7E00");
+#elif defined(__AVR_AT90USB646__)   // Teensy++ 1.0
+    EIMSK  = 0;
+    PCICR  = 0;
+    SPCR   = 0;
+    ACSR   = 0;
+    EECR   = 0;
+    ADCSRA = 0;
+    TIMSK0 = 0;
+    TIMSK1 = 0;
+    TIMSK2 = 0;
+    TIMSK3 = 0;
+    UCSR1B = 0;
+    TWCR   = 0;
+    DDRA   = 0;
+    DDRB   = 0;
+    DDRC   = 0;
+    DDRD   = 0;
+    DDRE   = 0;
+    DDRF   = 0;
+    PORTA  = 0;
+    PORTB  = 0;
+    PORTC  = 0;
+    PORTD  = 0;
+    PORTE  = 0;
+    PORTF  = 0;
+    asm volatile("jmp 0xFC00");
+#elif defined(__AVR_AT90USB1286__)  // Teensy++ 2.0
+    EIMSK  = 0;
+    PCICR  = 0;
+    SPCR   = 0;
+    ACSR   = 0;
+    EECR   = 0;
+    ADCSRA = 0;
+    TIMSK0 = 0;
+    TIMSK1 = 0;
+    TIMSK2 = 0;
+    TIMSK3 = 0;
+    UCSR1B = 0;
+    TWCR   = 0;
+    DDRA   = 0;
+    DDRB   = 0;
+    DDRC   = 0;
+    DDRD   = 0;
+    DDRE   = 0;
+    DDRF   = 0;
+    PORTA  = 0;
+    PORTB  = 0;
+    PORTC  = 0;
+    PORTD  = 0;
+    PORTE  = 0;
+    PORTF  = 0;
+    asm volatile("jmp 0x1FC00");
+#endif
+}
diff --git a/platforms/avr/bootloaders/none.c b/platforms/avr/bootloaders/none.c
new file mode 100644
index 0000000000..8af151c04d
--- /dev/null
+++ b/platforms/avr/bootloaders/none.c
@@ -0,0 +1,20 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+__attribute__((weak))
+void bootloader_jump(void) { }
diff --git a/platforms/avr/bootloaders/usbasploader.c b/platforms/avr/bootloaders/usbasploader.c
new file mode 100644
index 0000000000..b3e9b2d72d
--- /dev/null
+++ b/platforms/avr/bootloaders/usbasploader.c
@@ -0,0 +1,57 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <avr/wdt.h>
+
+#define FLASH_SIZE (FLASHEND + 1L)
+
+#if !defined(MCUCSR)
+#    if defined(MCUSR)
+#        define MCUCSR MCUSR
+#    endif
+#endif
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c
+
+    wdt_enable(WDTO_15MS);
+    wdt_reset();
+    asm volatile("cli                    \n\t"
+                 "ldi    r29 ,       %[ramendhi] \n\t"
+                 "ldi    r28 ,       %[ramendlo] \n\t"
+#if (FLASHEND > 131071)
+                 "ldi    r18 ,       %[bootaddrhi]   \n\t"
+                 "st     Y+,         r18     \n\t"
+#endif
+                 "ldi    r18 ,       %[bootaddrme]   \n\t"
+                 "st     Y+,     r18     \n\t"
+                 "ldi    r18 ,       %[bootaddrlo]   \n\t"
+                 "st     Y+,     r18     \n\t"
+                 "out    %[mcucsrio],    __zero_reg__    \n\t"
+                 "bootloader_startup_loop%=:         \n\t"
+                 "rjmp bootloader_startup_loop%=     \n\t"
+                 :
+                 : [mcucsrio] "I"(_SFR_IO_ADDR(MCUCSR)),
+#if (FLASHEND > 131071)
+                   [ramendhi] "M"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff),
+#else
+                   [ramendhi] "M"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 1) >> 0) & 0xff),
+#endif
+                   [bootaddrme] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff));
+}
diff --git a/platforms/chibios/bootloader.c b/platforms/chibios/bootloader.c
deleted file mode 100644
index 58212948b0..0000000000
--- a/platforms/chibios/bootloader.c
+++ /dev/null
@@ -1,145 +0,0 @@
-#include "bootloader.h"
-
-#include <ch.h>
-#include <hal.h>
-#include "wait.h"
-
-/* This code should be checked whether it runs correctly on platforms */
-#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
-#define BOOTLOADER_MAGIC 0xDEADBEEF
-#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)
-
-#ifndef STM32_BOOTLOADER_DUAL_BANK
-#    define STM32_BOOTLOADER_DUAL_BANK FALSE
-#endif
-
-#ifdef BOOTLOADER_TINYUF2
-
-#    define DBL_TAP_MAGIC 0xf01669ef  // From tinyuf2's board_api.h
-
-// defined by linker script
-extern uint32_t _board_dfu_dbl_tap[];
-#    define DBL_TAP_REG _board_dfu_dbl_tap[0]
-
-void bootloader_jump(void) {
-    DBL_TAP_REG = DBL_TAP_MAGIC;
-    NVIC_SystemReset();
-}
-
-void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */
-}
-
-#elif STM32_BOOTLOADER_DUAL_BANK
-
-// Need pin definitions
-#    include "config_common.h"
-
-#    ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO
-#        error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle"
-#    endif
-
-#    ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY
-#        define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0
-#    endif
-
-#    ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY
-#        define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000
-#    endif
-
-extern uint32_t __ram0_end__;
-
-__attribute__((weak)) void bootloader_jump(void) {
-    // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash
-    // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do
-    // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to
-    // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord
-    // #hardware channel pins for an example circuit.
-    palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL);
-#    if STM32_BOOTLOADER_DUAL_BANK_POLARITY
-    palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
-#    else
-    palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
-#    endif
-
-    // Wait for a while for the capacitor to charge
-    wait_ms(100);
-
-    // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high
-    NVIC_SystemReset();
-}
-
-void enter_bootloader_mode_if_requested(void) {}  // not needed at all, but if anybody attempts to invoke it....
-
-#elif defined(STM32_BOOTLOADER_ADDRESS)  // STM32_BOOTLOADER_DUAL_BANK
-
-extern uint32_t __ram0_end__;
-
-__attribute__((weak)) void bootloader_jump(void) {
-    *MAGIC_ADDR = BOOTLOADER_MAGIC;  // set magic flag => reset handler will jump into boot loader
-    NVIC_SystemReset();
-}
-
-void enter_bootloader_mode_if_requested(void) {
-    unsigned long *check = MAGIC_ADDR;
-    if (*check == BOOTLOADER_MAGIC) {
-        *check = 0;
-        __set_CONTROL(0);
-        __set_MSP(*(__IO uint32_t *)STM32_BOOTLOADER_ADDRESS);
-        __enable_irq();
-
-        typedef void (*BootJump_t)(void);
-        BootJump_t boot_jump = *(BootJump_t *)(STM32_BOOTLOADER_ADDRESS + 4);
-        boot_jump();
-        while (1)
-            ;
-    }
-}
-
-#elif defined(GD32VF103)
-
-#    define DBGMCU_KEY_UNLOCK 0x4B5A6978
-#    define DBGMCU_CMD_RESET 0x1
-
-__IO uint32_t *DBGMCU_KEY = (uint32_t *)DBGMCU_BASE + 0x0CU;
-__IO uint32_t *DBGMCU_CMD = (uint32_t *)DBGMCU_BASE + 0x08U;
-
-__attribute__((weak)) void bootloader_jump(void) {
-    /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST
-     * register to generate a software reset request.
-     * BUT instead two undocumented registers in the debug peripheral
-     * that allow issueing a software reset. WHO would need the MSFRST
-     * register anyway? Source:
-     * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */
-    *DBGMCU_KEY = DBGMCU_KEY_UNLOCK;
-    *DBGMCU_CMD = DBGMCU_CMD_RESET;
-}
-
-void enter_bootloader_mode_if_requested(void) { /* Jumping to bootloader is not possible from user code. */
-}
-
-#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062)  // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
-/* Kinetis */
-
-#    if defined(BOOTLOADER_KIIBOHD)
-/* Kiibohd Bootloader (MCHCK and Infinity KB) */
-#        define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000
-const uint8_t              sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
-__attribute__((weak)) void bootloader_jump(void) {
-    void *volatile vbat = (void *)VBAT;
-    __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
-    // request reset
-    SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;
-}
-
-#    else /* defined(BOOTLOADER_KIIBOHD) */
-/* Default for Kinetis - expecting an ARM Teensy */
-#        include "wait.h"
-__attribute__((weak)) void bootloader_jump(void) {
-    wait_ms(100);
-    __BKPT(0);
-}
-#    endif /* defined(BOOTLOADER_KIIBOHD) */
-
-#else /* neither STM32 nor KINETIS */
-__attribute__((weak)) void bootloader_jump(void) {}
-#endif
diff --git a/platforms/chibios/bootloaders/gd32v_dfu.c b/platforms/chibios/bootloaders/gd32v_dfu.c
new file mode 100644
index 0000000000..1ab0882084
--- /dev/null
+++ b/platforms/chibios/bootloaders/gd32v_dfu.c
@@ -0,0 +1,40 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+
+#define DBGMCU_KEY_UNLOCK 0x4B5A6978
+#define DBGMCU_CMD_RESET 0x1
+
+__IO uint32_t *DBGMCU_KEY = (uint32_t *)DBGMCU_BASE + 0x0CU;
+__IO uint32_t *DBGMCU_CMD = (uint32_t *)DBGMCU_BASE + 0x08U;
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST
+     * register to generate a software reset request.
+     * BUT instead two undocumented registers in the debug peripheral
+     * that allow issueing a software reset. WHO would need the MSFRST
+     * register anyway? Source:
+     * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */
+    *DBGMCU_KEY = DBGMCU_KEY_UNLOCK;
+    *DBGMCU_CMD = DBGMCU_CMD_RESET;
+}
+
+/* Jumping to bootloader is not possible from user code. */
+void enter_bootloader_mode_if_requested(void) { }
diff --git a/platforms/chibios/bootloaders/halfkay.c b/platforms/chibios/bootloaders/halfkay.c
new file mode 100644
index 0000000000..fb9b73dfb3
--- /dev/null
+++ b/platforms/chibios/bootloaders/halfkay.c
@@ -0,0 +1,26 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+#include "wait.h"
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    wait_ms(100);
+    __BKPT(0);
+}
diff --git a/platforms/chibios/bootloaders/kiibohd.c b/platforms/chibios/bootloaders/kiibohd.c
new file mode 100644
index 0000000000..f4bb18f058
--- /dev/null
+++ b/platforms/chibios/bootloaders/kiibohd.c
@@ -0,0 +1,33 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+
+/* Kiibohd Bootloader (MCHCK and Infinity KB) */
+#define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000
+
+const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    void *volatile vbat = (void *)VBAT;
+    __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));
+
+    // request reset
+    SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;
+}
diff --git a/platforms/chibios/bootloaders/none.c b/platforms/chibios/bootloaders/none.c
new file mode 100644
index 0000000000..8af151c04d
--- /dev/null
+++ b/platforms/chibios/bootloaders/none.c
@@ -0,0 +1,20 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+__attribute__((weak))
+void bootloader_jump(void) { }
diff --git a/platforms/chibios/bootloaders/stm32_dfu.c b/platforms/chibios/bootloaders/stm32_dfu.c
new file mode 100644
index 0000000000..f4bd8554bf
--- /dev/null
+++ b/platforms/chibios/bootloaders/stm32_dfu.c
@@ -0,0 +1,96 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+#include <hal.h>
+#include "wait.h"
+
+extern uint32_t __ram0_end__;
+
+#ifndef STM32_BOOTLOADER_DUAL_BANK
+#    define STM32_BOOTLOADER_DUAL_BANK FALSE
+#endif
+
+#if STM32_BOOTLOADER_DUAL_BANK
+#    include "config_common.h"
+
+#    ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO
+#        error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle"
+#    endif
+
+#    ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY
+#        define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0
+#    endif
+
+#    ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY
+#        define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000
+#    endif
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash
+    // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do
+    // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to
+    // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord
+    // #hardware channel pins for an example circuit.
+    palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL);
+#    if STM32_BOOTLOADER_DUAL_BANK_POLARITY
+    palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
+#    else
+    palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
+#    endif
+
+    // Wait for a while for the capacitor to charge
+    wait_ms(100);
+
+    // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high
+    NVIC_SystemReset();
+}
+
+// not needed at all, but if anybody attempts to invoke it....
+void enter_bootloader_mode_if_requested(void) { }
+
+#else
+
+/* This code should be checked whether it runs correctly on platforms */
+#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
+#define BOOTLOADER_MAGIC 0xDEADBEEF
+#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    *MAGIC_ADDR = BOOTLOADER_MAGIC;  // set magic flag => reset handler will jump into boot loader
+    NVIC_SystemReset();
+}
+
+void enter_bootloader_mode_if_requested(void) {
+    unsigned long *check = MAGIC_ADDR;
+    if (*check == BOOTLOADER_MAGIC) {
+        *check = 0;
+        __set_CONTROL(0);
+        __set_MSP(*(__IO uint32_t *)STM32_BOOTLOADER_ADDRESS);
+        __enable_irq();
+
+        typedef void (*BootJump_t)(void);
+        BootJump_t boot_jump = *(BootJump_t *)(STM32_BOOTLOADER_ADDRESS + 4);
+        boot_jump();
+        while (1)
+            ;
+    }
+}
+#endif
diff --git a/platforms/chibios/bootloaders/stm32duino.c b/platforms/chibios/bootloaders/stm32duino.c
new file mode 100644
index 0000000000..6ff9e28b4d
--- /dev/null
+++ b/platforms/chibios/bootloaders/stm32duino.c
@@ -0,0 +1,24 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    NVIC_SystemReset();
+}
diff --git a/platforms/chibios/bootloaders/tinyuf2.c b/platforms/chibios/bootloaders/tinyuf2.c
new file mode 100644
index 0000000000..ce82fa0c49
--- /dev/null
+++ b/platforms/chibios/bootloaders/tinyuf2.c
@@ -0,0 +1,35 @@
+/* Copyright 2021 QMK
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "bootloader.h"
+
+#include <ch.h>
+
+// From tinyuf2's board_api.h
+#define DBL_TAP_MAGIC 0xF01669EF
+
+// defined by linker script
+extern uint32_t _board_dfu_dbl_tap[];
+#define DBL_TAP_REG _board_dfu_dbl_tap[0]
+
+__attribute__((weak))
+void bootloader_jump(void) {
+    DBL_TAP_REG = DBL_TAP_MAGIC;
+    NVIC_SystemReset();
+}
+
+/* not needed, no two-stage reset */
+void enter_bootloader_mode_if_requested(void) { }
diff --git a/platforms/common.mk b/platforms/common.mk
index f7a0fc7028..12ab45f823 100644
--- a/platforms/common.mk
+++ b/platforms/common.mk
@@ -4,7 +4,7 @@ TMK_COMMON_SRC +=	\
 	$(PLATFORM_COMMON_DIR)/platform.c \
 	$(PLATFORM_COMMON_DIR)/suspend.c \
 	$(PLATFORM_COMMON_DIR)/timer.c \
-	$(PLATFORM_COMMON_DIR)/bootloader.c \
+	$(PLATFORM_COMMON_DIR)/bootloaders/$(BOOTLOADER_TYPE).c
 
 # Search Path
 VPATH += $(PLATFORM_PATH)
diff --git a/platforms/test/bootloader.c b/platforms/test/bootloaders/none.c
similarity index 95%
rename from platforms/test/bootloader.c
rename to platforms/test/bootloaders/none.c
index 5155d9ff04..2920c68f18 100644
--- a/platforms/test/bootloader.c
+++ b/platforms/test/bootloaders/none.c
@@ -16,4 +16,4 @@
 
 #include "bootloader.h"
 
-void bootloader_jump(void) {}
+void bootloader_jump(void) { }
diff --git a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
index e1749f872d..319ff8487d 100644
--- a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
+++ b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
@@ -19,7 +19,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define _ARM_ATSAM_PROTOCOL_H_
 
 #include "samd51j18a.h"
-#include "md_bootloader.h"
 
 #include "timer.h"
 #include "d51_util.h"
diff --git a/tmk_core/protocol/arm_atsam/md_bootloader.h b/tmk_core/protocol/arm_atsam/md_bootloader.h
deleted file mode 100644
index 362b9bd52a..0000000000
--- a/tmk_core/protocol/arm_atsam/md_bootloader.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef _MD_BOOTLOADER_H_
-#define _MD_BOOTLOADER_H_
-
-extern uint32_t _srom;
-extern uint32_t _lrom;
-extern uint32_t _erom;
-
-#define BOOTLOADER_SERIAL_MAX_SIZE 20  // DO NOT MODIFY!
-
-#ifdef KEYBOARD_massdrop_ctrl
-// WARNING: These are only for CTRL bootloader release "v2.18Jun 22 2018 17:28:08" for bootloader_jump support
-extern uint32_t _eram;
-#    define BOOTLOADER_MAGIC 0x3B9ACA00
-#    define MAGIC_ADDR (uint32_t *)((intptr_t)(&_eram) - 4)
-#endif
-
-#ifdef MD_BOOTLOADER
-
-#    define MCU_HZ 48000000
-#    define I2C_HZ 0  // Not used
-
-#endif  // MD_BOOTLOADER
-
-#endif  //_MD_BOOTLOADER_H_
diff --git a/tmk_core/protocol/arm_atsam/startup.c b/tmk_core/protocol/arm_atsam/startup.c
index 7a5791ab55..ce043bad51 100644
--- a/tmk_core/protocol/arm_atsam/startup.c
+++ b/tmk_core/protocol/arm_atsam/startup.c
@@ -28,7 +28,6 @@
  */
 
 #include "samd51.h"
-#include "md_bootloader.h"
 
 /* Initialize segments */
 extern uint32_t _sfixed;
@@ -496,6 +495,11 @@ __attribute__((section(".vectors"))) const DeviceVectors exception_table = {
 #endif
 };
 
+// WARNING: These are only for CTRL bootloader release "v2.18Jun 22 2018 17:28:08" for bootloader_jump support
+extern uint32_t _eram;
+#define BOOTLOADER_MAGIC 0x3B9ACA00
+#define MAGIC_ADDR (uint32_t *)((intptr_t)(&_eram) - 4)
+
 /**
  * \brief This is the code that gets called on processor reset.
  * To initialize the device, and call the main() routine.
diff --git a/tmk_core/protocol/arm_atsam/usb/udc.c b/tmk_core/protocol/arm_atsam/usb/udc.c
index d04e9b7b28..1f0c0d95d6 100644
--- a/tmk_core/protocol/arm_atsam/usb/udc.c
+++ b/tmk_core/protocol/arm_atsam/usb/udc.c
@@ -51,7 +51,8 @@
 #include "udi_device_conf.h"
 #include "udi.h"
 #include "udc.h"
-#include "md_bootloader.h"
+
+#define BOOTLOADER_SERIAL_MAX_SIZE 20  // DO NOT MODIFY!
 
 /**
  * \ingroup udc_group
@@ -122,6 +123,8 @@ static uint8_t udc_string_product_name[] = USB_DEVICE_PRODUCT_NAME;
 #    define USB_DEVICE_SERIAL_NAME_SIZE 0
 #endif
 
+extern uint32_t _srom;
+
 uint8_t     usb_device_serial_name_size = 0;
 #if defined USB_DEVICE_SERIAL_USE_BOOTLOADER_SERIAL
 uint8_t     bootloader_serial_number[BOOTLOADER_SERIAL_MAX_SIZE + 1] = "";