libsel4platsupport: Add IO FDT ops implementation

This commit adds an implementation of the IO FDT ops interface in
libplatsupport.
diff --git a/libsel4platsupport/include/sel4platsupport/io.h b/libsel4platsupport/include/sel4platsupport/io.h
index 9da11b2..f6d4986 100644
--- a/libsel4platsupport/include/sel4platsupport/io.h
+++ b/libsel4platsupport/include/sel4platsupport/io.h
@@ -42,6 +42,16 @@
 int sel4platsupport_new_malloc_ops(ps_malloc_ops_t *ops);
 
 /**
+ * Create a new FDT ops structure using a provided simple
+ *
+ * @param io_fdt Interface to fill in
+ * @param simple An initialised simple interface
+ *
+ * @return returns 0 on success
+ */
+int sel4platsupport_new_fdt_ops(ps_io_fdt_t *io_fdt, simple_t *simple, ps_malloc_ops_t *malloc_ops);
+
+/**
  * Creates a new implementation of the platsupport io_ops interface using a
  * provided vspace and vka
  *
diff --git a/libsel4platsupport/src/io.c b/libsel4platsupport/src/io.c
index 90a78e4..500bf16 100644
--- a/libsel4platsupport/src/io.c
+++ b/libsel4platsupport/src/io.c
@@ -269,6 +269,51 @@
     return 0;
 }
 
+static char *sel4platsupport_io_fdt_get(void *cookie)
+{
+    return cookie != NULL ? (char *) cookie : NULL;
+}
+
+int sel4platsupport_new_fdt_ops(ps_io_fdt_t *io_fdt, simple_t *simple, ps_malloc_ops_t *malloc_ops)
+{
+    if (!io_fdt || !simple || !malloc_ops) {
+        ZF_LOGE("arguments are NULL");
+        return -1;
+    }
+
+    ssize_t block_size = simple_get_extended_bootinfo_length(simple, SEL4_BOOTINFO_HEADER_FDT);
+
+    int error = ps_calloc(malloc_ops, 1, block_size, &io_fdt->cookie);
+    if (error) {
+        ZF_LOGE("Failed to allocate %zu bytes for the FDT", block_size);
+        return -1;
+    }
+
+    /* Copy the FDT from the extended bootinfo */
+    ssize_t copied_size = simple_get_extended_bootinfo(simple, SEL4_BOOTINFO_HEADER_FDT,
+                                                       io_fdt->cookie, block_size);
+    if (copied_size != block_size) {
+        ZF_LOGE("Failed to copy the FDT");
+        ZF_LOGF_IF(ps_free(malloc_ops, block_size, io_fdt->cookie),
+                   "Failed to clean-up after a failed operation!");
+        return -1;
+    }
+
+    /* Cut off the bootinfo header from the start of the buffer */
+    ssize_t fdt_size = block_size - sizeof(seL4_BootInfoHeader);
+    void *fdt_start = io_fdt->cookie + sizeof(seL4_BootInfoHeader);
+    memmove(io_fdt->cookie, fdt_start, fdt_size);
+
+    /* Trim off the extra bytes at the end of the FDT */
+    void *fdt_end = io_fdt->cookie + fdt_size;
+    memset(fdt_end, 0, sizeof(seL4_BootInfoHeader));
+
+    /* Set the function pointer inside the io_fdt interface */
+    io_fdt->get_fn = sel4platsupport_io_fdt_get;
+
+    return 0;
+}
+
 #ifdef CONFIG_PLAT_TK1
 #include <platsupport/gpio.h>