mctp refactor
diff --git a/services/mctp/server/src/main.rs b/services/mctp/server/src/main.rs
index c3195d1..b431aac 100644
--- a/services/mctp/server/src/main.rs
+++ b/services/mctp/server/src/main.rs
@@ -47,7 +47,7 @@
 use openprot_mctp_api::ResponseCode;
 use openprot_mctp_server::dispatch;
 
-use i2c_api::{BusIndex, I2cTargetClient, TargetMessage};
+use i2c_api::{BusIndex, I2cAddress, I2cTargetClient, TargetMessage};
 use i2c_client::IpcI2cClient;
 use openprot_mctp_transport_i2c::{I2cSender, MctpI2cReceiver};
 
@@ -70,12 +70,25 @@
 
     // I2C notification client: receives slave-mode interrupts via Signals::USER.
     let mut i2c_notify = IpcI2cClient::new(handle::I2C);
+
+    // Configure I2C bus 2 as slave at our address
+    let addr = I2cAddress::new(OWN_I2C_ADDR).map_err(|_| pw_status::Error::InvalidArgument)?;
     i2c_notify
-        .register_notification(BusIndex::BUS_0, 0)
+        .configure_target_address(BusIndex::BUS_2, addr)
+        .map_err(|_| pw_status::Error::Internal)?;
+
+    // Enable slave receive mode
+    i2c_notify
+        .enable_receive(BusIndex::BUS_2)
+        .map_err(|_| pw_status::Error::Internal)?;
+
+    // Register for notifications
+    i2c_notify
+        .register_notification(BusIndex::BUS_2, 0)
         .map_err(|_| pw_status::Error::Internal)?;
 
     // Separate handle for the sender — I2cSender takes ownership.
-    let sender = I2cSender::new(IpcI2cClient::new(handle::I2C), BusIndex::BUS_0, OWN_I2C_ADDR);
+    let sender = I2cSender::new(IpcI2cClient::new(handle::I2C), BusIndex::BUS_2, OWN_I2C_ADDR);
     let receiver = MctpI2cReceiver::new(OWN_I2C_ADDR);
     let mut server = openprot_mctp_server::Server::<_, 16>::new(
         mctp::Eid(OWN_EID),
@@ -93,6 +106,8 @@
     syscall::wait_group_add(handle::WG, handle::MCTP, Signals::READABLE, 0usize)?;
     syscall::wait_group_add(handle::WG, handle::I2C,  Signals::USER,     1usize)?;
 
+    pw_log::info!("MCTP server ready, entering event loop");
+
     loop {
         let ev = syscall::object_wait(handle::WG, Signals::READABLE, Instant::MAX)?;
 
@@ -100,7 +115,7 @@
             // Inbound I2C data: drain pending messages, decode I2C framing,
             // feed raw MCTP packets into the router.
             let mut msgs = [TargetMessage::default(); 1];
-            if let Ok(n) = i2c_notify.get_pending_messages(BusIndex::BUS_0, &mut msgs) {
+            if let Ok(n) = i2c_notify.get_pending_messages(BusIndex::BUS_2, &mut msgs) {
                 for msg in &msgs[..n] {
                     if let Ok((pkt, _src_addr)) = receiver.decode(msg) {
                         let _ = server.inbound(pkt);
diff --git a/target/ast1060-evb/BUILD.bazel b/target/ast1060-evb/BUILD.bazel
index f26545a..1aa57d3 100644
--- a/target/ast1060-evb/BUILD.bazel
+++ b/target/ast1060-evb/BUILD.bazel
@@ -21,8 +21,8 @@
     flags = flags_from_dict(
         KERNEL_DEVICE_COMMON_FLAGS | {
             "@pigweed//pw_kernel/config:kernel_config": ":config",
-            # TODO: Switch to :console_backend_uart when aspeed-ddk uart driver is available
-            "@pigweed//pw_kernel/subsys/console:console_backend": "@pigweed//pw_kernel/subsys/console:console_backend_semihosting",
+            # Using UART console backend - output on UART at 115200 baud
+            "@pigweed//pw_kernel/subsys/console:console_backend": ":console_backend_uart",
         },
     ),
 )
diff --git a/target/ast1060-evb/mctp/BUILD.bazel b/target/ast1060-evb/mctp/BUILD.bazel
index f813178..14a53d8 100644
--- a/target/ast1060-evb/mctp/BUILD.bazel
+++ b/target/ast1060-evb/mctp/BUILD.bazel
@@ -43,9 +43,9 @@
 system_image(
     name = "mctp",
     apps = [
-        ":mctp_echo",
         "//services/i2c/server:i2c_server",
         "//services/mctp/server:mctp_server",
+        ":mctp_echo",
     ],
     kernel = ":target",
     platform = "//target/ast1060-evb",
diff --git a/target/ast1060-evb/mctp/mctp_echo.rs b/target/ast1060-evb/mctp/mctp_echo.rs
index ff08843..3f01a8c 100644
--- a/target/ast1060-evb/mctp/mctp_echo.rs
+++ b/target/ast1060-evb/mctp/mctp_echo.rs
@@ -26,18 +26,29 @@
 
 use app_mctp_echo::handle;
 
-/// MCTP message type for echo (vendor-defined type 1).
-const ECHO_MSG_TYPE: u8 = 1;
+/// MCTP message type for echo (vendor-defined PCIe type).
+/// 0x7e = MCTP_TYPE_VENDOR_PCIE
+const ECHO_MSG_TYPE: u8 = 0x7e;
+
+/// AMD PCIe Vendor ID
+const AMD_PCIE_VENDOR_ID: u16 = 0x1022;
 
 fn mctp_echo_loop() -> Result<()> {
     pw_log::info!("MCTP echo starting");
 
     let client = IpcMctpClient::new(handle::MCTP);
 
-    // Register a listener for type-1 messages
+    // Register a listener for vendor-defined PCIe messages (0x7e)
     let listener = client
         .listener(ECHO_MSG_TYPE)
-        .map_err(|_| pw_status::Error::Internal)?;
+        .map_err(|e| {
+            pw_log::error!(
+                "Failed to register listener for msg_type 0x{:02x}: error code {}",
+                ECHO_MSG_TYPE as u32,
+                e.code as u32,
+            );
+            pw_status::Error::Internal
+        })?;
 
     let mut buf = [0u8; 1024];
 
@@ -47,18 +58,33 @@
             .recv(listener, 0, &mut buf)
             .map_err(|_| pw_status::Error::Internal)?;
 
-        pw_log::info!(
-            "Echo: received {} bytes from EID {}",
-            meta.payload_size as u32,
-            meta.remote_eid as u32,
-        );
+        // Verify vendor ID (first 2 bytes should be AMD PCIe vendor ID in little-endian)
+        if meta.payload_size >= 2 {
+            let vendor_id = u16::from_le_bytes([buf[0], buf[1]]);
+            pw_log::info!(
+                "Echo: received {} bytes from EID {}, vendor ID: 0x{:04x}",
+                meta.payload_size as u32,
+                meta.remote_eid as u32,
+                vendor_id as u32,
+            );
 
-        // Echo the payload back
+            if vendor_id != AMD_PCIE_VENDOR_ID {
+                pw_log::warn!(
+                    "Unexpected vendor ID 0x{:04x}, expected AMD 0x{:04x}",
+                    vendor_id as u32,
+                    AMD_PCIE_VENDOR_ID as u32,
+                );
+            }
+        } else {
+            pw_log::warn!("Payload too short: {} bytes", meta.payload_size as u32);
+        }
+
+        // Echo the payload back (including vendor ID)
         let payload = &buf[..meta.payload_size];
         client
             .send(
                 None,                    // no request handle (this is a response)
-                meta.msg_type,           // same message type
+                meta.msg_type,           // same message type (0x7e)
                 Some(meta.remote_eid),   // back to sender
                 Some(meta.msg_tag),      // same tag
                 meta.msg_ic,             // preserve integrity check
diff --git a/target/ast1060-evb/mctp/system.json5 b/target/ast1060-evb/mctp/system.json5
index 86c2abd..4a2a74a 100644
--- a/target/ast1060-evb/mctp/system.json5
+++ b/target/ast1060-evb/mctp/system.json5
@@ -5,15 +5,16 @@
 // 768KB SRAM (640KB usable), executes from RAM
 //
 // Memory Layout (PMSAv7-friendly, power-of-2 aligned regions):
+// FIXED: Kernel RAM now comes BEFORE application RAM to avoid boot crashes
 //   0x00000000 - 0x00000500: Vector table + kernel annotations (1280 bytes)
-//   0x00000500 - 0x00020000: Kernel flash (~127KB, ends at 128KB boundary)
+//   0x00000500 - 0x00020000: Kernel flash (~127KB)
 //   0x00020000 - 0x00040000: i2c_server flash (128KB)
-//   0x00040000 - 0x00060000: mctp_server flash (128KB)
-//   0x00060000 - 0x00080000: mctp_echo flash (128KB)
-//   0x00080000 - 0x00088000: i2c_server RAM (32KB)
-//   0x00088000 - 0x00090000: mctp_server RAM (32KB)
-//   0x00090000 - 0x00098000: mctp_echo RAM (32KB)
-//   0x00098000 - 0x000A0000: Kernel RAM (32KB)
+//   0x00040000 - 0x00050000: mctp_server flash (64KB)
+//   0x00050000 - 0x00060000: mctp_echo flash (64KB)
+//   0x00060000 - 0x00080000: Kernel RAM (128KB) <- MOVED HERE
+//   0x00080000 - 0x00090000: i2c_server RAM (64KB)
+//   0x00090000 - 0x00098000: mctp_server RAM (32KB)
+//   0x00098000 - 0x000A0000: mctp_echo RAM (32KB)
 //
 // Total: 0xA0000 = 640KB (fits within the 640KB usable window of the AST1060's 768KB SRAM).
 {
@@ -25,8 +26,8 @@
     kernel: {
         flash_start_address: 0x00000500,  // After vector table + annotations
         flash_size_bytes: 129792,         // 0x1FB00: ends at exactly 0x20000 (128KB boundary)
-        ram_start_address: 0x00098000,    // After all app RAM (3 × 32KB = 0x18000 past 0x80000)
-        ram_size_bytes: 32768,            // 32KB
+        ram_start_address: 0x00060000,    // Kernel RAM comes FIRST, before all app RAM
+        ram_size_bytes: 131072,           // 128KB
     },
     apps: [
         // ──── I2C Server ────
@@ -35,7 +36,7 @@
         {
             name: "i2c_server",
             flash_size_bytes: 131072,     // 128KB for server code
-            ram_size_bytes: 32768,        // 32KB RAM (register maps + buffers)
+            ram_size_bytes: 65536,        // 64KB RAM (register maps + buffers)
             process: {
                 name: "i2c server process",
                 objects: [
@@ -75,7 +76,7 @@
         // uses I2C transport for on-the-wire communication.
         {
             name: "mctp_server",
-            flash_size_bytes: 131072,     // 128KB for MCTP server code
+            flash_size_bytes: 65536,      // 64KB for MCTP server code
             ram_size_bytes: 32768,        // 32KB RAM (Router state, buffers)
             process: {
                 name: "mctp server process",
@@ -109,7 +110,7 @@
         // Listens for MCTP type-1 messages and echoes the payload back.
         {
             name: "mctp_echo",
-            flash_size_bytes: 131072,     // 128KB for echo app
+            flash_size_bytes: 65536,      // 64KB for echo app
             ram_size_bytes: 32768,        // 32KB RAM
             process: {
                 name: "mctp echo process",
diff --git a/target/ast1060-evb/mctp/target.rs b/target/ast1060-evb/mctp/target.rs
index 3cb0a2e..931afa5 100644
--- a/target/ast1060-evb/mctp/target.rs
+++ b/target/ast1060-evb/mctp/target.rs
@@ -8,7 +8,6 @@
 #![no_std]
 #![no_main]
 
-use cortex_m_semihosting::debug::{EXIT_FAILURE, EXIT_SUCCESS, exit};
 use target_common::{TargetInterface, declare_target};
 use {console_backend as _, entry as _};
 
@@ -25,11 +24,6 @@
 
     fn shutdown(code: u32) -> ! {
         pw_log::info!("Shutting down with code {}", code as u32);
-        let status = match code {
-            0 => EXIT_SUCCESS,
-            _ => EXIT_FAILURE,
-        };
-        exit(status);
         #[expect(clippy::empty_loop)]
         loop {}
     }