diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 3d86691ae0..179696ec91 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -314,6 +314,7 @@ config STM32F205_SOC
 config STM32F405_SOC
     bool
     select ARM_V7M
+    select STM32F4XX_FLASHIF
     select STM32F4XX_SYSCFG
     select STM32F4XX_EXTI
 
diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c
index 9bcad97853..72b16910c7 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -30,6 +30,7 @@
 #include "hw/arm/stm32f405_soc.h"
 #include "hw/misc/unimp.h"
 
+#define FLASHIF_ADDR                   0x40023C00
 #define SYSCFG_ADD                     0x40013800
 static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800,
                                        0x40004C00, 0x40005000, 0x40011400,
@@ -59,6 +60,9 @@ static void stm32f405_soc_initfn(Object *obj)
     sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m),
                           TYPE_ARMV7M);
 
+    sysbus_init_child_obj(obj, "flashif", &s->flashif, sizeof(s->flashif),
+                          TYPE_STM32F4XX_FLASHIF);
+
     sysbus_init_child_obj(obj, "syscfg", &s->syscfg, sizeof(s->syscfg),
                           TYPE_STM32F4XX_SYSCFG);
 
@@ -130,6 +134,16 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
         return;
     }
 
+    /* Flash interface */
+    dev = DEVICE(&s->flashif);
+    object_property_set_bool(OBJECT(&s->flashif), true, "realized", &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    busdev = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(busdev, 0, FLASHIF_ADDR);
+
     /* System configuration controller */
     dev = DEVICE(&s->syscfg);
     object_property_set_bool(OBJECT(&s->syscfg), true, "realized", &err);
@@ -261,7 +275,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("GPIOI",       0x40022000, 0x400);
     create_unimplemented_device("CRC",         0x40023000, 0x400);
     create_unimplemented_device("RCC",         0x40023800, 0x400);
-    create_unimplemented_device("Flash Int",   0x40023C00, 0x400);
     create_unimplemented_device("BKPSRAM",     0x40024000, 0x400);
     create_unimplemented_device("DMA1",        0x40026000, 0x400);
     create_unimplemented_device("DMA2",        0x40026400, 0x400);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index bdd77d8020..e333a955b2 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -79,6 +79,9 @@ config IMX
     select SSI
     select USB_EHCI_SYSBUS
 
+config STM32F4XX_FLASHIF
+    bool
+
 config STM32F2XX_SYSCFG
     bool
 
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index da993f45b7..1aea0b9bb9 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -58,6 +58,7 @@ common-obj-$(CONFIG_RASPI) += bcm2835_thermal.o
 common-obj-$(CONFIG_SLAVIO) += slavio_misc.o
 common-obj-$(CONFIG_ZYNQ) += zynq_slcr.o
 common-obj-$(CONFIG_ZYNQ) += zynq-xadc.o
+common-obj-$(CONFIG_STM32F4XX_FLASHIF) += stm32f4xx_flashif.o
 common-obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
 common-obj-$(CONFIG_STM32F4XX_SYSCFG) += stm32f4xx_syscfg.o
 common-obj-$(CONFIG_STM32F4XX_EXTI) += stm32f4xx_exti.o
diff --git a/hw/misc/stm32f4xx_flashif.c b/hw/misc/stm32f4xx_flashif.c
new file mode 100644
index 0000000000..d280fc5b2c
--- /dev/null
+++ b/hw/misc/stm32f4xx_flashif.c
@@ -0,0 +1,215 @@
+/*
+ * STM32F4xx FLASHIF
+ *
+ * Copyright (c) 2020 Stephanos Ioannidis <root@stephanos.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/stm32f4xx_flashif.h"
+
+static void stm32f4xx_flashif_reset(DeviceState *dev)
+{
+    STM32F4xxFlashIfState *s = STM32F4XX_FLASHIF(dev);
+
+    /* Initialise states */
+    s->cr_key_index = 0;
+    s->optcr_key_index = 0;
+
+    /* Initialise register values */
+    s->flash_acr.reg = 0x00000000;
+    s->flash_keyr.reg = 0x00000000;
+    s->flash_optkeyr.reg = 0x00000000;
+    s->flash_sr.reg = 0x00000000;
+    s->flash_cr.reg = 0x80000000;
+    s->flash_optcr.reg = 0x0FFFAAED;
+}
+
+static uint64_t stm32f4xx_flashif_read(void * opaque, hwaddr addr,
+                                       unsigned int size)
+{
+    STM32F4xxFlashIfState *s = opaque;
+
+    trace_stm32f4xx_flashif_read(addr);
+
+    switch (addr) {
+    case FLASH_ACR:
+        return s->flash_acr.reg;
+    case FLASH_SR:
+        return s->flash_sr.reg;
+    case FLASH_CR:
+        return s->flash_cr.reg;
+    case FLASH_OPTCR:
+        return s->flash_optcr.reg;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+        return 0;
+    }
+}
+
+static void flash_acr_write(STM32F4xxFlashIfState *s, FlashAcrType val)
+{
+    /* Set latency */
+    s->flash_acr.latency = val.latency;
+    /* Set prefetch status */
+    s->flash_acr.prften = val.prften;
+    /* Set instruction cache status */
+    s->flash_acr.icen = val.icen;
+    /* Set data cache status */
+    s->flash_acr.dcen = val.dcen;
+}
+
+static void flash_cr_write(STM32F4xxFlashIfState *s, FlashCrType val)
+{
+    /* Lock FLASH_CR if lock bit is set */
+    if (val.lock) {
+        s->flash_cr.lock = 1;
+        s->cr_key_index = 0;
+        return;
+    }
+}
+
+static void flash_optcr_write(STM32F4xxFlashIfState *s, FlashOptcrType val)
+{
+    /* Lock FLASH_OPTCR if lock bit is set */
+    if (val.optlock) {
+        s->flash_optcr.optlock = 1;
+        s->optcr_key_index = 0;
+        return;
+    }
+}
+
+static void stm32f4xx_flashif_write(void *opaque, hwaddr addr,
+                                    uint64_t val64, unsigned int size)
+{
+    STM32F4xxFlashIfState *s = opaque;
+    uint32_t value = val64;
+
+    trace_stm32f4xx_flashif_write(value, addr);
+
+    switch (addr) {
+    case FLASH_ACR:
+        flash_acr_write(s, (FlashAcrType)value);
+        return;
+    case FLASH_KEYR:
+        if (s->cr_key_index == 0 && value == 0x45670123) {
+            s->cr_key_index = 1;
+        } else if (s->cr_key_index == 1 && value == 0xCDEF89AB) {
+            /* Valid key; unlock FLASH_CR */
+            s->flash_cr.lock = 0;
+            s->cr_key_index = 0;
+        } else {
+            /* Invalid key; permanently lock FLASH_CR until reset */
+            s->flash_cr.lock = 1;
+            s->cr_key_index = -1;
+        }
+        return;
+    case FLASH_OPTKEYR:
+        if (s->optcr_key_index == 0 && value == 0x08192A3B) {
+            s->optcr_key_index = 1;
+        } else if (s->optcr_key_index == 1 && value == 0x4C5D6E7F) {
+            /* Valid key; unlock FLASH_OPTCR */
+            s->flash_optcr.optlock = 0;
+            s->optcr_key_index = 0;
+        } else {
+            /* Invalid key; lock FLASH_OPTCR until reset */
+            s->flash_optcr.optlock = 1;
+            s->optcr_key_index = -1;
+        }
+        return;
+    case FLASH_SR:
+        if (s->flash_sr.eop)        s->flash_sr.eop = 0;
+        if (s->flash_sr.operr)      s->flash_sr.operr = 0;
+        if (s->flash_sr.wrperr)     s->flash_sr.wrperr = 0;
+        if (s->flash_sr.pgaerr)     s->flash_sr.pgaerr = 0;
+        if (s->flash_sr.pgperr)     s->flash_sr.pgperr = 0;
+        if (s->flash_sr.pgserr)     s->flash_sr.pgserr = 0;
+        return;
+    case FLASH_CR:
+        flash_cr_write(s, (FlashCrType)value);
+        return;
+    case FLASH_OPTCR:
+        flash_optcr_write(s, (FlashOptcrType)value);
+        return;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+    }
+}
+
+static const MemoryRegionOps stm32f4xx_flashif_ops = {
+    .read = stm32f4xx_flashif_read,
+    .write = stm32f4xx_flashif_write,
+    .endianness = DEVICE_NATIVE_ENDIAN
+};
+
+static void stm32f4xx_flashif_init(Object *obj)
+{
+    STM32F4xxFlashIfState *s = STM32F4XX_FLASHIF(obj);
+
+    memory_region_init_io(&s->mmio, obj, &stm32f4xx_flashif_ops, s,
+                          TYPE_STM32F4XX_FLASHIF, 0x400);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const VMStateDescription vmstate_stm32f4xx_flashif = {
+    .name = TYPE_STM32F4XX_FLASHIF,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(flash_acr.reg, STM32F4xxFlashIfState),
+        VMSTATE_UINT32(flash_keyr.reg, STM32F4xxFlashIfState),
+        VMSTATE_UINT32(flash_optkeyr.reg, STM32F4xxFlashIfState),
+        VMSTATE_UINT32(flash_sr.reg, STM32F4xxFlashIfState),
+        VMSTATE_UINT32(flash_cr.reg, STM32F4xxFlashIfState),
+        VMSTATE_UINT32(flash_optcr.reg, STM32F4xxFlashIfState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void stm32f4xx_flashif_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = stm32f4xx_flashif_reset;
+    dc->vmsd = &vmstate_stm32f4xx_flashif;
+}
+
+static const TypeInfo stm32f4xx_flashif_info = {
+    .name          = TYPE_STM32F4XX_FLASHIF,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(STM32F4xxFlashIfState),
+    .instance_init = stm32f4xx_flashif_init,
+    .class_init    = stm32f4xx_flashif_class_init
+};
+
+static void stm32f4xx_flashif_register_types(void)
+{
+    type_register_static(&stm32f4xx_flashif_info);
+}
+
+type_init(stm32f4xx_flashif_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 7f0f5dff3a..bd9a5dd483 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -84,6 +84,10 @@ mos6522_set_sr_int(void) "set sr_int"
 mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
 
+# stm32f4xx_flashif
+stm32f4xx_flashif_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " "
+stm32f4xx_flashif_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
+
 # stm32f4xx_syscfg
 stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
 stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h
index 1fe97f8c3a..a94c3c51ac 100644
--- a/include/hw/arm/stm32f405_soc.h
+++ b/include/hw/arm/stm32f405_soc.h
@@ -25,6 +25,7 @@
 #ifndef HW_ARM_STM32F405_SOC_H
 #define HW_ARM_STM32F405_SOC_H
 
+#include "hw/misc/stm32f4xx_flashif.h"
 #include "hw/misc/stm32f4xx_syscfg.h"
 #include "hw/timer/stm32f2xx_timer.h"
 #include "hw/char/stm32f2xx_usart.h"
@@ -57,6 +58,7 @@ typedef struct STM32F405State {
 
     ARMv7MState armv7m;
 
+    STM32F4xxFlashIfState flashif;
     STM32F4xxSyscfgState syscfg;
     STM32F4xxExtiState exti;
     STM32F2XXUsartState usart[STM_NUM_USARTS];
diff --git a/include/hw/misc/stm32f4xx_flashif.h b/include/hw/misc/stm32f4xx_flashif.h
new file mode 100644
index 0000000000..e364ca39c7
--- /dev/null
+++ b/include/hw/misc/stm32f4xx_flashif.h
@@ -0,0 +1,144 @@
+/*
+ * STM32F4xx FLASHIF
+ *
+ * Copyright (c) 2020 Stephanos Ioannidis <root@stephanos.io>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_STM_FLASHIF_H
+#define HW_STM_FLASHIF_H
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+
+#define TYPE_STM32F4XX_FLASHIF "stm32f4xx-flashif"
+#define STM32F4XX_FLASHIF(obj) \
+    OBJECT_CHECK(STM32F4xxFlashIfState, (obj), TYPE_STM32F4XX_FLASHIF)
+
+#define FLASH_ACR       0x00
+#define FLASH_KEYR      0x04
+#define FLASH_OPTKEYR   0x08
+#define FLASH_SR        0x0C
+#define FLASH_CR        0x10
+#define FLASH_OPTCR     0x14
+
+typedef union {
+    struct {
+        uint32_t latency : 3;
+        uint32_t reserved0 : 5;
+        uint32_t prften : 1;
+        uint32_t icen : 1;
+        uint32_t dcen : 1;
+        uint32_t icrst : 1;
+        uint32_t dcrst : 1;
+        uint32_t reserved1 : 19;
+    };
+    uint32_t reg;
+} FlashAcrType;
+
+typedef union {
+    struct {
+        uint32_t key : 32;
+    };
+    uint32_t reg;
+} FlashKeyrType;
+
+typedef union {
+    struct {
+        uint32_t optkey : 32;
+    };
+    uint32_t reg;
+} FlashOptkeyrType;
+
+typedef union {
+    struct {
+        uint32_t eop : 1;
+        uint32_t operr : 1;
+        uint32_t reserved0 : 2;
+        uint32_t wrperr : 1;
+        uint32_t pgaerr : 1;
+        uint32_t pgperr : 1;
+        uint32_t pgserr : 1;
+        uint32_t reserved1 : 8;
+        uint32_t bsy : 1;
+        uint32_t reserved2 : 15;
+    };
+    uint32_t reg;
+} FlashSrType;
+
+typedef union {
+    struct {
+        uint32_t pg : 1;
+        uint32_t ser : 1;
+        uint32_t mer : 1;
+        uint32_t snb : 4;
+        uint32_t reserved0 : 1;
+        uint32_t psize : 2;
+        uint32_t reserved1 : 6;
+        uint32_t strt : 1;
+        uint32_t reserved2 : 7;
+        uint32_t eopie : 1;
+        uint32_t reserved3 : 6;
+        uint32_t lock : 1;
+    };
+    uint32_t reg;
+} FlashCrType;
+
+typedef union {
+    struct {
+        uint32_t optlock : 1;
+        uint32_t optstrt : 1;
+        uint32_t bor_lev : 2;
+        uint32_t reserved0 : 1;
+        uint32_t wdg_sw : 1;
+        uint32_t nrst_stop : 1;
+        uint32_t nrst_stdby : 1;
+        uint32_t rdp : 8;
+        uint32_t nwrp : 12;
+        uint32_t reserved1 : 4;
+    };
+    uint32_t reg;
+} FlashOptcrType;
+
+typedef struct {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion mmio;
+
+    int32_t cr_key_index;
+    int32_t optcr_key_index;
+
+    /* Access control register (FLASH_ACR) */
+    FlashAcrType flash_acr;
+    /* Key register (FLASH_KEYR) */
+    FlashKeyrType flash_keyr;
+    /* Option key register (FLASH_OPTKEYR) */
+    FlashOptkeyrType flash_optkeyr;
+    /* Status register (FLASH_SR) */
+    FlashSrType flash_sr;
+    /* Control register (FLASH_CR) */
+    FlashCrType flash_cr;
+    /* Option control register (FLASH_OPTCR) */
+    FlashOptcrType flash_optcr;
+} STM32F4xxFlashIfState;
+
+#endif
