pw_boot_armv7m: Initial commit

pw_boot_armv7m provides a generic linker script and startup script to
speed up device bringup on ARMv7-M based cores.

Change-Id: Ia4abe78ab1d6bde017608eeb46b2e780bf53dad1
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index dee8a15..c5325bb 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -41,6 +41,7 @@
     ":core_docs",
     ":target_docs",
     "$dir_pw_bloat:docs",
+    "$dir_pw_boot_armv7m:docs",
     "$dir_pw_build:docs",
     "$dir_pw_cpu_exception:docs",
     "$dir_pw_cpu_exception_armv7m:docs",
diff --git a/modules.gni b/modules.gni
index fbbee69..6a65579 100644
--- a/modules.gni
+++ b/modules.gni
@@ -18,6 +18,7 @@
 
 dir_pw_base64 = "$dir_pigweed/pw_base64"
 dir_pw_bloat = "$dir_pigweed/pw_bloat"
+dir_pw_boot_armv7m = "$dir_pigweed/pw_boot_armv7m"
 dir_pw_build = "$dir_pigweed/pw_build"
 dir_pw_cpu_exception = "$dir_pigweed/pw_cpu_exception"
 dir_pw_cpu_exception_armv7m = "$dir_pigweed/pw_cpu_exception_armv7m"
diff --git a/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty b/pw_boot_armv7m/BUILD
similarity index 65%
copy from pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
copy to pw_boot_armv7m/BUILD
index 0b92d47..0c78627 100644
--- a/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
+++ b/pw_boot_armv7m/BUILD
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -12,18 +12,14 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-source_filter: "^SEG .+$"
+package(default_visibility = ["//visibility:public"])
 
-custom_data_source: {
-  name: "segment_names"
-  base_data_source: "segments"
+licenses(["notice"])  # Apache License 2.0
 
-  rewrite: {
-    pattern: "LOAD #0"
-    replacement: "SEG FLASH"
-  }
-  rewrite: {
-    pattern: "LOAD #1"
-    replacement: "SEG RAM"
-  }
-}
+filegroup(
+    name = "pw_dumb_io_baremetal_stm32f429",
+    srcs = [
+        "core_init.c",
+        "public/pw_boot_armv7m/boot.h",
+    ],
+)
diff --git a/pw_boot_armv7m/BUILD.gn b/pw_boot_armv7m/BUILD.gn
new file mode 100644
index 0000000..6376d58
--- /dev/null
+++ b/pw_boot_armv7m/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("$dir_pw_build/linker_script.gni")
+import("$dir_pw_docgen/docs.gni")
+
+if (dir_pw_boot_backend == dir_pw_boot_armv7m) {
+  config("default_config") {
+    include_dirs = [ "public" ]
+  }
+
+  pw_linker_script("armv7m_linker_script") {
+    # pw_boot_armv7m_config is a scope provided by the target.
+    assert(defined(pw_boot_armv7m_config),
+           "pw_boot_armv7m depends on pw_boot_armv7m_config being defined!")
+    defines = pw_boot_armv7m_config.defines
+    linker_script = "basic_armv7m.ld"
+  }
+
+  source_set("pw_boot_armv7m") {
+    public_configs = [ ":default_config" ]
+    deps = [
+      ":armv7m_linker_script",
+      "$dir_pw_preprocessor",
+    ]
+    public = [
+      "public/pw_boot_armv7m/boot.h",
+    ]
+    sources = [ "core_init.c" ] + public
+  }
+}
+
+pw_doc_group("docs") {
+  sources = [
+    "docs.rst",
+  ]
+}
diff --git a/pw_boot_armv7m/basic_armv7m.ld b/pw_boot_armv7m/basic_armv7m.ld
new file mode 100644
index 0000000..3f38aeb
--- /dev/null
+++ b/pw_boot_armv7m/basic_armv7m.ld
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2020 The Pigweed Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/* This relatively simplified linker script will work with many ARMv7-M cores
+ * that have on-board memory-mapped RAM and FLASH. For more complex projects and
+ * devices, it's possible this linker script will not be sufficient as-is.
+ *
+ * This linker script is likely not suitable for a project with a bootloader.
+ */
+
+/* Provide useful error messages when required configurations are not set. */
+#ifndef PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN
+#error "PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN
+
+#ifndef PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE
+#error "PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE
+
+#ifndef PW_BOOT_FLASH_BEGIN
+#error "PW_BOOT_FLASH_BEGIN is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_FLASH_BEGIN
+
+#ifndef PW_BOOT_FLASH_SIZE
+#error "PW_BOOT_FLASH_SIZE is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_FLASH_SIZE
+
+#ifndef PW_BOOT_RAM_BEGIN
+#error "PW_BOOT_RAM_BEGIN is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_RAM_BEGIN
+
+#ifndef PW_BOOT_RAM_SIZE
+#error "PW_BOOT_RAM_SIZE is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_RAM_SIZE
+
+#ifndef PW_BOOT_HEAP_SIZE
+#error "PW_BOOT_HEAP_SIZE is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_HEAP_SIZE
+
+#ifndef PW_BOOT_MIN_STACK_SIZE
+#error "PW_BOOT_MIN_STACK_SIZE is not defined, and is required to use pw_boot_armv7m"
+#endif  // PW_BOOT_MIN_STACK_SIZE
+
+
+/* Note: This technically doesn't set the firmware's entry point. Setting the
+ *       firmware entry point is done by setting vector_table[1]
+ *       (Reset_Handler). However, this DOES tell the compiler how to optimize
+ *       when --gc-sections is enabled.
+ */
+ENTRY(pw_BootEntry)
+
+MEMORY
+{
+  /* TODO(pwbug/57): Make it possible for projects to freely customize
+   * memory regions.
+   */
+
+  /* Vector Table (typically in flash) */
+  INTERRUPT_VECTOR_TABLE(rx) : \
+    ORIGIN = PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN, \
+    LENGTH = PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE
+  /* Internal Flash */
+  FLASH(rx) : \
+    ORIGIN = PW_BOOT_FLASH_BEGIN, \
+    LENGTH = PW_BOOT_FLASH_SIZE
+  /* Internal SRAM */
+  RAM(rwx) : \
+    ORIGIN = PW_BOOT_RAM_BEGIN, \
+    LENGTH = PW_BOOT_RAM_SIZE
+}
+
+SECTIONS
+{
+  /* This is the link-time vector table. If used, the VTOR (Vector Table Offset
+   * Register) MUST point to this memory location in order to be used. This can
+   * be done by ensuring this section exists at the default location of the VTOR
+   * so it's used on reset, or by explicitly setting the VTOR in a bootloader
+   * manually to point to &pw_vector_table_addr before interrupts are enabled.
+   */
+  .vector_table : ALIGN(512)
+  {
+    pw_vector_table_addr = .;
+    KEEP(*(.vector_table))
+  } >INTERRUPT_VECTOR_TABLE
+
+  /* Main executable code. */
+  .code : ALIGN(8)
+  {
+    . = ALIGN(8);
+    /* Application code. */
+    *(.text)
+    *(.text*)
+    KEEP(*(.init))
+    KEEP(*(.fini))
+
+    . = ALIGN(8);
+    /* Constants.*/
+    *(.rodata)
+    *(.rodata*)
+
+    /* .preinit_array, .init_array, .fini_array are used by libc.
+     * Each section is a list of function pointers that are called pre-main and
+     * post-exit for object initialization and tear-down.
+     * Since the region isn't explicitly referenced, specify KEEP to prevent
+     * link-time garbage collection. SORT is used for sections that have strict
+     * init/de-init ordering requirements. */
+    . = ALIGN(8);
+    PROVIDE_HIDDEN(__preinit_array_start = .);
+    KEEP(*(.preinit_array*))
+    PROVIDE_HIDDEN(__preinit_array_end = .);
+
+    PROVIDE_HIDDEN(__init_array_start = .);
+    KEEP(*(SORT(.init_array.*)))
+    KEEP(*(.init_array*))
+    PROVIDE_HIDDEN(__init_array_end = .);
+
+    PROVIDE_HIDDEN(__fini_array_start = .);
+    KEEP(*(SORT(.fini_array.*)))
+    KEEP(*(.fini_array*))
+    PROVIDE_HIDDEN(__fini_array_end = .);
+  } >FLASH
+
+  /* Used by unwind-arm/ */
+  .ARM : ALIGN(8) {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  /* Explicitly initialized global and static data. (.data)*/
+  .static_init_ram : ALIGN(8)
+  {
+    *(.data)
+    *(.data*)
+    . = ALIGN(8);
+  } >RAM AT> FLASH
+
+  /* Zero initialized global/static data. (.bss)
+   * This section is zero initialized in pw_BootEntry(). */
+  .zero_init_ram : ALIGN(8)
+  {
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+    . = ALIGN(8);
+  } >RAM
+
+  .heap : ALIGN(8)
+  {
+    pw_heap_low_addr = .;
+    . = . + PW_BOOT_HEAP_SIZE;
+    . = ALIGN(8);
+    pw_heap_high_addr = .;
+  } >RAM
+
+  /* Link-time check for stack overlaps. */
+  .stack (NOLOAD) : ALIGN(8)
+  {
+    /* Set the address that the main stack pointer should be initialized to. */
+    pw_stack_low_addr = .;
+    HIDDEN(_stack_size = ORIGIN(RAM) + LENGTH(RAM) - .);
+    /* Align the stack to a lower address to ensure it isn't out of range. */
+    HIDDEN(_stack_high = (. + _stack_size) & ~0x7);
+    ASSERT(_stack_high - . >= PW_BOOT_MIN_STACK_SIZE,
+           "Error: Not enough RAM for desired minimum stack size.");
+    . = _stack_high;
+    pw_stack_high_addr = .;
+  } >RAM
+}
+
+/* Symbols used by core_init.c: */
+/* Start of .static_init_ram in FLASH. */
+_pw_static_init_flash_start = LOADADDR(.static_init_ram);
+
+/* Region of .static_init_ram in RAM. */
+_pw_static_init_ram_start = ADDR(.static_init_ram);
+_pw_static_init_ram_end = _pw_static_init_ram_start + SIZEOF(.static_init_ram);
+
+/* Region of .zero_init_ram. */
+_pw_zero_init_ram_start = ADDR(.zero_init_ram);
+_pw_zero_init_ram_end = _pw_zero_init_ram_start + SIZEOF(.zero_init_ram);
+
+/* arm-none-eabi expects `end` symbol to point to start of heap for sbrk. */
+PROVIDE(end = _pw_zero_init_ram_end);
diff --git a/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty b/pw_boot_armv7m/bloaty_config.bloaty
similarity index 92%
rename from pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
rename to pw_boot_armv7m/bloaty_config.bloaty
index 0b92d47..b67ec76 100644
--- a/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
+++ b/pw_boot_armv7m/bloaty_config.bloaty
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -19,11 +19,11 @@
   base_data_source: "segments"
 
   rewrite: {
-    pattern: "LOAD #0"
+    pattern: "LOAD #1"
     replacement: "SEG FLASH"
   }
   rewrite: {
-    pattern: "LOAD #1"
+    pattern: "LOAD #2"
     replacement: "SEG RAM"
   }
 }
diff --git a/pw_boot_armv7m/core_init.c b/pw_boot_armv7m/core_init.c
new file mode 100644
index 0000000..209cac4
--- /dev/null
+++ b/pw_boot_armv7m/core_init.c
@@ -0,0 +1,109 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+//                               !!!WARNING!!!
+//
+// Some of the code in this file is run without static initialization expected
+// by C/C++. Any accesses to statically initialized objects/variables before
+// memory is initialized will result in undefined values and violates the C
+// specification. Only code run after memory initialization is complete will be
+// compliant and truly safe to run. In general, make early initialization code
+// run AFTER memory initialization has completed unless it is ABSOLUTELY
+// NECESSARY to modify the way memory is initialized.
+//
+// This file is similar to a traditional assembly startup file. It turns out
+// that everything typically done in ARMv7-M assembly startup can be done
+// straight from C code. This makes startup code easier to maintain, modify,
+// and read.
+//
+// When execution begins due to SoC power-on (or the device is reset), three
+// key things must happen to properly enter C++ execution context:
+//   1. Static variables must be loaded from flash to RAM.
+//   2. Zero-initialized variables must be zero-initialized.
+//   3. Statically allocated objects must have their constructors run.
+// The SoC doesn't inherently have a notion of how to do this, so this is
+// handled in StaticInit();
+//
+// Following this, execution is handed over to pw_PreMainInit() to facilitate
+// platform, project, or application pre-main initialization. When
+// pw_PreMainInit() returns, main() is executed.
+//
+// The simple flow is as follows:
+//   1. Power on
+//   2. PC and SP set (from vector_table by SoC, or by bootloader)
+//   3. pw_BootEntry()
+//     3.1. Static-init RAM (.data, .bss, C++ constructors)
+//     3.2. pw_PreMainInit()
+//     3.3. main()
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "pw_boot_armv7m/boot.h"
+#include "pw_preprocessor/compiler.h"
+
+// Extern symbols provided by linker script.
+// These symbols tell us where various memory sections start and end.
+extern uint8_t _pw_static_init_ram_start;
+extern uint8_t _pw_static_init_ram_end;
+extern uint8_t _pw_static_init_flash_start;
+extern uint8_t _pw_zero_init_ram_start;
+extern uint8_t _pw_zero_init_ram_end;
+
+// Functions called as part of firmware initialization.
+void __libc_init_array(void);
+
+// WARNING: Be EXTREMELY careful when running code before this function
+// completes. The context before this function violates the C spec
+// (Section 6.7.8, paragraph 10 for example, which requires uninitialized static
+// values to be zero-initialized).
+void StaticInit(void) {
+  // Static-init RAM (load static values into ram, .data section init).
+  memcpy(&_pw_static_init_ram_start,
+         &_pw_static_init_flash_start,
+         &_pw_static_init_ram_end - &_pw_static_init_ram_start);
+
+  // Zero-init RAM (.bss section init).
+  memset(&_pw_zero_init_ram_start,
+         0,
+         &_pw_zero_init_ram_end - &_pw_zero_init_ram_start);
+
+  // Call static constructors.
+  __libc_init_array();
+}
+
+// WARNING: This code is run immediately upon boot, and performs initialization
+// of RAM. Note that code running before this function finishes memory
+// initialization will violate the C spec (Section 6.7.8, paragraph 10 for
+// example, which requires uninitialized static values to be zero-initialized).
+// Be EXTREMELY careful when running code before this function finishes RAM
+// initialization.
+//
+// This function runs immediately at boot because it is at index 1 of the
+// interrupt vector table.
+void pw_BootEntry() {
+  StaticInit();
+
+  // This function is not provided by pw_boot_armv7m, a platform layer, project,
+  // or application is expected to implement it.
+  pw_PreMainInit();
+
+  // Run main.
+  main();
+
+  // In case main() returns, just sit here until the device is reset.
+  while (true) {
+  }
+}
diff --git a/pw_boot_armv7m/docs.rst b/pw_boot_armv7m/docs.rst
new file mode 100644
index 0000000..c9b3fff
--- /dev/null
+++ b/pw_boot_armv7m/docs.rst
@@ -0,0 +1,148 @@
+.. _chapter-pw-boot-armv7m:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+--------------
+pw_boot_armv7m
+--------------
+
+The ARMv7-M boot module provides a linker script and some early initialization
+of static memory regions and C++ constructors. This is enough to get many
+ARMv7-M cores booted and ready to run C++ code.
+
+This module is currently designed to support a very minimal device memory layout
+configuration:
+
+ - One contiguous region for RAM.
+ - One contiguous region for flash.
+ - Static, in-flash vector table at the default location expected by the SoC.
+
+Note that this module is not yet particularly suited for projects that utilize
+a bootloader, as it's relatively opinionated regarding where code is stored.
+
+.. warning::
+  This module is currently NOT stable! Depending on this module may cause
+  breakages as this module is updated.
+
+Setup
+=====
+
+User-Implemented Functions
+--------------------------
+This module expects two extern "C" functions to be defined outside this module.
+
+ - ``int main()``: This is where applications reside.
+ - ``void pw_PreMainInit()``: This function executes just before main, and
+   can be used for any device initialization that isn't application specific.
+   Depending on your platform, this might be turning on a UART, setting up
+   default clocks, etc.
+
+If either of these functions are unimplemented, executables will encounter a
+link error.
+
+Required Configs
+----------------
+This module has a number of required configuration options that mold the linker
+script to fit to a wide variety of ARMv7-M SoCs. The ``pw_boot_armv7m_config``
+GN variable has a ``defines`` member that can be used to modify these linker
+script options. See the documentation section on configuration for information
+regarding which configuration options are required.
+
+Vector Table
+------------
+Targets using pw_boot_armv7m will need to provide an ARMv7-M interrupt vector
+table (ARMv7-M Architecture Reference Manual DDI 0403E.b section B1.5.2 and
+B1.5.3). This is done by storing an array into the ``.vector_table`` section,
+and properly configuring ``PW_BOOT_INTERRUPT_VECTOR_TABLE_*`` preprocessor
+defines to cover the address region your SoC expects the vector table to be
+located at (often the beginning of the flash region). If using a bootloader,
+ensure VTOR (Vector Table Offset Register) is configured to point to the vector
+table. Otherwise, refer to the hardware vendor's documentation to determine
+where the vector table should be located such that it resides where VTOR is
+initialized to by default.
+
+Example vector table:
+
+.. code-block:: cpp
+
+  typedef void (*InterruptHandler)();
+
+  PW_KEEP_IN_SECTION(".vector_table")
+  const InterruptHandler vector_table[] = {
+      // The starting location of the stack pointer.
+      // This address is NOT an interrupt handler/function pointer, it is simply
+      // the address that the main stack pointer should be initialized to. The
+      // value is reinterpret casted because it needs to be in the vector table.
+      [0] = reinterpret_cast<InterruptHandler>(&pw_stack_high_addr),
+
+      // Reset handler, dictates how to handle reset interrupt. This is the
+      // address that the Program Counter (PC) is initialized to at boot.
+      [1] = pw_BootEntry,
+
+      // NMI handler.
+      [2] = DefaultFaultHandler,
+      // HardFault handler.
+      [3] = DefaultFaultHandler,
+      ...
+  };
+
+Usage
+=====
+
+Publicly exported symbols
+-------------------------
+The linker script provided by this module exports a number of symbols that
+may be used to retrieve the locations of specific memory regions at runtime.
+These symbols are declared as ``uint8_t`` variables. The variables themselves
+do not contain the addresses, they only reside at the memory location they
+reference. To retrieve the memory locations, simply take the reference of the
+symbol (e.g. ``&pw_vector_table_addr``).
+
+``pw_heap_[low/high]_addr``: Beginning and end of the memory range of the heap.
+These addresses may be identical, indicating a heap with a size of zero bytes.
+
+``pw_stack_[low/high]_addr``: Beginning and end of the memory range of the main
+stack. This might not be the only stack in the system.
+
+``pw_vector_table_addr``: Beginning of the ARMv7-M interrupt vector table.
+
+Configuration
+=============
+These configuration options can be controlled by appending to
+``pw_boot_armv7m_config.defines`` as part of a Pigweed target config file.
+
+**PW_BOOT_HEAP_SIZE** (required):
+How much memory (in bytes) to reserve for the heap. This can be zero.
+
+**PW_BOOT_MIN_STACK_SIZE** (required):
+The minimum size reserved for the main stack. If statically allocated memory
+begins to cut into the minimum, a link error will be emitted.
+
+**PW_BOOT_FLASH_BEGIN** (required):
+The start address of the MCU's flash region. This region must NOT include the
+vector table. (i.e. if the INTERRUPT_VECTOR_TABLE is in flash, the flash region
+should begin *after* the vtable)
+
+**PW_BOOT_FLASH_SIZE** (required):
+Size of the flash region in bytes.
+
+**PW_BOOT_RAM_BEGIN** (required):
+The start address of the MCU's RAM region.
+
+**PW_BOOT_RAM_SIZE** (required):
+Size of the RAM region in bytes.
+
+**PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN** (required):
+Address the target MCU expects the link-time vector table to be located at. This
+is typically the beginning of the flash region. While the vector table may be
+changed later in the boot process, a minimal vector table MUST be present for
+the MCU to operate as expected.
+
+**PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE** (required):
+Number of bytes to reserve for the ARMv7-M vector table.
+
+Dependencies
+============
+  * pw_preprocessor module
diff --git a/pw_boot_armv7m/public/pw_boot_armv7m/boot.h b/pw_boot_armv7m/public/pw_boot_armv7m/boot.h
new file mode 100644
index 0000000..ab0da28
--- /dev/null
+++ b/pw_boot_armv7m/public/pw_boot_armv7m/boot.h
@@ -0,0 +1,90 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+// This module is similar to a traditional assembly startup file paired with a
+// linker script. It turns out that everything typically done in ARMv7-M
+// assembly startup can be done straight from C code. This makes startup code
+// easier to maintain, modify, and read.
+//
+// Core initialization is comprised of two primary parts:
+//
+// 1. Load boot information from ARMv7-M Vector Table: The ARMv7-M vector table
+//    (See ARMv7-M Architecture Reference Manual DDI 0403E.b section B1.5)
+//    dictates the starting Program Counter (PC) and Stack Pointer (SP) when the
+//    SoC powers on. The vector table also contains a number of other vectors to
+//    handle different exceptions. This module does not provide a vector table,
+//    but it does account for it in the linker script.
+//
+// 2. Initialize static memory: When execution begins due to SoC power-on (or
+//    the device is reset), static memory regions must be initialized to ensure
+//    they contains the expected values when code begins to run. The SoC doesn't
+//    inherently have a notion of how to do this, so before ANYTHING else the
+//    memory must be initialized. This is done at the beginning of
+//    pw_BootEntry().
+//
+//
+// The simple flow is as follows:
+//   Power on -> PC and SP set (from vector_table by SoC) -> pw_BootEntry()
+//
+// In pw_BootEntry():
+//   Initialize memory -> pw_PreMainInit() -> main()
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_preprocessor/util.h"
+
+PW_EXTERN_C_START
+
+// The following extern symbols are provided by the linker script, and their
+// values are accessible via the reference of the symbol.
+//
+// Example:
+//   if (stack_pointer < &pw_stack_low_addr) {
+//     PW_LOG_ERROR("Main stack overflowed!")
+//   }
+
+// pw_stack_[low/high]_addr indicate the range of the main stack. Note that this
+// might not be the only stack in the system.
+//
+// The main stack pointer (sp_main) should be initialized to pw_stack_high_addr.
+// This can be done by inserting the address into index 0 of the ARMv7-M vector
+// table. (See ARMv7-M Architecture Reference Manual DDI 0403E.b section B1.5.3)
+extern uint8_t pw_stack_low_addr;
+extern uint8_t pw_stack_high_addr;
+
+// pw_heap_[low/high]_addr indicate the address range reserved for the heap.
+extern uint8_t pw_heap_low_addr;
+extern uint8_t pw_heap_high_addr;
+
+// The address that denotes the beginning of the .vector_table section. This
+// can be used to set VTOR (vector table offset register) by the bootloader.
+extern uint8_t pw_vector_table_addr;
+
+// Forward declaration of main. Pigweed applications are expected to implement
+// this function. An implementation of main() is NOT provided by this module.
+int main();
+
+// For this module to work as expected, index 1 of the ARMv7-M vector table
+// (which usually points to Reset_Handler) must be set to point to this
+// function. This function is implemented by pw_boot_armv7m, and does early
+// memory initialization.
+PW_NO_PROLOGUE void pw_BootEntry();
+
+// This function is called by pw_BootEntry() after memory initialization but
+// before main. This allows targets to have pre-main initialization of the
+// device and seamlessly swap out the main() implementation. This function is
+// NOT implemented by pw_boot_armv7m.
+void pw_PreMainInit();
+
+PW_EXTERN_C_END
diff --git a/pw_build/linker_script.gni b/pw_build/linker_script.gni
new file mode 100644
index 0000000..da64e95
--- /dev/null
+++ b/pw_build/linker_script.gni
@@ -0,0 +1,132 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("$dir_pw_build/exec.gni")
+
+# Preprocess a linker script and turn it into a target.
+#
+# Note: to use this template, pw_cc_command must be specified in your target's
+# config. It should match the name of the C compiler your target uses (e.g.
+# arm-none-eabi-gcc).
+#
+# In lieu of direct GN support for linker scripts, this template makes it
+# possible to run the C Preprocessor on a linker script file so defines can
+# be properly evaluated before the linker script is passed to the dir_pw_build
+#
+# TODO(pwbug/53): This template serves as a stand-in until native GN support for
+# linker scripts is added.
+#
+# Args:
+#  linker_script: The linker script to send through the C preprocessor.
+#
+#  defines: Preprocessor defines to apply when running the C preprocessor.
+#
+#  cflags: Flags to pass to the C compiler.
+#
+#  inputs: Files that, when changed, should trigger a re-build of the linker
+#    script. linker_script is implicitly added to this by the template.
+#
+# Example:
+#
+#   pw_linker_script("generic_linker_script") {
+#     defines = [
+#       "PW_HEAP_SIZE=1K",
+#       "PW_NOINIT_SIZE=512"
+#     ]
+#     linker_script = "basic_script.ld"
+#   }
+#
+template("pw_linker_script") {
+  assert(
+      defined(pw_cc_command) && pw_cc_command != "",
+      "pw_cc_command has not been properly configured. This variable must be " +
+          "defined to enable linker script preprocessing.")
+
+  assert(
+      defined(invoker.linker_script) && invoker.linker_script != "",
+      "$target_name did not set `linker_script` to refer to a valid linker " +
+          "script. This variable is required for linker script targets.")
+
+  _final_linker_script = "${target_gen_dir}/${target_name}_final.ld"
+
+  # This action invokes the C compiler provided by the target to preprocess the
+  # linker script.
+  pw_exec("${target_name}_preprocess") {
+    program = pw_cc_command
+    inputs = [
+      invoker.linker_script,
+    ]
+    args = [
+      # Run compiler in preprocessor-only mode.
+      "-E",
+
+      # Do not generate linemarkers in output.
+      "-P",
+
+      # Do not discard comments.
+      "-C",
+
+      # Treat the following file as a C file.
+      "-x",
+      "c",
+      rebase_path(invoker.linker_script),
+    ]
+
+    # Include any explicitly listed c flags.
+    if (defined(invoker.cflags)) {
+      args += cflags
+    }
+
+    # Add defines.
+    if (defined(invoker.defines)) {
+      args += process_file_template(invoker.defines, "-D{{source_name_part}}")
+    }
+
+    # Set output file.
+    args += [
+      "-o",
+      _final_linker_script,
+    ]
+    outputs = [
+      _final_linker_script,
+    ]
+  }
+
+  # This config adds a the linker script produced by the preprocess action to
+  # the linker flags.
+  config("${target_name}_config") {
+    inputs = [
+      invoker.linker_script,
+    ]
+    if (!defined(invoker.ldflags)) {
+      ldflags = []
+    }
+    ldflags += [ "-T" + rebase_path(_final_linker_script) ]
+  }
+
+  # The target that adds the linker script config to this library and everything
+  # that depends on it.
+  source_set(target_name) {
+    inputs = [
+      _final_linker_script,
+    ]
+    if (defined(invoker.inputs)) {
+      inputs += invoker.inputs
+    }
+    all_dependent_configs = [ ":${target_name}_config" ]
+    deps = [
+      ":${target_name}_preprocess",
+    ]
+  }
+}
diff --git a/pw_dumb_io_baremetal_stm32f429/BUILD b/pw_dumb_io_baremetal_stm32f429/BUILD
index 2d9f88b..595b3c4 100644
--- a/pw_dumb_io_baremetal_stm32f429/BUILD
+++ b/pw_dumb_io_baremetal_stm32f429/BUILD
@@ -19,7 +19,6 @@
 filegroup(
     name = "pw_dumb_io_baremetal_stm32f429",
     srcs = [
-        "core_init.c",
         "dumb_io_baremetal.cc",
     ],
 )
diff --git a/pw_dumb_io_baremetal_stm32f429/BUILD.gn b/pw_dumb_io_baremetal_stm32f429/BUILD.gn
index 7eaf6f1..1a3a04a 100644
--- a/pw_dumb_io_baremetal_stm32f429/BUILD.gn
+++ b/pw_dumb_io_baremetal_stm32f429/BUILD.gn
@@ -17,21 +17,9 @@
 # This if statement allows docs to always build even if the target isn't
 # compatible with this backend.
 if (dir_pw_dumb_io_backend == dir_pw_dumb_io_baremetal_stm32f429) {
-  config("linker_script_config") {
-    _linker_script = "stm32f429.ld"
-    inputs = [
-      _linker_script,
-    ]
-    ldflags = [ "-T" + rebase_path("$_linker_script") ]
-  }
-
-  source_set("linker_script") {
-    public_configs = [ ":linker_script_config" ]
-  }
-
   source_set("pw_dumb_io_baremetal_stm32f429") {
     public_deps = [
-      ":linker_script",
+      "$dir_pw_boot_armv7m",
     ]
     deps = [
       "$dir_pw_dumb_io:default_putget_bytes",
@@ -39,7 +27,6 @@
       "$dir_pw_preprocessor",
     ]
     sources = [
-      "core_init.c",
       "dumb_io_baremetal.cc",
     ]
   }
diff --git a/pw_dumb_io_baremetal_stm32f429/core_init.c b/pw_dumb_io_baremetal_stm32f429/core_init.c
deleted file mode 100644
index 15d012e..0000000
--- a/pw_dumb_io_baremetal_stm32f429/core_init.c
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2019 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-//                               !!!WARNING!!!
-//
-// Some of the code in this file is run without static initialization expected
-// by C/C++. Any accesses to statically initialized objects/variables before
-// memory is initialized will result in undefined values and violates the C
-// specification. Only code run after memory initialization is complete will be
-// compliant and truly safe to run. In general, make early initialization code
-// run AFTER memory initialization has complete unless it is ABSOLUTELY
-// NECESSARY to modify the way memory is initialized.
-//
-// This file is similar to a traditional assembly startup file. It turns out
-// that everything typically done in ARMv7-M assembly startup can be done
-// straight from C code. This makes startup code easier to maintain, modify,
-// and read.
-//
-// Core initialization is comprised of two primary parts:
-//
-// 1. Initialize ARMv7-M Vector Table: The ARMv7-M vector table (See ARMv7-M
-//    Architecture Reference Manual DDI 0403E.b section B1.5) dictates the
-//    starting program counter (PC) and stack pointer (SP) when the SoC powers
-//    on. The vector table also contains a number of other vectors to handle
-//    different exceptions. This file omits many of the vectors and only
-//    configures the four most important ones.
-//
-// 2. Initialize Memory: When execution begins due to SoC power-on (or the
-//    device is reset), memory must be initialized to ensure it contains the
-//    expected values when code begins to run. The SoC doesn't inherently have a
-//    notion of how to do this, so before ANYTHING else the memory must be
-//    initialized. This is done at the beginning of pw_FirmwareInit().
-//
-//
-// The simple flow is as follows:
-//   Power on -> PC and SP set (from vector_table by SoC) -> pw_FirmwareInit()
-//
-// In pw_FirmwareInit():
-//   Initialize memory -> initialize board (pre-main init) -> main()
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "pw_preprocessor/compiler.h"
-
-// Extern symbols referenced in the vector table.
-extern const uint8_t _stack_end[];
-extern uint8_t _static_init_ram_start[];
-extern uint8_t _static_init_ram_end[];
-extern uint8_t _static_init_flash_start[];
-extern uint8_t _zero_init_ram_start[];
-extern uint8_t _zero_init_ram_end[];
-
-// Functions called as part of firmware initialization.
-void __libc_init_array(void);
-void pw_BoardInit(void);
-int main(void);
-
-void DefaultFaultHandler(void) {
-  while (true) {
-    // Wait for debugger to attach.
-  }
-}
-
-// WARNING: This code is run immediately upon boot, and performs initialization
-// of RAM. Note that code running before this function finishes memory
-// initialization will violate the C spec (Section 6.7.8, paragraph 10 for
-// example, which requires uninitialized static values to be zero-initialized).
-// Be EXTREMELY careful when running code before this function finishes RAM
-// initialization.
-//
-// This function runs immediately at boot because it is at index 1 of the
-// interrupt vector table.
-PW_NO_PROLOGUE void pw_FirmwareInit() {
-  // Begin memory initialization.
-  // Static-init RAM.
-  memcpy(_static_init_ram_start,
-         _static_init_flash_start,
-         _static_init_ram_end - _static_init_ram_start);
-
-  // Zero-init RAM.
-  memset(_zero_init_ram_start, 0, _zero_init_ram_end - _zero_init_ram_start);
-
-  // Call static constructors.
-  __libc_init_array();
-
-  // End memory initialization.
-
-  // Do any necessary board init.
-  pw_BoardInit();
-
-  // Run main.
-  main();
-
-  // In case main() returns, just sit here until the device is reset.
-  while (true) {
-  }
-}
-
-// This is the device's interrupt vector table. It's not referenced in any
-// code because the platform (STM32F4xx) expects this table to be present at the
-// beginning of flash.
-//
-// For more information, see ARMv7-M Architecture Reference Manual DDI 0403E.b
-// section B1.5.3.
-PW_KEEP_IN_SECTION(".vector_table")
-const uint32_t vector_table[] = {
-    // The starting location of the stack pointer.
-    [0] = (uint32_t)_stack_end,
-
-    // Reset handler, dictates how to handle reset interrupt. This is also run
-    // at boot.
-    [1] = (uint32_t)pw_FirmwareInit,
-
-    // NMI handler.
-    [2] = (uint32_t)DefaultFaultHandler,
-    // HardFault handler.
-    [3] = (uint32_t)DefaultFaultHandler,
-};
diff --git a/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc b/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc
index 1c6b288..1810d28 100644
--- a/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc
+++ b/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc
@@ -14,6 +14,7 @@
 
 #include <cinttypes>
 
+#include "pw_boot_armv7m/boot.h"
 #include "pw_dumb_io/dumb_io.h"
 #include "pw_preprocessor/compiler.h"
 
@@ -132,9 +133,49 @@
 volatile UsartBlock& usart1 =
     *reinterpret_cast<volatile UsartBlock*>(kApb2PeripheralBase + 0x1000U);
 
+// Default handler to insert into the ARMv7-M vector table (below).
+// This function exists for convenience. If a device isn't doing what you
+// expect, it might have hit a fault and ended up here.
+void DefaultFaultHandler(void) {
+  while (true) {
+    // Wait for debugger to attach.
+  }
+}
+
+// This is the device's interrupt vector table. It's not referenced in any
+// code because the platform (STM32F4xx) expects this table to be present at the
+// beginning of flash. The exact address is specified in the pw_boot_armv7m
+// configuration as part of the target config.
+//
+// For more information, see ARMv7-M Architecture Reference Manual DDI 0403E.b
+// section B1.5.3.
+
+// This typedef is for convenience when building the vector table. With the
+// exception of SP_main (0th entry in the vector table), all the entries of the
+// vector table are function pointers.
+typedef void (*InterruptHandler)();
+
+PW_KEEP_IN_SECTION(".vector_table")
+const InterruptHandler vector_table[] = {
+    // The starting location of the stack pointer.
+    // This address is NOT an interrupt handler/function pointer, it is simply
+    // the address that the main stack pointer should be initialized to. The
+    // value is reinterpret casted because it needs to be in the vector table.
+    [0] = reinterpret_cast<InterruptHandler>(&pw_stack_high_addr),
+
+    // Reset handler, dictates how to handle reset interrupt. This is the
+    // address that the Program Counter (PC) is initialized to at boot.
+    [1] = pw_BootEntry,
+
+    // NMI handler.
+    [2] = DefaultFaultHandler,
+    // HardFault handler.
+    [3] = DefaultFaultHandler,
+};
+
 }  // namespace
 
-extern "C" void pw_BoardInit() {
+extern "C" void pw_PreMainInit() {
   // Enable 'A' GIPO clocks.
   platform_rcc.ahb1_config |= kGpioAEnable;
 
diff --git a/pw_dumb_io_baremetal_stm32f429/stm32f429.ld b/pw_dumb_io_baremetal_stm32f429/stm32f429.ld
deleted file mode 100644
index 0a63372..0000000
--- a/pw_dumb_io_baremetal_stm32f429/stm32f429.ld
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2019 The Pigweed Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- *     https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-HIDDEN(_min_stack_size = 1K);
-
-/* Note: This technically doesn't set the firmware's entry point. Setting the
- *       firmware entry point is done by setting vector_table[1] in core_init.c.
- *       However, this DOES tell the compiler how to optimize when --gc-sections
- *       is enabled.
- */
-ENTRY(pw_FirmwareInit)
-
-MEMORY
-{
-  /* Internal Flash */
-  FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 512K
-  /* Internal SRAM */
-  RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 192K
-}
-
-SECTIONS
-{
-
-  /* Main executable code. */
-  .code : ALIGN(8)
-  {
-    /* STM32F4xx expects the vector table to be at the beginning of flash. */
-    KEEP(*(.vector_table))
-
-    . = ALIGN(8);
-    /* Application code. */
-    *(.text)
-    *(.text*)
-    KEEP(*(.init))
-    KEEP(*(.fini))
-
-    . = ALIGN(8);
-    /* Constants.*/
-    *(.rodata)
-    *(.rodata*)
-
-    /* .preinit_array, .init_array, .fini_array are used by libc.
-     * Each section is a list of function pointers that are called pre-main and
-     * post-exit for object initialization and tear-down.
-     * Since the region isn't explicitly referenced, specify KEEP to prevent
-     * link-time garbage collection. SORT is used for sections that have strict
-     * init/de-init ordering requirements. */
-    . = ALIGN(8);
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array*))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-
-    PROVIDE_HIDDEN (__init_array_start = .);
-    KEEP (*(SORT(.init_array.*)))
-    KEEP (*(.init_array*))
-    PROVIDE_HIDDEN (__init_array_end = .);
-
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(SORT(.fini_array.*)))
-    KEEP (*(.fini_array*))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  } >FLASH
-
-  /* Used by unwind-arm/ */
-  .ARM : ALIGN(8) {
-    __exidx_start = .;
-    *(.ARM.exidx*)
-    __exidx_end = .;
-  } >FLASH
-
-  /* Explicitly initialized global and static data. (.data)*/
-  .static_init_ram : ALIGN(8)
-  {
-    *(.data)
-    *(.data*)
-    . = ALIGN(8);
-  } >RAM AT> FLASH
-
-  /* Zero initialized global/static data. (.bss)
-   * This section is zero initialized in pw_FirmwareInit(). */
-  .zero_init_ram : ALIGN(8)
-  {
-    *(.bss)
-    *(.bss*)
-    *(COMMON)
-    . = ALIGN(8);
-  } >RAM
-
-  /* Link-time check for stack overlaps. */
-  .stack (NOLOAD) : ALIGN(8)
-  {
-    HIDDEN(_stack_size = ORIGIN(RAM) + LENGTH(RAM) - .);
-    ASSERT(_stack_size >= _min_stack_size, "Error: Not enough RAM for stack.");
-    . = . + _stack_size;
-  } >RAM
-}
-
-/* Symbols used by core_init.c: */
-/* Top of stack to set stack pointer. */
-_stack_end = ORIGIN(RAM) + LENGTH(RAM);
-
-/* Start of .static_init_ram in FLASH. */
-_static_init_flash_start = LOADADDR(.static_init_ram);
-
-/* Region of .static_init_ram in RAM. */
-_static_init_ram_start = ADDR(.static_init_ram);
-_static_init_ram_end = _static_init_ram_start + SIZEOF(.static_init_ram);
-
-/* Region of .zero_init_ram. */
-_zero_init_ram_start = ADDR(.zero_init_ram);
-_zero_init_ram_end = _zero_init_ram_start + SIZEOF(.zero_init_ram);
-
-/* arm-none-eabi expects `end` symbol to point to start of heap for sbrk. */
-PROVIDE (end = _zero_init_ram_end);
diff --git a/pw_vars_default.gni b/pw_vars_default.gni
index c5458da..071aec1 100644
--- a/pw_vars_default.gni
+++ b/pw_vars_default.gni
@@ -92,10 +92,21 @@
 # empty (but defined) variable.
 #
 # All of these should default to empty strings. For target-specific defaults,
-# modify these variables in a target confiruation file.
+# modify these variables in a target configuration file.
+
+# Backend for the pw_boot module.
+dir_pw_boot_backend = ""
+
+# Backend for the pw_cpu_exception module.
+dir_pw_cpu_exception_backend = ""
 
 # Backend for the pw_dumb_io module.
 dir_pw_dumb_io_backend = ""
 
-# Backend for the pw_cpu_exception module.
-dir_pw_cpu_exception_backend = ""
+############################## MODULE CONFIGS ##################################
+
+# Module configuration options for pw_boot_armv7m.
+pw_boot_armv7m_config = {
+  # C Preprocessor defines used for linker script configuration.
+  defines = []
+}
diff --git a/targets/stm32f429i-disc1/target_config.gni b/targets/stm32f429i-disc1/target_config.gni
index f768e23..0a1b382 100644
--- a/targets/stm32f429i-disc1/target_config.gni
+++ b/targets/stm32f429i-disc1/target_config.gni
@@ -29,6 +29,11 @@
   pw_use_test_server = false
 }
 
+# Expose the tool to use for preprocessing linker scripts.
+# TODO(pwbug/53): Temporary, will be removed when proper linker script support
+# is added to GN.
+pw_cc_command = "arm-none-eabi-gcc"
+
 # Executable wrapper that includes some baremetal startup code.
 template("stm32f429i_executable") {
   target("executable", target_name) {
@@ -45,12 +50,7 @@
 
 # Path to the bloaty config file for the output binaries.
 pw_executable_config.bloaty_config_file =
-    "$dir_pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty"
-
-# Path to a linker script target. This must be a target (e.g. source_set)
-# that provides a linker script (through a public config, for example).
-linker_script_target =
-    "$dir_pw_dumb_io_baremetal_stm32f429:linker_script_target"
+    "$dir_pw_boot_armv7m/bloaty_config.bloaty"
 
 if (pw_use_test_server) {
   pw_automatic_test_runner =
@@ -58,5 +58,17 @@
 }
 
 # Facade backends
-dir_pw_cpu_exception_backend = "$dir_pw_cpu_exception_armv7m"
-dir_pw_dumb_io_backend = "$dir_pw_dumb_io_baremetal_stm32f429"
+dir_pw_boot_backend = dir_pw_boot_armv7m
+dir_pw_cpu_exception_backend = dir_pw_cpu_exception_armv7m
+dir_pw_dumb_io_backend = dir_pw_dumb_io_baremetal_stm32f429
+
+pw_boot_armv7m_config.defines += [
+  "PW_BOOT_FLASH_BEGIN=0x08000200",
+  "PW_BOOT_FLASH_SIZE=512K",
+  "PW_BOOT_HEAP_SIZE=0",
+  "PW_BOOT_MIN_STACK_SIZE=1K",
+  "PW_BOOT_RAM_BEGIN=0x20000000",
+  "PW_BOOT_RAM_SIZE=192K",
+  "PW_BOOT_INTERRUPT_VECTOR_TABLE_BEGIN=0x08000000",
+  "PW_BOOT_INTERRUPT_VECTOR_TABLE_SIZE=512",
+]