aboutsummaryrefslogtreecommitdiff
path: root/src/memory
diff options
context:
space:
mode:
authorvetch <vetch97@gmail.com>2020-01-04 19:37:32 +0100
committervetch <vetch97@gmail.com>2020-01-04 19:37:32 +0100
commit615e3302c9dd358bb64cd56d1f3814ad8d5df84d (patch)
tree07b0469807eb3bff7ff7d3f3576858642bc66675 /src/memory
parent885a097da42317f48cead2d91c0e0240066943a8 (diff)
downloadrpi-MMU-example-615e3302c9dd358bb64cd56d1f3814ad8d5df84d.tar.gz
rpi-MMU-example-615e3302c9dd358bb64cd56d1f3814ad8d5df84d.zip
rearranged files, updated makefile
Diffstat (limited to 'src/memory')
-rw-r--r--src/memory/cp_regs.h114
-rw-r--r--src/memory/makefs.c97
-rw-r--r--src/memory/memory.h72
-rw-r--r--src/memory/paging.c249
-rw-r--r--src/memory/paging.h14
-rw-r--r--src/memory/ramfs.c65
-rw-r--r--src/memory/ramfs.h16
-rw-r--r--src/memory/translation_table_descriptors.h180
8 files changed, 807 insertions, 0 deletions
diff --git a/src/memory/cp_regs.h b/src/memory/cp_regs.h
new file mode 100644
index 0000000..e5e7063
--- /dev/null
+++ b/src/memory/cp_regs.h
@@ -0,0 +1,114 @@
+#ifndef CP_REGS_H
+#define CP_REGS_H
+
+#include <stdint.h>
+
+// SCTLR - System Control Register
+
+// Wandering why I didn't typedef this struct with fields?
+// That's because
+typedef union
+{
+ uint32_t raw;
+ struct
+ {
+ uint32_t M : 1; // bit 0
+ uint32_t A : 1; // bit 1
+ uint32_t C : 1; // bit 2
+ uint32_t Bits_4_3 : 2; // bits 3:4
+ uint32_t CP15BEN : 1; // bit 5
+ uint32_t Bit_6 : 1; // bit 6
+ uint32_t B : 1; // bit 7
+ uint32_t Bits_9_8 : 2; // bits 9:8
+ uint32_t SW : 1; // bit 10
+ uint32_t Z : 1; // bit 11
+ uint32_t I : 1; // bit 12
+ uint32_t V : 1; // bit 13
+ uint32_t RR : 1; // bit 14
+ uint32_t Bit_15 : 1; // bit 15
+ uint32_t Bit_16 : 1; // bit 16
+ uint32_t HA : 1; // bit 17
+ uint32_t Bit_18 : 1; // bit 18
+ uint32_t WXN : 1; // bit 19
+ uint32_t UWXN : 1; // bit 20
+ uint32_t FI : 1; // bit 21
+ uint32_t U : 1; // bit 22
+ uint32_t Bit_23 : 1; // bit 23
+ uint32_t VE : 1; // bit 24
+ uint32_t EE : 1; // bit 25
+ uint32_t Bit_26 : 1; // bit 26
+ uint32_t NMFI : 1; // bit 27
+ uint32_t TRE : 1; // bit 28
+ uint32_t AFE : 1; // bit 29
+ uint32_t TE : 1; // bit 30
+ uint32_t Bit_31 : 1; // bit 31
+ } fields;
+} SCTLR_t;
+
+// DACR - Domain Access Control Register
+// DACR holds 16 pairs of bits; each pair represents access
+// permissions to a respective memory domain. There's no point
+// declaring a union for this.
+typedef uint32_t DACR_t;
+
+inline static uint8_t domain_permissions(DACR_t DACR_contents,
+ int domain)
+{
+ return (DACR_contents << (30 - 2 * domain)) >> 30;
+}
+
+inline static DACR_t set_domain_permissions(DACR_t DACR_contents,
+ int domain,
+ uint8_t permissions)
+{
+ uint32_t clear_domain_permissions_mask = ~(0b11 << (2 * domain));
+ uint32_t new_domain_permissions_mask =
+ ((uint32_t) permissions) << (2 * domain);
+
+ return (DACR_contents & clear_domain_permissions_mask)
+ | new_domain_permissions_mask;
+}
+
+#define DOMAIN_NO_ACCESS 0b00
+#define DOMAIN_CLIENT_ACCESS 0b01
+#define DOMAIN_RESERVED 0b10
+#define DOMAIN_MANAGER_ACCESS 0b11
+
+// TTBR - Translation Table Base Register (there're 2 of them with
+// (almost) the same structure)
+
+// A field in TTBCR determines how long the address field is in TTBR0,
+// but here we'll ignore this and just assume the greatest possible
+// length of this field (18 bits). In TTBR1 it's always 18 bits.
+typedef union
+{
+ uint32_t raw;
+ struct
+ {
+
+ uint32_t C : 1; // bit 0
+ uint32_t S : 1; // bit 1
+ uint32_t IMP : 1; // bit 2
+ uint32_t RGN : 2; // bits 4:3
+ uint32_t NOS : 1; // bit 5
+ uint32_t IRGN_0 : 1; // bit 6
+ uint32_t Bits_13_6 : 7; // bits 13:7
+ uint32_t Bits_31_14 : 18; // bits 31:14
+ // with multiprocessing extensions the cacheable bit becomes
+ // upper IRGN bit
+#define IRGN_1 C
+
+ // i'm not sure 'interprocess region bits' is the right name,
+ // I'm just guessing (by analogy to RGN -> region bits)
+#define TTBR_CACHEABLE_BIT C
+#define TTBR_INTERPROCESS_REGION_BITS_1 IRGN_1
+#define TTBR_SHAREABLE_BIT S
+#define TTBR_IMPLEMENTATION_DEFINED_BIT IMP
+#define TTBR_REGION_BITS_1_0 RGN
+#define TTBR_INTERPROCESS_REGION_BITS_0 IRGN_0
+#define TTBR_NON_OUTER_SHAREABLE_BIT NOS
+#define TTBR_TRANSLATION_TABLE_BASE_ADDRESS Bits_31_14
+ } fields;
+} TTBR_t;
+
+#endif // CP_REGS_H
diff --git a/src/memory/makefs.c b/src/memory/makefs.c
new file mode 100644
index 0000000..379e8c5
--- /dev/null
+++ b/src/memory/makefs.c
@@ -0,0 +1,97 @@
+// Take files given on stdin and make them into a ramfs image of our
+// own, (stupid) simple format.
+// In the format: for each file comes the null-terminated string
+// with filename, then null-padding until a 4-aligned offset, then
+// 4-byte little-endian size of the file and then the contents
+// of the file and then another null-padding until a 4-aligned offset.
+// Files encoded this way go one after another (so it's easy to add
+// something at the beginning).
+// At the and comes one null-byte (as if a file with empty name
+// was there).
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#define ANSI_FG_RED "\033[0;31m"
+#define ANSI_FG_DEFAULT "\033[0;39m"
+
+int main(int argc, char **argv)
+{
+ // process files in the order they are provided on the command line
+ for (int i = 1; i < argc; i++)
+ {
+ struct stat fileinfo;
+
+ if (stat(argv[i], &fileinfo))
+ err(-1, "couldn't stat " ANSI_FG_RED "%s" ANSI_FG_DEFAULT,
+ argv[i]);
+
+ if (!S_ISREG(fileinfo.st_mode))
+ errx(-1, ANSI_FG_RED "%s" ANSI_FG_DEFAULT
+ " is not a regular file.", argv[i]);
+
+ // don't allow files with size so big, that it can't be encoded
+ // in a 4-byte unsigned int... In practice even smaller files
+ // won't fit on the rpi.
+ if (fileinfo.st_size > UINT32_MAX)
+ errx(-1, ANSI_FG_RED "%s" ANSI_FG_DEFAULT
+ " is too big.", argv[i]);
+
+ uint32_t file_size = fileinfo.st_size;
+ uint32_t name_size = strlen(argv[i]) + 1; // 1 for null-byte
+
+ if (fwrite(argv[i], 1, name_size, stdout) != name_size)
+ errx(-1, "error writing to stdout");
+
+ // pad with null-bytes until a 4-aligned offset
+ for (uint32_t j = 0; (j + name_size) & 0b11; j++)
+ if (putchar('\0'))
+ errx(-1, "error writing to stdout");
+
+ // TODO convert file_size to little endian first (in case our
+ // host is be).
+ if (fwrite(&file_size, 4, 1, stdout) != 1)
+ errx(-1, "error writing to stdout");
+
+ // flush b4 running cat, so that stuff we've written comes
+ // b4 the actual file contents in the output
+ if (fflush(stdout))
+ err(-1, "couldn't flush stdout");
+
+ // we don't copy the actual file ourselves - we run cat for that
+ pid_t pid;
+ int wstatus;
+ switch (pid = fork())
+ {
+ case -1:
+ err(-1, "couldn't fork");
+ case 0:
+ if (execlp("cat", "cat", argv[i], NULL))
+ err(-1, "couldn't execute cat");
+ default:
+ if (wait(&wstatus) == -1)
+ err(-1, "error waiting for child");
+
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus))
+ exit(-1);
+ }
+
+ // again, pad with null-bytes until a 4-aligned offset
+ for (uint32_t j = 0; (j + file_size) & 0b11; j++)
+ if (putchar('\0'))
+ errx(-1, "error writing to stdout");
+ }
+
+ if (putchar('\0'))
+ errx(-1, "error writing to stdout");
+
+ return 0;
+}
+
diff --git a/src/memory/memory.h b/src/memory/memory.h
new file mode 100644
index 0000000..bdeba52
--- /dev/null
+++ b/src/memory/memory.h
@@ -0,0 +1,72 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <stddef.h>
+
+// These macros were heavily used b4 I moved all the address
+// computation to the linker script. Now I'm just keeping them
+// in case they're needed for something else :)
+#define POWER_OF_2(EXP) (((size_t) 1) << EXP)
+
+#define ALIGN_POWER_OF_2(ADDR, EXP) \
+ (((ADDR - 1) & ~(POWER_OF_2(EXP) - 1)) + POWER_OF_2(EXP))
+
+#define SECTION_SIZE POWER_OF_2(20)
+
+#define ALIGN_SECTION(ADDR) ALIGN_POWER_OF_2(ADDR, 20)
+
+
+// memory layout
+
+#define INTERRUPT_VECTOR_TABLE_START ((uint32_t) 0x0)
+
+// all those symbols are defined in the linker script
+extern char __end;
+extern char __start;
+extern char _translation_table_start;
+extern char _translation_table_end;
+extern char _sections_list_start;
+extern char _sections_list_end;
+extern char _stack_start;
+extern char _fiq_stack_start;
+extern char _fiq_stack_top;
+extern char _irq_stack_start;
+extern char _irq_stack_top;
+extern char _supervisor_stack_start;
+extern char _supervisor_stack_top;
+extern char _stack_end;
+
+#define KERNEL_START ((size_t) &__start) // this is 0x0
+#define KERNEL_END ((size_t) &__end)
+
+// first 2^14 aligned address after the kernel
+#define TRANSLATION_TABLE_BASE ((size_t) &_translation_table_start)
+#define TRANSLATION_TABLE_END ((size_t) &_translation_table_end)
+
+// another 32KB after the translation table are used for sections list
+#define SECTIONS_LIST_START ((size_t) &_sections_list_start)
+#define SECTIONS_LIST_END ((size_t) &_sections_list_end)
+
+// first section after the translation table is left unused;
+// the next section is used as the stack
+#define STACK_START ((size_t) &_stack_start)
+#define FIQ_STACK_START ((size_t) &_fiq_stack_start)
+#define FIQ_STACK_END ((size_t) &_fiq_stack_top)
+#define IRQ_STACK_START ((size_t) &_irq_stack_start)
+#define IRQ_STACK_END ((size_t) &_irq_stack_top)
+#define SUPERVISOR_STACK_START ((size_t) &_supervisor_stack_start)
+#define SUPERVISOR_STACK_END ((size_t) &_supervisor_stack_top)
+#define STACK_END ((size_t) &_stack_end)
+
+#define PRIVILEGED_MEMORY_END STACK_END
+
+
+// the following describes the virtual section for our PL0 programs
+#define PL0_SECTION_NUMBER ((size_t) 0xaaa)
+
+#define VIRTUAL_PL0_MEMORY_START (PL0_SECTION_NUMBER << 20)
+#define VIRTUAL_PL0_MEMORY_END \
+ (VIRTUAL_PL0_MEMORY_START + SECTION_SIZE)
+
+#endif // MEMORY_H
+
diff --git a/src/memory/paging.c b/src/memory/paging.c
new file mode 100644
index 0000000..c883d4e
--- /dev/null
+++ b/src/memory/paging.c
@@ -0,0 +1,249 @@
+#include "cp_regs.h"
+#include "utils/strings.h"
+#include "memory.h"
+#include "translation_table_descriptors.h"
+#include "utils/io.h"
+
+#include "paging.h"
+
+void setup_flat_map(void)
+{
+ // compute translation table base address
+ // translation table shall start at first 2^14-bytes aligned
+ // address after the kernel image
+
+ prints("chosen lvl1 translation table address: 0x");
+ printhex(TRANSLATION_TABLE_BASE);
+ puts("");
+
+ // flat map all memory
+ puts("preparing translation table");
+ short_descriptor_lvl1_t volatile *translation_table =
+ (short_descriptor_lvl1_t*) TRANSLATION_TABLE_BASE;
+
+ for (uint32_t i = 0; i < 4096; i++)
+ translation_table[i].section_fields =
+ (short_section_descriptor_t) {
+ .SECTION_BASE_ADDRESS_31_20 = i,
+ .SECTION_OR_SUPERSECTION_BIT = DESCRIBES_SECTION,
+ .ACCESS_PERMISSIONS_2 = AP_2_0_MODEL_RW_PL1 >> 2,
+ .ACCESS_PERMISSIONS_1_0 = AP_2_0_MODEL_RW_PL1 & 0b011,
+ .DESCRIPTOR_TYPE_1 =
+ SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION >> 1,
+ // rest of fields are 0s
+ };
+
+ // meddle with domain settings
+ puts("setting domain0 to client access and blocking other domains");
+
+ DACR_t DACR = 0;
+ DACR = set_domain_permissions(DACR, 0, DOMAIN_CLIENT_ACCESS);
+ for (int i = 1; i < 16; i++)
+ DACR = set_domain_permissions(DACR, i, DOMAIN_NO_ACCESS);
+
+ // the above should do the same as this:
+ // DACR = 1;
+
+ asm("mcr p15, 0, %0, c3, c0, 0" :: "r" (DACR));
+
+ // meddle with SCTLR, which determines how some bits in
+ // table descriptors work and also controls caches
+ // we don't want to use access flag, so we set AFE to 0
+ // we don't want TEX remap, so we set TRE to 0
+ // we also disable data and instruction caches and the MMU
+
+ // some of this is redundant (i.e. MMU should already be disabled)
+ puts("setting C, I, AFE and TRE to 0 in SCTLR");
+
+ SCTLR_t SCTLR;
+ asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw));
+
+ SCTLR.fields.M = 0; // disable MMU
+ SCTLR.fields.C = 0; // disable data cache
+ SCTLR.fields.I = 0; // disable instruction cache
+ SCTLR.fields.TRE = 0; // disable TEX remap
+ SCTLR.fields.AFE = 0; // disable access flag usage
+ asm("mcr p15, 0, %0, c1, c0, 0\n\r"
+ "isb" :: "r" (SCTLR.raw) : "memory");
+
+ // TODO: move invalidation instructions to some header as inlines
+
+ puts("invalidating instruction cache, branch prediction,"
+ " and entire main TLB");
+
+ // invalidate instruction cache
+ asm("mcr p15, 0, r0, c7, c5, 0\n\r" // r0 gets ignored
+ "isb" ::: "memory");
+
+ // invalidate branch-prediction
+ asm("mcr p15, 0, r0, c7, c5, 6\n\r" // r0 - same as above
+ "isb" ::: "memory");
+
+ // invalidate main Translation Lookup Buffer
+ asm("mcr p15, 0, %0, c8, c7, 0\n\r"
+ "isb" :: "r" (0) : "memory");
+
+ // now set TTBCR to use TTBR0 exclusively
+ puts("Setting TTBCR.N to 0, so that TTBR0 is used everywhere");
+
+ uint32_t TTBCR = 0;
+ asm("mcr p15, 0, %0, c2, c0, 2" :: "r" (TTBCR));
+
+ // Now do stuff with TTBR0
+ TTBR_t TTBR0;
+ TTBR0.raw = 0;
+ TTBR0.fields.TTBR_TRANSLATION_TABLE_BASE_ADDRESS =
+ TRANSLATION_TABLE_BASE >> 14;
+ // rest of TTBR0 remains 0s
+
+ asm("mcr p15, 0, %0, c2, c0, 0" :: "r" (TTBR0.raw));
+
+ // enable MMU
+ puts("enabling the MMU");
+
+ // redundant - we already have SCTLR contents in the variable
+ // asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (SCTLR.raw));
+
+ SCTLR.fields.M = 1;
+
+ asm("mcr p15, 0, %0, c1, c0, 0\n\r"
+ "isb" :: "r" (SCTLR.raw) : "memory");
+}
+
+#define OWNER_FREE ((void*) 0)
+#define OWNER_KERNEL ((void*) 1)
+#define OWNER_SPLIT ((void*) 2)
+
+// we want to maintain a list of free and used physical sections
+struct section_node
+{
+ // we're going to add processes, process management and
+ // struct process. Then, owner will be struct process*.
+ void *owner; // 0 if free, 1 if used by kernel, 2 if split to pages
+
+ // it's actually a 2-directional lists;
+ // end of list is marked by reference to SECTION_NULL;
+ // we use offsets into sections_list array instead of pointers;
+ uint16_t prev, next;
+};
+
+static struct section_node volatile *sections_list;
+
+static uint16_t
+ all_sections_count, kernel_sections_count,
+ split_sections_count, free_sections_count;
+
+// those are SECTION_NULL when the corresponding count is 0;
+static uint16_t
+ first_free_section, first_kernel_section, first_split_section;
+
+void setup_pager_structures(uint32_t available_mem)
+{
+ all_sections_count = available_mem / SECTION_SIZE;
+ kernel_sections_count = PRIVILEGED_MEMORY_END / SECTION_SIZE;
+ free_sections_count = all_sections_count - kernel_sections_count;
+ split_sections_count = 0;
+
+ sections_list = (struct section_node*) SECTIONS_LIST_START;
+
+ first_split_section = SECTION_NULL;
+
+ for (uint16_t i = 0; i < kernel_sections_count; i++)
+ sections_list[i] = (struct section_node) {
+ .owner = OWNER_KERNEL,
+ .prev = i == 0 ? SECTION_NULL : i - 1,
+ .next = i == kernel_sections_count - 1 ? SECTION_NULL : i + 1
+ };
+
+ first_kernel_section = 0;
+
+ for (uint16_t i = kernel_sections_count;
+ i < all_sections_count; i++)
+ sections_list[i] = (struct section_node) {
+ .owner = OWNER_FREE,
+ .prev = i == kernel_sections_count ? SECTION_NULL : i - 1,
+ .next = i == all_sections_count - 1 ? SECTION_NULL : i + 1
+ };
+
+ first_free_section = kernel_sections_count;
+
+ puts("Initialized kernel's internal structures for paging");
+ prints("We have "); printdect(free_sections_count);
+ puts(" free sections left for use");
+}
+
+// return section number or SECTION_NULL in case of failure
+static uint16_t claim_section(void *owner)
+{
+ if (!free_sections_count)
+ return SECTION_NULL; // failure
+
+ uint16_t section = first_free_section;
+
+ if (--free_sections_count)
+ {
+ uint16_t next;
+
+ next = sections_list[section].next;
+ sections_list[next].prev = SECTION_NULL;
+
+ first_free_section = next;
+ }
+ else
+ first_free_section = SECTION_NULL;
+
+ if (owner == OWNER_KERNEL)
+ {
+ sections_list[first_kernel_section].prev = section;
+
+ sections_list[section] = (struct section_node) {
+ .owner = owner,
+ .prev = SECTION_NULL,
+ .next = first_kernel_section
+ };
+
+ kernel_sections_count++;
+
+ first_kernel_section = section;
+ }
+ else
+ sections_list[section] = (struct section_node) {
+ .owner = owner,
+ .prev = SECTION_NULL,
+ .next = SECTION_NULL
+ };
+
+ return section;
+}
+
+// return values like claim_section()
+uint16_t claim_and_map_section
+(void *owner, uint16_t where_to_map, uint8_t access_permissions)
+{
+ uint16_t section = claim_section(owner);
+
+ if (section == SECTION_NULL)
+ return section;
+
+ short_section_descriptor_t volatile *section_entry =
+ &((short_section_descriptor_t*)
+ TRANSLATION_TABLE_BASE)[where_to_map];
+
+ short_section_descriptor_t descriptor = *section_entry;
+
+ // set up address of section
+ descriptor.SECTION_BASE_ADDRESS_31_20 = section;
+
+ // set requested permissions on section
+ descriptor.ACCESS_PERMISSIONS_2 = access_permissions >> 2;
+ descriptor.ACCESS_PERMISSIONS_1_0 = access_permissions & 0b011;
+
+ // write modified descriptor to the table
+ *section_entry = descriptor;
+
+ // invalidate main Translation Lookup Buffer
+ asm("mcr p15, 0, r1, c8, c7, 0\n\r"
+ "isb" ::: "memory");
+
+ return section;
+}
diff --git a/src/memory/paging.h b/src/memory/paging.h
new file mode 100644
index 0000000..4ac8efa
--- /dev/null
+++ b/src/memory/paging.h
@@ -0,0 +1,14 @@
+#ifndef PAGING_H
+#define PAGING_H
+
+void setup_flat_map(void);
+
+void setup_pager_structures(uint32_t available_mem);
+
+#define SECTION_NULL 0xffff
+
+// returns section number or SECTION_NULL in case of failure
+uint16_t claim_and_map_section
+(void *owner, uint16_t where_to_map, uint8_t access_permissions);
+
+#endif // PAGING_H
diff --git a/src/memory/ramfs.c b/src/memory/ramfs.c
new file mode 100644
index 0000000..cc66b4c
--- /dev/null
+++ b/src/memory/ramfs.c
@@ -0,0 +1,65 @@
+// driver for the read-only ramfs
+// see makefs.c for details
+
+#include <stdint.h>
+#include "ramfs.h"
+
+static int strcmp(char const *str1, char const *str2)
+{
+ while (1)
+ {
+ int c1 = (unsigned char) *str1, c2 = (unsigned char) *str2;
+
+ if (!c1 && !c2)
+ return 0;
+
+ if (c1 != c2)
+ return c1 - c2;
+
+ str1++; str2++;
+ }
+}
+
+static uint32_t strlen(char const *str1)
+{
+ uint32_t len = 0;
+
+ while (str1[len])
+ len++;
+
+ return len;
+}
+
+static inline char *align4(char *addr)
+{
+ return (char*) (((uint32_t) addr - 1) & ~0b11) + 4;
+}
+
+int find_file(void *ramfs, char *filename, struct ramfile *buf)
+{
+ char *fs_file = ramfs;
+
+ while (*fs_file)
+ {
+ uint32_t *fs_file_size = (uint32_t*)
+ align4(fs_file + strlen(fs_file) + 1);
+
+ char *fs_file_contents = (char*) (fs_file_size + 1);
+
+ if (!strcmp(fs_file, filename))
+ {
+ buf->file_size = *fs_file_size;
+
+ buf->file_name = fs_file;
+
+ buf->file_contents = fs_file_contents;
+
+ return 0;
+ }
+
+ // move to the next file in ramfs
+ fs_file = align4(fs_file_contents + *fs_file_size);
+ }
+
+ return -1; // reached end of ramfs; file not found
+}
diff --git a/src/memory/ramfs.h b/src/memory/ramfs.h
new file mode 100644
index 0000000..cf45736
--- /dev/null
+++ b/src/memory/ramfs.h
@@ -0,0 +1,16 @@
+#ifndef RAMFS_H
+#define RAMFS_H
+
+struct ramfile
+{
+ char *file_name;
+ uint32_t file_size;
+ char *file_contents;
+};
+
+// search for file named filename in ramfs;
+// If found - return 0 and fill buf fields with file's info.
+// Otherwise return a non-zero value.
+int find_file(void *ramfs, char *filename, struct ramfile *buf);
+
+#endif // RAMFS_H
diff --git a/src/memory/translation_table_descriptors.h b/src/memory/translation_table_descriptors.h
new file mode 100644
index 0000000..981c3c7
--- /dev/null
+++ b/src/memory/translation_table_descriptors.h
@@ -0,0 +1,180 @@
+#ifndef TRANSLATION_TABLE_DESCRIPTORS_H
+#define TRANSLATION_TABLE_DESCRIPTORS_H
+
+#include <stdint.h>
+
+// ARM lets you choose between 32-bit abd 64-bit translation table
+// descriptors (called short and long descriptors respectively).
+// The format of the descriptor differs depending on what it describes
+// (section, supersection, a page table, etc...) and table of which
+// level of lookup it belongs to.
+
+// Even in case of descriptor of a specified type (e.g. short-format
+// section descriptor), a given field inside it may have different
+// meanings depending on settings in coprocessor registers... (yeah, ARM
+// looks a bit messy... all for backward compatibility, i guess)
+
+
+////// Here are the definitions for short-format descriptors
+
+//// short-format page table descriptor
+
+typedef struct
+{
+ uint32_t Bits_1_0 : 1; // bits 1:0
+ uint32_t PXN : 1; // bit 2
+ uint32_t NS : 1; // bit 3
+ uint32_t SBZ : 1; // bit 4
+ uint32_t Domain_3_0 : 4; // bits 8:5
+ uint32_t Bit_9 : 1; // bit 9
+ uint32_t Bits_31_10 : 22; // bits 31:10
+#define DESCRIPTOR_TYPE_1_0 Bits_1_0
+#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
+#define NON_SECURE_BIT NS
+ // me thinks SBZ means "should be zero",
+ // but me sees no point #defining it
+#define DOMAIN_3_0 Domain_3_0
+#define IMPLEMENTATION_DEFINED_BIT Bit_9
+#define PAGE_TABLE_BASE_ADDRESS_31_10 Bits_31_10
+} short_page_table_descriptor_t;
+
+
+//// short-format section descriptor
+
+typedef struct
+{
+ uint32_t PXN : 1; // bit 0
+ uint32_t Bit_1 : 1; // bit 1
+ uint32_t B : 1; // bit 2
+ uint32_t C : 1; // bit 3
+ uint32_t XN : 1; // bit 4
+ uint32_t Domain_3_0 : 4; // bits 8:5
+ uint32_t Bit_9 : 1; // bit 9
+ uint32_t AP_1_0 : 2; // bit 11:10
+ uint32_t TEX_2_0 : 3; // bits 14:12
+ uint32_t AP_2 : 1; // bit 15
+ uint32_t S : 1; // bit 16
+ uint32_t nG : 1; // bit 17
+ uint32_t Bit_18 : 1; // bit 18
+ uint32_t NS : 1; // bit 19
+ uint32_t PA_31_20 : 12; // bits 31:20
+ // some of these are already defined the same for page table
+ //#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
+#define DESCRIPTOR_TYPE_1 Bit_1
+#define BUFFERABLE_BIT B
+#define CACHEABLE_BIT C
+#define EXECUTE_NEVER_BIT XN
+ //#define DOMAIN_3_0 Domain_3_0
+ //#define IMPLEMENTATION_DEFINED_BIT Bit_9
+#define ACCESS_PERMISSIONS_1_0 AP_1_0
+#define TYPE_EXTENSION_2_0 TEX_2_0
+#define ACCESS_PERMISSIONS_2 AP_2
+#define SHAREABLE_BIT S
+#define NON_GLOBAL_BIT nG
+#define SECTION_OR_SUPERSECTION_BIT Bit_18
+ //#define NON_SECURE_BIT NS
+#define SECTION_BASE_ADDRESS_31_20 PA_31_20
+} short_section_descriptor_t;
+
+
+//// short-format supersection descriptor
+
+typedef struct
+{
+ uint32_t PXN : 1; // bit 0
+ uint32_t Bit_1 : 1; // bit 1
+ uint32_t B : 1; // bit 2
+ uint32_t C : 1; // bit 3
+ uint32_t XN : 1; // bit 4
+ uint32_t PA_39_36 : 4; // bits 8:5
+ uint32_t Bit_9 : 1; // bit 9
+ uint32_t AP_1_0 : 2; // bit 11:10
+ uint32_t TEX_2_0 : 3; // bits 14:12
+ uint32_t AP_2 : 1; // bit 15
+ uint32_t S : 1; // bit 16
+ uint32_t nG : 1; // bit 17
+ uint32_t Bit_18 : 1; // bit 18
+ uint32_t NS : 1; // bit 19
+ uint32_t PA_35_32 : 4; // bits 23:20
+ uint32_t PA_31_24 : 8; // bits 31:24
+ // most of these are already defined the same for section
+ //#define PRIVILEGED_EXECUTE_NEVER_BIT PXN
+ //#define DESCRIPTOR_TYPE_1 Bit_1
+ //#define BUFFERABLE_BIT B
+ //#define CACHEABLE_BIT C
+ //#define EXECUTE_NEVER_BIT XN
+#define SUPERSECTION_BASE_ADDRESS_39_36 PA_39_36
+ //#define IMPLEMENTATION_DEFINED_BIT Bit_9
+ //#define ACCESS_PERMISSIONS_1_0 AP_1_0
+ //#define TYPE_EXTENSION_2_0 TEX_2_0
+ //#define ACCESS_PERMISSIONS_2 AP_2
+ //#define SHAREABLE_BIT S
+ //#define NON_GLOBAL_BIT nG
+ //#define SECTION_OR_SUPERSECTION_BIT Bit_18
+ //#define NON_SECURE_BIT NS
+#define SUPERSECTION_BASE_ADDRESS_35_32 PA_35_32
+#define SUPERSECTION_BASE_ADDRESS_31_24 PA_31_24
+} short_supersection_descriptor_t;
+
+
+//// possible access permission field values
+
+// How AP[2:0] is used depends on settings in SCTLR.AFE
+
+// Meaning of #define'd names below:
+// RW - read-write
+// RO - read-only
+// PL1 - a given permission applies to privilege level PL1
+// PL2 - a given permission applies to privilege level PL2
+// ALL - a given permission applies to both privilege levels
+// If only a permission for one privilege level is given in the name,
+// it means the other one has no access.
+
+// When SCTLR.AFE is 0 (access flag not used) and short-format
+// descritor table is used, the following access permission control
+// schema for AP[2:0] is used:
+#define AP_2_0_MODEL_NO_ACCESS 0b000
+#define AP_2_0_MODEL_RW_PL1 0b001
+#define AP_2_0_MODEL_RW_PL1_RO_PL0 0b010
+#define AP_2_0_MODEL_RW_ALL 0b011
+#define AP_2_0_MODEL_RESERVED 0b100
+#define AP_2_0_MODEL_RO_PL1 0b101
+#define AP_2_0_MODEL_RO_ALL_DEPRECATED 0b110 // use 0b111 instead
+#define AP_2_0_MODEL_RO_ALL 0b111 // reserved in VMSAv6
+// TODO: the #define's of RO_ALL and reserved could be done
+// conditionally depending on the VMSA version available (either give
+// the programmer #including this the possibility to #define their
+// VMSA version or assume the VMSA version respective to the ARM
+// version we're compiling against)
+
+
+//// Values for bit18, that determines whether a descriptor describes
+// section or supersection:
+#define DESCRIBES_SECTION 0b0
+#define DESCRIBES_SUPERSECTION 0b1
+
+
+//// short-format descriptor generic type
+
+typedef union
+{
+ uint32_t raw;
+ uint8_t descriptor_type : 2;
+
+ short_page_table_descriptor_t page_table_fields;
+ short_section_descriptor_t section_fields;
+ short_supersection_descriptor_t supersection_fields;
+} short_descriptor_lvl1_t;
+
+
+//// possible values of descriptor_type field:
+
+#define SHORT_DESCRIPTOR_INVALID 0b00
+#define SHORT_DESCRIPTOR_PAGE_TABLE 0b01
+#define SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION 0b10
+#define SHORT_DESCRIPTOR_SECTION_OR_SUPERSECTION_PXN 0b11
+// on an implementation that does not support the PXN attribute
+// 0b11 should not be used
+#define SHORT_DESCRIPTOR_RESERVED 0b11
+
+#endif // TRANSLATION_TABLE_DESCRIPTORS_H