diff --git a/Makefile.dep b/Makefile.dep
index 4a811c91a31f18a12ad8e4b1a0ebde71a7b62156..d01275961d6914a2efc784570db2302b98a2aca8 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -808,6 +808,10 @@ ifneq (,$(filter periph_gpio_irq,$(USEMODULE)))
   FEATURES_REQUIRED += periph_gpio
 endif
 
+ifneq (,$(filter riotboot_slot, $(USEMODULE)))
+  USEMODULE += riotboot_hdr
+endif
+
 # always select gpio (until explicit dependencies are sorted out)
 FEATURES_OPTIONAL += periph_gpio
 
diff --git a/Makefile.include b/Makefile.include
index 93a9958cd8e28357631a5dcdad6d67e5c1baacd3..d85ce551da7dd4038390db2e735b17daa4d55e4f 100644
--- a/Makefile.include
+++ b/Makefile.include
@@ -434,6 +434,11 @@ else
   _LINK = $(if $(CPPMIX),$(LINKXX),$(LINK)) $(UNDEF) $(LINKFLAGPREFIX)--start-group $(BASELIBS) -lm $(LINKFLAGPREFIX)--end-group $(LINKFLAGS) $(LINKFLAGPREFIX)-Map=$(BINDIR)/$(APPLICATION).map
 endif # BUILDOSXNATIVE
 
+# include bootloaders support. When trying to overwrite one variable
+# like HEXFILE, the value will already have been evaluated when declaring
+# the link target. Therefore, it must be placed before `link`.
+include $(RIOTMAKE)/boot/riotboot.mk
+
 ifeq ($(BUILD_IN_DOCKER),1)
 link: ..in-docker-container
 else
diff --git a/boards/common/iotlab/Makefile.features b/boards/common/iotlab/Makefile.features
index 5b0e6f92e99b3243798b97478dffa1e7c4ff924b..c4cd705c6d051df1bee5a8c8a4bf221f041d5af3 100644
--- a/boards/common/iotlab/Makefile.features
+++ b/boards/common/iotlab/Makefile.features
@@ -6,5 +6,8 @@ FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
+# Put other features for this board (in alphabetical order)
+FEATURES_PROVIDED += riotboot
+
 # The board MPU family (used for grouping by the CI system)
 FEATURES_MCU_GROUP = cortex_m3_1
diff --git a/boards/nucleo-l152re/Makefile.features b/boards/nucleo-l152re/Makefile.features
index ad81f0d97e9836a2c33651808a5720d32c40fdce..459e65383d2679fc8330f26d2b083de84dfad78e 100644
--- a/boards/nucleo-l152re/Makefile.features
+++ b/boards/nucleo-l152re/Makefile.features
@@ -9,6 +9,9 @@ FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
+# Put other features for this board (in alphabetical order)
+FEATURES_PROVIDED += riotboot
+
 # load the common Makefile.features for Nucleo boards
 include $(RIOTBOARD)/common/nucleo64/Makefile.features
 
diff --git a/boards/saml21-xpro/Makefile.features b/boards/saml21-xpro/Makefile.features
index edd3b96f393a0804d8023242b18a1ebef8905256..651f526750ea53f582df0bd9edd5f65fa0c8ba95 100644
--- a/boards/saml21-xpro/Makefile.features
+++ b/boards/saml21-xpro/Makefile.features
@@ -7,6 +7,9 @@ FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
+# Put other features for this board (in alphabetical order)
+FEATURES_PROVIDED += riotboot
+
 # The board MPU family (used for grouping by the CI system)
 FEATURES_MCU_GROUP = cortex_m0_2
 
diff --git a/boards/samr21-xpro/Makefile.features b/boards/samr21-xpro/Makefile.features
index 432747244252b7600268f96949635bae4a759b51..02f222cb2a25dabd0a16ab7860eb19e03885c0fa 100644
--- a/boards/samr21-xpro/Makefile.features
+++ b/boards/samr21-xpro/Makefile.features
@@ -8,6 +8,9 @@ FEATURES_PROVIDED += periph_spi
 FEATURES_PROVIDED += periph_timer
 FEATURES_PROVIDED += periph_uart
 
+# Put other features for this board (in alphabetical order)
+FEATURES_PROVIDED += riotboot
+
 # The board MPU family (used for grouping by the CI system)
 FEATURES_MCU_GROUP = cortex_m0_2
 
diff --git a/bootloaders/riotboot/Makefile b/bootloaders/riotboot/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b8398cc24a4194f502f36719153a4e66e9459fc2
--- /dev/null
+++ b/bootloaders/riotboot/Makefile
@@ -0,0 +1,25 @@
+# Default RIOT bootloader
+APPLICATION = riotboot
+
+# Default testing board
+BOARD ?= samr21-xpro
+
+# Select the boards with riotboot feature
+FEATURES_REQUIRED += riotboot
+
+# Disable unused modules
+CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE
+DISABLE_MODULE += auto_init
+
+# Include riotboot flash partition functionality
+USEMODULE += riotboot_slot
+
+# RIOT codebase
+RIOTBASE ?= $(CURDIR)/../../
+
+include $(RIOTBASE)/Makefile.include
+
+# limit riotboot bootloader size
+# TODO: Manage to set this variable for boards which already embed a
+# bootloader, currently it will be overwritten
+ROM_LEN := $(RIOTBOOT_LEN)
diff --git a/bootloaders/riotboot/README.md b/bootloaders/riotboot/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..90af49a94464f373d749bc069c4ebcb9ac48d406
--- /dev/null
+++ b/bootloaders/riotboot/README.md
@@ -0,0 +1,75 @@
+# Overview
+This folder contains a simple bootloader called "riotboot".
+A header with metadata of length `RIOTBOOT_HDR_LEN` precedes
+the RIOT firmware. The header contains "RIOT" as a magic
+number to recognise a RIOT firmware image, a checksum, and
+the version of the RIOT firmware `APP_VER`.
+This bootloader verifies the checksum of the header which is located
+at an offset (`ROM_OFFSET`) with respect to  the `ROM_START_ADDR`
+defined by the CPU, just after the space allocated for riotboot.
+
+riotboot consists of:
+
+  - This application which serves as minimal bootloader,
+  - the module "riotboot_hdr" used to recognise RIOT firmware which riotboot
+    can boot,
+  - the module "riotboot_slot" used to manage the partitions (slots) with a
+    RIOT header attached to them,
+  - a tool in dist/tools/riotboot_gen_hdr for header generation,
+  - several make targets to glue everything together.
+
+## Concept
+`riotboot` expects the flash to be formatted in slots: at CPU_FLASH_BASE
+address resides the bootloader, which is followed by a slot 0 with a
+RIOT firmware.
+
+A RIOT firmware in a single slot is composed by:
+
+```
+|------------------------------- FLASH -------------------------------------|
+|----- RIOTBOOT_LEN ----|----------- RIOTBOOT_SLOT_SIZE (slot 0) -----------|
+                        |----- RIOTBOOT_HDR_LEN ------|
+ ---------------------------------------------------------------------------
+|        riotboot       | riotboot_hdr_t + filler (0) |   RIOT firmware     |
+ ---------------------------------------------------------------------------
+```
+
+Please note that `RIOTBOOT_HDR_LEN` depends on the architecture of the
+MCU, since it needs to be aligned to 256B. This is fixed regardless of
+`sizeof(riotboot_hdr_t)`
+
+The bootloader will, on reset, verify the checksum of the first slot header,
+then boot it. If the slot doesn't have a valid checksum, no image will be
+booted and the bootloader will enter `while(1);` endless loop.
+
+# Requirements
+A board capable to use riotboot must meet the following requirements:
+
+  - Embed a Cortex-M0+/3/4/7 processor
+  - Provide the variables `ROM_START_ADDR` and `ROM_LEN`
+  - Use cpu/cortexm_common/ldscripts/cortexm.ld ld script
+  - Pass the cortexm_common_ldscript test in tests/
+  - Being able to execute startup code at least twice (`board_init()`)
+  - Declare `FEATURES_PROVIDED += riotboot` to pull the rigth dependencies
+  - Being able to flash binary files, if integration with the build
+    system is required for flashing
+
+The above requirements are usually met if the board succeeds to execute
+the riotboot test in tests/.
+
+# Usage
+Just compile your application using the target `riotboot`. The header
+is generated automatically according to your `APP_VER`, which can be
+optionally set (0 by default) in your makefile.
+
+## Flashing
+The image can be flashed using `riotboot/flash` which also flashes
+the bootloader.
+
+e.g. `BOARD=samr21-xpro FEATURES_REQUIRED+=riotboot APP_VER=$(date +%s) make -C examples/hello-world riotboot/flash`
+
+The command compiles both the hello-world example and riotboot,
+generates the header and attaches it at the beginning of the example
+binary.
+
+A comprehensive test is available at tests/riotboot.
diff --git a/bootloaders/riotboot/main.c b/bootloaders/riotboot/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..ce6914296c7f93d19fdc4d1d33daa54be2d656d9
--- /dev/null
+++ b/bootloaders/riotboot/main.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *                    Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    bootloaders    RIOT compatible bootloaders
+ * @ingroup     bootloaders
+ * @{
+ *
+ * @file
+ * @brief      Minimal riot-based bootloader
+ *
+ * @author     Kaspar Schleiser <kaspar@schleiser.de>
+ * @author     Francisco Acosta <francisco.acosta@inria.fr>
+ *
+ * @}
+ */
+
+#include "cpu.h"
+#include "panic.h"
+#include "riotboot/slot.h"
+
+void kernel_init(void)
+{
+    /* bootloader boots only slot 0 if it is valid */
+    unsigned slot = 0;
+
+    if (riotboot_slot_validate(slot) == 0) {
+        riotboot_slot_jump(slot);
+    }
+
+    /* serious trouble! */
+    while (1) {}
+}
+
+NORETURN void core_panic(core_panic_t crash_code, const char *message)
+{
+    (void)crash_code;
+    (void)message;
+    while (1) {}
+}
diff --git a/cpu/cortexm_common/Makefile.include b/cpu/cortexm_common/Makefile.include
index 60e5938bc439bdc1679bc34e6cf016ff3abbe387..f99c5b6cda97e73711905b2ebfb6cd8edfc9bf5c 100644
--- a/cpu/cortexm_common/Makefile.include
+++ b/cpu/cortexm_common/Makefile.include
@@ -24,3 +24,11 @@ TOOLCHAINS_SUPPORTED = gnu llvm
 LINKFLAGS += $(if $(ROM_OFFSET),$(LINKFLAGPREFIX)--defsym=_rom_offset=$(ROM_OFFSET))
 # FW_ROM_LEN: rom length to use for firmware linking. Allows linking only in a section of the rom.
 LINKFLAGS += $(if $(FW_ROM_LEN),$(LINKFLAGPREFIX)--defsym=_fw_rom_length=$(FW_ROM_LEN))
+
+
+# Configure riotboot bootloader and slot lengths
+# 4KB are currently enough
+RIOTBOOT_LEN ?= 0x1000
+# Take the whole flash minus RIOTBOOT_LEN
+SLOT0_LEN ?= $(shell printf "0x%x" $$(($(ROM_LEN:%K=%*1024)-$(RIOTBOOT_LEN))))
+SLOT0_LEN := $(SLOT0_LEN)
diff --git a/cpu/cortexm_common/include/cpu.h b/cpu/cortexm_common/include/cpu.h
index 50ba3b3e12e5a6ceabab5a321edb056b5408f721..aa1364c4922014fdd7a9d053776dd22b1bc2642b 100644
--- a/cpu/cortexm_common/include/cpu.h
+++ b/cpu/cortexm_common/include/cpu.h
@@ -189,6 +189,44 @@ static inline void cortexm_isr_end(void)
     }
 }
 
+/**
+ * @brief   Jumps to another image in flash
+ *
+ * This function is supposed to be called by a bootloader application.
+ *
+ * @param[in]   image_address   address in flash of other image
+ */
+static inline void cpu_jump_to_image(uint32_t image_address)
+{
+    /* Disable IRQ */
+    __disable_irq();
+
+    /* set MSP */
+    __set_MSP(*(uint32_t*)image_address);
+
+    /* skip stack pointer */
+    image_address += 4;
+
+    /* load the images reset_vector address */
+    uint32_t destination_address = *(uint32_t*)image_address;
+
+    /* Make sure the Thumb State bit is set. */
+    destination_address |= 0x1;
+
+    /* Branch execution */
+    __asm("BX %0" :: "r" (destination_address));
+}
+
+/* The following register is only present for Cortex-M0+, -M3, -M4 and -M7 CPUs */
+#if defined(CPU_ARCH_CORTEX_M0PLUS) || defined(CPU_ARCH_CORTEX_M3) || \
+    defined(CPU_ARCH_CORTEX_M4) || defined(CPU_ARCH_CORTEX_M4F) || \
+    defined(CPU_ARCH_CORTEX_M7)
+static inline uint32_t cpu_get_image_baseaddr(void)
+{
+    return SCB->VTOR;
+}
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/dist/tools/riotboot_gen_hdr/Makefile b/dist/tools/riotboot_gen_hdr/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..dd57e45f27107856e5c16abc913baacd2e98fe62
--- /dev/null
+++ b/dist/tools/riotboot_gen_hdr/Makefile
@@ -0,0 +1,39 @@
+RIOTBASE       := ../../..
+RIOT_INCLUDE   := $(RIOTBASE)/sys/include
+RIOT_CORE_INC  := $(RIOTBASE)/core/include
+NATIVE_INCLUDE := $(RIOTBASE)/cpu/native/include
+COMMON_SRC     := common.c
+COMMON_HDR     := common.h
+
+RIOT_HDR_SRC := \
+	$(RIOTBASE)/sys/checksum/fletcher32.c \
+	$(RIOTBASE)/sys/riotboot/hdr.c
+
+RIOT_HDR_HDR := $(RIOT_INCLUDE)/riotboot/hdr.h \
+	$(RIOT_INCLUDE)/checksum/fletcher32.h \
+	$(RIOTBASE)/core/include/byteorder.h
+
+GENHDR_SRC := $(COMMON_SRC) $(RIOT_HDR_SRC) \
+	main.c genhdr.c
+
+GENHDR_HDR := $(COMMON_HDR) $(RIOT_HDR_HDR)
+
+CFLAGS += -g -I. -O3 -Wall -Wextra -pedantic -std=c99
+
+ifeq ($(QUIET),1)
+  Q=@
+else
+  Q=
+endif
+
+all: bin/genhdr
+
+bin/:
+	$(Q)mkdir -p bin
+
+bin/genhdr: $(GENHDR_HDR) $(GENHDR_SRC) Makefile | bin/
+	$(Q)$(CC) $(CFLAGS) -I$(RIOT_INCLUDE) -I$(RIOT_CORE_INC) \
+		-I$(NATIVE_INCLUDE) $(GENHDR_SRC) -o $@
+
+clean:
+	$(Q)rm -rf bin/
diff --git a/dist/tools/riotboot_gen_hdr/common.c b/dist/tools/riotboot_gen_hdr/common.c
new file mode 100644
index 0000000000000000000000000000000000000000..c3cb771b6a15c183369e5d91a5c5bc14c06b11f4
--- /dev/null
+++ b/dist/tools/riotboot_gen_hdr/common.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file LICENSE for more details.
+ */
+
+/**
+ * @file
+ * @brief       Common tools for RIOT images header generation
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int to_file(const char *filename, void *buf, size_t len)
+{
+    int fd;
+
+    if (strcmp("-", filename)) {
+        fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+    }
+    else {
+        fd = STDOUT_FILENO;
+    }
+
+    if (fd > 0) {
+        ssize_t res = write(fd, buf, len);
+        close(fd);
+        return res == (ssize_t)len;
+    }
+    else {
+        return fd;
+    }
+}
diff --git a/dist/tools/riotboot_gen_hdr/common.h b/dist/tools/riotboot_gen_hdr/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3380d8ffb4b3d777cb9048e8f58f9636d573a0a
--- /dev/null
+++ b/dist/tools/riotboot_gen_hdr/common.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file LICENSE for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * @brief  Write len bytes of a given buffer into a file
+ *
+ * @param[out] filename    name of the file to be written
+ * @param[in]  buf         a pointer to the buffer which needs to be written
+ * @param[in]  len         the number of bytes from buf to be written
+ *
+ */
+int to_file(const char *filename, void *buf, size_t len);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* COMMON_H */
diff --git a/dist/tools/riotboot_gen_hdr/genhdr.c b/dist/tools/riotboot_gen_hdr/genhdr.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8559d589fddc002ff07c0a1ad94af9b6666ae46
--- /dev/null
+++ b/dist/tools/riotboot_gen_hdr/genhdr.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Inria
+ *               2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file LICENSE for more details.
+ *
+ */
+
+/**
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "riotboot/hdr.h"
+#include "common.h"
+
+/**
+ *  @brief Alignment required to set VTOR in Cortex-M0+/3/4/7
+ */
+#define HDR_ALIGN              (256)
+
+static void populate_hdr(riotboot_hdr_t *hdr, uint32_t ver, uint32_t addr)
+{
+    /* ensure the buffer and header have 0's */
+    memset(hdr, '\0', sizeof(riotboot_hdr_t));
+
+    /* Generate image header */
+    hdr->magic_number = RIOTBOOT_MAGIC;
+    hdr->version = ver;
+    hdr->start_addr = addr;
+
+    /* calculate header checksum */
+    hdr->chksum = riotboot_hdr_checksum(hdr);
+}
+
+int genhdr(int argc, char *argv[])
+{
+    const char generate_usage[] = "<IMG_BIN> <APP_VER> <START_ADDR> <HDR_LEN> <outfile|->";
+
+    /* riotboot_hdr buffer */
+    uint8_t *hdr_buf;
+
+    /* arguments storage variables */
+    long app_ver_arg = 0;
+    long start_addr_arg = 0;
+    long hdr_len_arg = 0;
+
+    /* header variables */
+    size_t hdr_len = 0;
+    uint32_t app_ver = 0;
+    uint32_t start_addr = 0;
+
+    /* helpers */
+    errno = 0;
+    char *p;
+
+    if (argc < 6) {
+        fprintf(stderr, "usage: genhdr generate %s\n", generate_usage);
+        return -1;
+    }
+
+    app_ver_arg = strtol(argv[2], &p, 0);
+    if (errno != 0 || *p != '\0' || app_ver_arg > UINT32_MAX) {
+        fprintf(stderr, "Error: APP_VER not valid!\n");
+    }
+    else {
+        app_ver = app_ver_arg;
+    }
+
+    start_addr_arg = strtol(argv[3], &p, 0);
+    if (errno != 0 || *p != '\0' || start_addr_arg > UINT32_MAX) {
+        fprintf(stderr, "Error: START_ADDR not valid!\n");
+    }
+    else {
+        start_addr = start_addr_arg;
+    }
+
+    hdr_len_arg = strtol(argv[4], &p, 0);
+    if (errno != 0 || *p != '\0' || hdr_len_arg % HDR_ALIGN || hdr_len_arg > UINT32_MAX) {
+        fprintf(stderr, "Error: HDR_LEN not valid!\n");
+        return -1;
+    }
+    else {
+        hdr_len = hdr_len_arg;
+    }
+
+    /* prepare a 0 initialised buffer for riotboot_hdr_t */
+    hdr_buf = calloc(1, hdr_len);
+    if (hdr_buf == NULL) {
+        fprintf(stderr, "Error: not enough memory!\n");
+        return -1;
+    }
+
+    populate_hdr((riotboot_hdr_t*)hdr_buf, app_ver, start_addr);
+
+    /* Write the header */
+    if (!to_file(argv[5], hdr_buf, hdr_len)) {
+        fprintf(stderr, "Error: cannot write output\n");
+        free(hdr_buf);
+        return 1;
+    }
+
+    free(hdr_buf);
+
+    return 0;
+}
diff --git a/dist/tools/riotboot_gen_hdr/main.c b/dist/tools/riotboot_gen_hdr/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..6288fa29edda6b155c743e25f77e57aa8cceb8d1
--- /dev/null
+++ b/dist/tools/riotboot_gen_hdr/main.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *               2018 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file LICENSE for more details.
+ */
+
+/**
+ * @file
+ * @brief       Header generation tool for RIOT firmware images
+ *
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+int genhdr(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+    char *usage = "genhdr generate [args]";
+
+    if (argc < 2) {
+        goto usage;
+    }
+    else if (!strcmp(argv[1], "generate")) {
+        return genhdr(argc - 1, &argv[1]);
+    }
+
+usage:
+    fprintf(stderr, "usage: %s\n", usage);
+    return 1;
+}
diff --git a/makefiles/boot/riotboot.mk b/makefiles/boot/riotboot.mk
new file mode 100644
index 0000000000000000000000000000000000000000..ab0c6e63fa3b23402ffa49bbc2731ac341a550dc
--- /dev/null
+++ b/makefiles/boot/riotboot.mk
@@ -0,0 +1,122 @@
+ifneq (,$(filter riotboot,$(FEATURES_USED)))
+
+.PHONY: riotboot/flash riotboot/flash-bootloader riotboot/flash-slot0 riotboot/bootloader/%
+
+RIOTBOOT_DIR = $(RIOTBASE)/bootloaders/riotboot
+RIOTBOOT ?= $(RIOTBOOT_DIR)/bin/$(BOARD)/riotboot.elf
+CFLAGS += -I$(BINDIR)/riotbuild
+
+HEADER_TOOL_DIR = $(RIOTBASE)/dist/tools/riotboot_gen_hdr
+HEADER_TOOL ?= $(HEADER_TOOL_DIR)/bin/genhdr
+BINDIR_APP = $(BINDIR)/$(APPLICATION)
+
+# Indicate the reserved space for a header, 256B by default
+# Notice that it must be 256B aligned. This is restricted by
+# the Cortex-M0+/3/4/7 architecture
+RIOTBOOT_HDR_LEN ?= 0x100
+
+# Export variables for 'riotboot_slot'
+export SLOT0_LEN
+
+# By default, slot 0 is found just after RIOTBOOT_LEN. It might
+# be overridden to add more offset as needed.
+export SLOT0_OFFSET ?= $(RIOTBOOT_LEN)
+
+# Mandatory APP_VER, set to 0 by default
+APP_VER ?= 0
+
+# Final target for slot 0 with riot_hdr
+SLOT0_RIOT_BIN = $(BINDIR_APP)-slot0.riot.bin
+
+# For slot generation only link is needed
+$(BINDIR_APP)-%.elf: link
+	$(Q)$(_LINK) -o $@
+
+# Slot 0 firmware offset, after header
+SLOT0_IMAGE_OFFSET := $$(($(RIOTBOOT_LEN) + $(RIOTBOOT_HDR_LEN)))
+
+# Link slots ELF *after* riot_hdr and limit the ROM to the slots length
+$(BINDIR_APP)-slot0.elf: FW_ROM_LEN=$$((SLOT0_LEN - $(RIOTBOOT_HDR_LEN)))
+$(BINDIR_APP)-slot0.elf: ROM_OFFSET=$(SLOT0_IMAGE_OFFSET)
+
+# Create binary target with RIOT header
+$(SLOT0_RIOT_BIN): %.riot.bin: %.hdr %.bin
+	@echo "creating $@..."
+	$(Q)cat $^ > $@
+
+# Compile header tool if it doesn't exist, force its compilation in case
+# some files changed
+$(HEADER_TOOL): FORCE
+	@echo "compiling $@..."
+	$(Q)/usr/bin/env -i \
+		QUIET=$(QUIET) \
+		PATH=$(PATH) \
+			$(MAKE) --no-print-directory -C $(HEADER_TOOL_DIR) all
+
+# Generate RIOT header and keep the original binary file
+# It must be always regenerated in case of any changes, so FORCE
+.PRECIOUS: %.bin
+%.hdr: $(HEADER_TOOL) %.bin FORCE
+	$(Q)$(HEADER_TOOL) generate $< $(APP_VER) $$(($(ROM_START_ADDR)+$(OFFSET))) $(RIOTBOOT_HDR_LEN) - > $@
+
+$(BINDIR_APP)-slot0.hdr: OFFSET=$(SLOT0_IMAGE_OFFSET)
+
+# Generic target to create a binary file from the image with header
+riotboot: $(SLOT0_RIOT_BIN)
+
+# riotboot bootloader compile target
+riotboot/flash-bootloader: riotboot/bootloader/flash
+riotboot/bootloader/%:
+	$(Q)/usr/bin/env -i \
+		QUIET=$(QUIET)\
+		PATH=$(PATH) BOARD=$(BOARD) \
+			$(MAKE) --no-print-directory -C $(RIOTBOOT_DIR) $*
+
+# Generate a binary file from the bootloader which fills all the
+# allocated space. This is used afterwards to create a combined
+# binary with both bootloader and RIOT firmware with header
+BOOTLOADER_BIN = $(RIOTBOOT_DIR)/bin/$(BOARD)
+$(BOOTLOADER_BIN)/riotboot.extended.bin: $(BOOTLOADER_BIN)/riotboot.bin
+	$(Q)cp $^ $@.tmp
+	$(Q)truncate -s $$(($(RIOTBOOT_LEN))) $@.tmp
+	$(Q)mv $@.tmp $@
+
+# Only call sub make if not already in riotboot
+ifneq ($(BOOTLOADER_BIN)/riotboot.bin,$(BINFILE))
+  $(BOOTLOADER_BIN)/riotboot.bin: riotboot/bootloader/binfile
+endif
+
+# Create combined binary booloader + RIOT firmware with header
+RIOTBOOT_COMBINED_BIN = $(BINDIR_APP)-slot0-combined.bin
+riotboot/combined-slot0: $(RIOTBOOT_COMBINED_BIN)
+$(RIOTBOOT_COMBINED_BIN): $(BOOTLOADER_BIN)/riotboot.extended.bin $(SLOT0_RIOT_BIN)
+	$(Q)cat $^ > $@
+
+# Flashing rule for edbg to flash combined binaries
+riotboot/flash-combined-slot0: HEXFILE=$(RIOTBOOT_COMBINED_BIN)
+# Flashing rule for openocd to flash combined binaries
+riotboot/flash-combined-slot0: ELFFILE=$(RIOTBOOT_COMBINED_BIN)
+riotboot/flash-combined-slot0: $(RIOTBOOT_COMBINED_BIN) $(FLASHDEPS)
+	$(FLASHER) $(FFLAGS)
+
+# Flashing rule for slot 0
+riotboot/flash-slot0: export IMAGE_OFFSET=$(SLOT0_OFFSET)
+# Flashing rule for edbg to flash only slot 0
+riotboot/flash-slot0: HEXFILE=$(SLOT0_RIOT_BIN)
+# openocd
+riotboot/flash-slot0: ELFFILE=$(SLOT0_RIOT_BIN)
+riotboot/flash-slot0: $(SLOT0_RIOT_BIN) $(FLASHDEPS)
+	$(FLASHER) $(FFLAGS)
+
+# Targets to generate only slot 0 binary
+riotboot/slot0: $(SLOT0_RIOT_BIN)
+
+# Default flashing rule for bootloader + slot 0
+riotboot/flash: riotboot/flash-slot0 riotboot/flash-bootloader
+
+else
+riotboot:
+	$(Q)echo "error: riotboot feature not selected! (try FEATURES_REQUIRED += riotboot)"
+	$(Q)false
+
+endif # (,$(filter riotboot,$(FEATURES_USED)))
diff --git a/sys/include/riotboot/slot.h b/sys/include/riotboot/slot.h
new file mode 100644
index 0000000000000000000000000000000000000000..24d65e32da174ce45b1232cf117bebe0c4745777
--- /dev/null
+++ b/sys/include/riotboot/slot.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
+ *                    Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    sys_riotboot_slot   Helpers to manipulate partitions (slots)
+ *                              on internal flash
+ * @ingroup     sys
+ * @{
+ *
+ * @file
+ * @brief       Slot management tools
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ *
+ * @}
+ */
+
+#ifndef RIOTBOOT_SLOT_H
+#define RIOTBOOT_SLOT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "riotboot/hdr.h"
+
+/**
+ * @brief  Get currently running image slot
+ *
+ * @returns nr of currently active slot
+ */
+int riotboot_slot_current(void);
+
+/**
+ * @brief  Get jump-to address of image slot
+ *
+ * @param[in]   slot    slot nr to work on
+ *
+ * @returns address of first byte of @p slot
+ */
+uint32_t riotboot_slot_get_image_startaddr(unsigned slot);
+
+/**
+ * @brief  Boot into image in slot @p slot
+ *
+ * @param[in]   slot    slot nr to jump to
+ */
+void riotboot_slot_jump(unsigned slot);
+
+/**
+ * @brief  Get header from a given flash slot
+ *
+ * @param[in]   slot    slot nr to work on
+ *
+ * @returns header of image slot nr @p slot
+ */
+const riotboot_hdr_t *riotboot_slot_get_hdr(unsigned slot);
+
+/**
+ * @brief  Validate slot
+ *
+ * @param[in] slot    slot nr to work on
+ *
+ * @returns 0 if ok.
+ */
+static inline int riotboot_slot_validate(unsigned slot)
+{
+    return riotboot_hdr_validate(riotboot_slot_get_hdr(slot));
+}
+
+/**
+ * @brief  Print formatted slot header to STDIO
+ *
+ * @param[in]   slot    slot nr to work on
+ *
+ */
+static inline void riotboot_slot_print_hdr(unsigned slot)
+{
+    riotboot_hdr_print(riotboot_slot_get_hdr(slot));
+}
+
+/**
+ * @brief  Dump the addresses of all configured slots
+ *
+ */
+void riotboot_slot_dump_addrs(void);
+
+/**
+ * @brief   Number of configured firmware slots (incl. bootloader slot)
+ */
+extern const unsigned riotboot_slot_numof;
+
+/**
+ * @brief   Storage for header pointers of the configured slots
+ */
+extern const riotboot_hdr_t * const riotboot_slots[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RIOTBOOT_SLOT_H */
diff --git a/sys/riotboot/Makefile b/sys/riotboot/Makefile
index cd1af2456e0554b9f34f996712ad3fa356b0d770..344672d2e216db7b2213058ff46de27039e661d0 100644
--- a/sys/riotboot/Makefile
+++ b/sys/riotboot/Makefile
@@ -1,3 +1,7 @@
 SUBMODULES := 1
 
+ifneq (,$(filter riotboot_slot,$(USEMODULE)))
+    CFLAGS += -DSLOT0_OFFSET=$(SLOT0_OFFSET)
+endif
+
 include $(RIOTBASE)/Makefile.base
diff --git a/sys/riotboot/slot.c b/sys/riotboot/slot.c
new file mode 100644
index 0000000000000000000000000000000000000000..09cac62a789bf28990d9029874546539dc65bee8
--- /dev/null
+++ b/sys/riotboot/slot.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
+ *                    Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     sys_riotboot_slot
+ * @{
+ *
+ * @file
+ * @brief       Slot management functions
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ *
+ * @}
+ */
+#include <string.h>
+
+#include "cpu.h"
+#include "riotboot/slot.h"
+#include "riotboot/hdr.h"
+
+/*
+ * Store the start addresses of each slot.
+ * Take into account that CPU_FLASH_BASE represents the starting
+ * address of the bootloader, thus the header is located after the
+ * space reserved to the bootloader.
+ */
+const riotboot_hdr_t * const riotboot_slots[] = {
+    (riotboot_hdr_t*)(CPU_FLASH_BASE + SLOT0_OFFSET),   /* First slot address -> firmware image */
+};
+
+/* Calculate the number of slots */
+const unsigned riotboot_slot_numof = sizeof(riotboot_slots) / sizeof(riotboot_hdr_t*);
+
+static void _riotboot_slot_jump_to_image(const riotboot_hdr_t *hdr)
+{
+    cpu_jump_to_image(hdr->start_addr);
+}
+
+int riotboot_slot_current(void)
+{
+    uint32_t base_addr = cpu_get_image_baseaddr();
+
+    for (unsigned i = 0; i < riotboot_slot_numof; i++) {
+        const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(i);
+        if (base_addr == hdr->start_addr) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+void riotboot_slot_jump(unsigned slot)
+{
+    _riotboot_slot_jump_to_image(riotboot_slot_get_hdr(slot));
+}
+
+uint32_t riotboot_slot_get_image_startaddr(unsigned slot)
+{
+    return riotboot_slot_get_hdr(slot)->start_addr;
+}
+
+void riotboot_slot_dump_addrs(void)
+{
+    for (unsigned slot = 0; slot < riotboot_slot_numof; slot++) {
+        const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(slot);
+
+        printf("slot %u: metadata: %p image: 0x%08" PRIx32 "\n", slot,
+               hdr,
+               hdr->start_addr);
+    }
+}
+
+const riotboot_hdr_t *riotboot_slot_get_hdr(unsigned slot)
+{
+    assert(slot < riotboot_slot_numof);
+
+    return riotboot_slots[slot];
+}
diff --git a/tests/riotboot/Makefile b/tests/riotboot/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c4b90fa4e37902fbb9797527daa8baf38c1d5798
--- /dev/null
+++ b/tests/riotboot/Makefile
@@ -0,0 +1,34 @@
+# If no BOARD is found in the environment, use this default:
+BOARD ?= samr21-xpro
+
+TEST_ON_CI_WHITELIST += all
+
+# Select the boards with riotboot feature
+FEATURES_REQUIRED += riotboot
+
+# Include modules to test the bootloader
+USEMODULE += riotboot_slot
+USEMODULE += shell
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+DEVELHELP ?= 1
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+all: riotboot
+
+include ../Makefile.tests_common
+include $(RIOTBASE)/Makefile.include
+
+# Make 'flash' and 'flash-only' work without specific command.
+# This is currently hacky as there is no way of specifiying a FLASHFILE
+all: riotboot/combined-slot0
+# openocd
+ELFFILE = $(BINDIR_APP)-slot0-combined.bin
+# edbg
+HEXFILE = $(BINDIR_APP)-slot0-combined.bin
+# murdock uses ':=' to get the flashfile variable so should also be overwritten
+FLASHFILE = $(BINDIR_APP)-slot0-combined.bin
diff --git a/tests/riotboot/README.md b/tests/riotboot/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c85ed4d5ae8486562f84cdf669221739c9b78e16
--- /dev/null
+++ b/tests/riotboot/README.md
@@ -0,0 +1,21 @@
+RIOT bootloader test
+====================
+
+This is a basic example how to use RIOT bootloader in your embedded
+application.
+
+This test should foremost give you an overview how to use riotboot:
+
+  - `make all` build the test using the target riotboot, which generates
+  a binary file of the application with a header on top of it, used by
+  the bootloader to recognise a bootable image.
+
+  - `make riotboot/flash` creates the binary files and flashes both
+  riotboot and the RIOT image with headers included. This should boot
+  as a normal application.
+
+In this test two modules `riotboot_hdr` and `riotboot_slot` are used to showcase
+the access to riotboot shared functions.
+
+  - `make test` can be executed to run the automatic Python test that checks
+  basic functionalities of riotboot
diff --git a/tests/riotboot/main.c b/tests/riotboot/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..8064b41d2bb57f03b7eaabcadfae22c2dca81f98
--- /dev/null
+++ b/tests/riotboot/main.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 Inria
+ *                    Federico Pellegrin <fede@evolware.org>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     tests
+ * @{
+ *
+ * @file
+ * @brief       riotboot bootloader test
+ *
+ * @author      Francisco Acosta <francisco.acosta@inria.fr>
+ * @author      Federico Pellegrin <fede@evolware.org>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "riotboot/slot.h"
+#include "shell.h"
+
+static int cmd_print_slot_nr(int argc, char **argv)
+{
+    (void)argc;
+    (void)argv;
+
+    printf("Current slot=%d\n", riotboot_slot_current());
+    return 0;
+}
+
+static int cmd_print_slot_hdr(int argc, char **argv)
+{
+    (void)argc;
+    (void)argv;
+
+    int current_slot = riotboot_slot_current();
+    riotboot_slot_print_hdr(current_slot);
+    return 0;
+}
+
+static int cmd_print_slot_addr(int argc, char **argv)
+{
+    (void)argc;
+
+    int reqslot=atoi(argv[1]);
+    printf("Slot %d address=0x%08" PRIx32 "\n",
+           reqslot, riotboot_slot_get_image_startaddr(reqslot));
+    return 0;
+}
+
+static int cmd_dumpaddrs(int argc, char **argv)
+{
+    (void)argc;
+    (void)argv;
+
+    riotboot_slot_dump_addrs();
+    return 0;
+}
+
+static const shell_command_t shell_commands[] = {
+    { "curslotnr", "Print current slot number", cmd_print_slot_nr },
+    { "curslothdr", "Print current slot header", cmd_print_slot_hdr },
+    { "getslotaddr", "Print address of requested slot", cmd_print_slot_addr },
+    { "dumpaddrs", "Prints all slot data in header", cmd_dumpaddrs },
+    { NULL, NULL, NULL }
+};
+
+int main(void)
+{
+    int current_slot;
+
+    puts("Hello riotboot!");
+
+    printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD);
+    printf("This board features a(n) %s MCU.\n", RIOT_MCU);
+
+    /* print some information about the running image */
+    current_slot = riotboot_slot_current();
+    if (current_slot != -1) {
+        printf("riotboot_test: running from slot %d\n", current_slot);
+        riotboot_slot_print_hdr(current_slot);
+    }
+    else {
+        printf("[FAILED] You're not running riotboot\n");
+    }
+
+    /* run the shell */
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
+    return 0;
+}
diff --git a/tests/riotboot/tests/01-run.py b/tests/riotboot/tests/01-run.py
new file mode 100755
index 0000000000000000000000000000000000000000..07ececb93f0320eb88f75b274ecd2712efea8095
--- /dev/null
+++ b/tests/riotboot/tests/01-run.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018 Federico Pellegrin <fede@evolware.org>
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+
+import sys
+from testrunner import run
+
+
+def testfunc(child):
+    # Ask for current slot, should be 0 (riotboot slot)
+    child.sendline("curslotnr")
+    child.expect_exact("Current slot=0")
+    child.expect('>')
+
+    # Ask for current slot header info and checks for basic output integrity
+    child.sendline("curslothdr")
+    # Magic number is "RIOT" (always in little endian)
+    child.expect_exact("Image magic_number: 0x544f4952")
+    # Other info is hardware/app dependant so we just check basic compliance
+    child.expect("Image Version: 0x[0-9a-fA-F]{8}")
+    child.expect("Image start address: 0x[0-9a-fA-F]{8}")
+    child.expect("Header chksum: 0x[0-9a-fA-F]{8}")
+    child.expect('>')
+
+    # Ask for address of slot 0
+    child.sendline("getslotaddr 0")
+    child.expect("Slot 0 address=0x[0-9a-fA-F]{8}")
+    child.expect('>')
+
+    # Ask for data of all slots
+    child.sendline("dumpaddrs")
+    child.expect("slot 0: metadata: 0x[0-9a-fA-F]{1,8} image: 0x[0-9a-fA-F]{8}")
+    child.expect('>')
+
+
+if __name__ == "__main__":
+    sys.exit(run(testfunc))