diff --git a/arch/bsp/.gitignore b/arch/bsp/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/arch/bsp/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/arch/bsp/yellow_led.c b/arch/bsp/yellow_led.c new file mode 100644 index 0000000..5895a39 --- /dev/null +++ b/arch/bsp/yellow_led.c @@ -0,0 +1,42 @@ +#include +#include + +static constexpr unsigned int GPIO_BASE = 0x7E200000 - 0x3F000000; +static constexpr unsigned int YELLOW_LED = 7u; +static constexpr unsigned int GPF_BITS = 3u; +static constexpr unsigned int GPF_MASK = 0b111u; +static constexpr unsigned int YELLOW_LED_GPF_SHIFT = YELLOW_LED * GPF_BITS; + +enum gpio_func { + gpio_input = 0x0, + gpio_output = 0x1, +}; + +struct gpio { + unsigned int func[6]; + unsigned int unused0; + unsigned int set[2]; + unsigned int unused1; + unsigned int clr[2]; +}; +static_assert(offsetof(struct gpio, func) == 0x00); +static_assert(offsetof(struct gpio, set) == 0x1C); +static_assert(offsetof(struct gpio, clr) == 0x28); + +static volatile struct gpio *const gpio_port = (struct gpio *)GPIO_BASE; + +void init_yellow(void) +{ + gpio_port->func[0] = (gpio_port->func[0] & ~(GPF_MASK << YELLOW_LED_GPF_SHIFT)) | + (gpio_output << YELLOW_LED_GPF_SHIFT); +} + +void yellow_on(void) +{ + gpio_port->set[0] = 1 << YELLOW_LED; +} + +void yellow_off(void) +{ + gpio_port->clr[0] = 1 << YELLOW_LED; +} diff --git a/arch/cpu/.gitignore b/arch/cpu/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/arch/cpu/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/arch/cpu/entry.S b/arch/cpu/entry.S new file mode 100644 index 0000000..af48a31 --- /dev/null +++ b/arch/cpu/entry.S @@ -0,0 +1,70 @@ + +.section .init + +.global _start +_start: + /* Erkennen ob Hypervisor Mode aktiv */ + mrs r0, cpsr + and r0, r0, #0x1F + mov r1, #0x1A //Modebits = 0x1A = Hypervisor Mode + cmp r0, r1 //Vergleichen und Statusbits setzen für bedingte Sprünge + + /* Wenn Hypemode erkannt: abschalten (sprung nach _exitHyper) + * wenn nicht erkannt: dann weitermachen (weiterlaufen nach _bsprak) + */ + beq _exitHyper + + /* Qemu startet immer alle 4 Kerne + * Wir lassen alle anderen Kerne endless loopen + */ +_checkCores: + /* Id des Cpu Cores Abfragen */ + mrc p15, 0, r0, c0, c0, 5 + /* Falls nicht core 0 Core disablen */ + tst r0, #3 + bne _parkCore + +/* not modeled in qemu 6.0 */ +_enableAlignCheck: + /* SCTLR des cp15 laden */ + mrc p15, 0, r0, c1, c0, 0 + /* A-bit (c1[1]) auf 1 setzen */ + orr r0, r0, #0x2 + /* neues SCTLR speichern */ + mcr p15, 0, r0, c1, c0, 0 + + +_bsprak: + /* Validen Stack Pointer setzten */ + ldr sp,=0x100000 + /* Zu c Code springen */ + bl start_kernel +.Lend: + WFI + b .Lend + +_parkCore: + /* Interrupts für Core 1 bis 3 ausschalten */ + cpsid if + /* In Endlosschleife springen */ + b .Lend + +_exitHyper: + + /* Rücksprungadresse ins Hypervisor Rücksprungregister schreiben. + * Dies ist das Label _bsprak für die normale Programmausfürung. + */ + ldr lr, =_checkCores + msr ELR_hyp, lr + + /* Der Hypervisor mode ist auch "nur" eine etwas kompliziertere Exception. + * Also springen wir aus dieser zurück. + * (Rücksprunge in Aufgabe 2 sind anders zu lösen!). + * Dazu zunächst das CPSR auslesen und die Modebits zum Supervisor ändern. + * Danach dies in das SPSR für den Rücksprung schreiben. + */ + mrs r0, cpsr + bic r0, r0, #0x1F + orr r0, r0, #0x13 //Supervisormode + msr spsr_hyp, r0 + eret diff --git a/include/arch/bsp/.gitignore b/include/arch/bsp/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/include/arch/bsp/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/include/arch/bsp/yellow_led.h b/include/arch/bsp/yellow_led.h new file mode 100644 index 0000000..29fc7d0 --- /dev/null +++ b/include/arch/bsp/yellow_led.h @@ -0,0 +1,8 @@ +#ifndef YELLOW_LED_H +#define YELLOW_LED_H + +void init_yellow(void); +void yellow_on(void); +void yellow_off(void); + +#endif diff --git a/include/arch/cpu/.gitignore b/include/arch/cpu/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/include/arch/cpu/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..d30562f --- /dev/null +++ b/include/config.h @@ -0,0 +1,48 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/* Falls auf den Boards gearbeitet wird am besten + * die nächste Zeile auskommentieren + */ +#define BUILD_FOR_QEMU + +#ifndef __ASSEMBLER__ + +/** + * \file config.h + * + * Enthält defines und static Funktionen zum testen der + * Implementierung. Wir tauschen diese Werte/Funktionen beim + * Korrigieren zum Testen aus. Bitte fügt hier KEINE weiteren + * defines oÄ ein. Ihr könnt diese Werte zum Testen natürlich + * auch gerne selbst verändern. + */ + +void test_kernel [[gnu::weak]] (void); +void test_user [[gnu::weak]] (void *args); +void test_user_main [[gnu::weak]] (void); + +/** + * Erst ab Aufgabenblatt 2 relevant + */ + +#ifdef BUILD_FOR_QEMU +/* Werte zum testen unter QEMU */ +static constexpr unsigned int BUSY_WAIT_COUNTER = 10000000; +#else +/* Werte zum testen auf der Hardware */ +static constexpr unsigned int BUSY_WAIT_COUNTER = 30000; +#endif // BUILD_FOR_QEMU + +static constexpr unsigned int PRINT_COUNT = 5; + +// Wir testen nur mit Werten die durch 2^n darstellbar sind +static constexpr unsigned int UART_INPUT_BUFFER_SIZE = 128; + +// Timer Interrupt Interval zum testen in Mikrosekunden +// Systimer taktet mit 1MHz +// 1000000 -> 1 Sekunde +static constexpr unsigned int TIMER_INTERVAL = 1000000; + +#endif // __ASSEMBLER__ +#endif // CONFIG_H_ diff --git a/include/kernel/.gitignore b/include/kernel/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/include/kernel/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/include/lib/.gitignore b/include/lib/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/include/lib/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/include/lib/list.h b/include/lib/list.h new file mode 100644 index 0000000..f6a76d0 --- /dev/null +++ b/include/lib/list.h @@ -0,0 +1,110 @@ +#ifndef LIB_LIST_H_ +#define LIB_LIST_H_ +/* + * \file list.h + * \brief Doppelt verkettete List Datenstruktur + * + * Diese Datei ist eine Hilfestellung für Aufgabe 3. + * Sie erspart euch den Aufwand, selber eine doppelt verkettete Liste zu implementieren. + */ + +// List Node struct +typedef struct list_node { + struct list_node *next; + struct list_node *prev; +} list_node; + +// Makro zum initialisieren einer Liste +#define list_create(N) \ + static list_node head__##N = { &(head__##N), &(head__##N) }; \ + static list_node *N = &(head__##N) + +//checks if list is empty +[[nodiscard]] static inline bool list_is_empty(list_node *head) +{ + return head == head->next; +} + +// Gibt das erste Elemnt der Liste zurück +[[nodiscard, maybe_unused]] static inline list_node *list_get_first(list_node *head) +{ + if (list_is_empty(head)) { + return nullptr; + } + return head->next; +} + +// Gibt das letzte Element der Liste zurück +[[nodiscard, maybe_unused]] static inline list_node *list_get_last(list_node *head) +{ + if (list_is_empty(head)) { + return nullptr; + } + return head->prev; +} + +// Füge Elemente hinzu (Hilfsfunktion) +static inline void list_add_(list_node *node, list_node *prev) +{ + node->prev = prev; + node->next = prev->next; + + prev->next->prev = node; + prev->next = node; +} + +// Füge Element am Anfang der Liste hinzu +[[maybe_unused]] static inline void list_add_first(list_node *head, list_node *new) +{ + list_add_(new, head); +} + +// Füge Element am Ende der Liste hinzu +[[maybe_unused]] static inline void list_add_last(list_node *head, list_node *new) +{ + list_add_(new, head->prev); +} + +// Entferne Element (Hilfsfunktion) +static inline void list_remove_(list_node *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +// Entfernt das erste Element aus der Liste und gibt es zurück +[[maybe_unused]] static inline list_node *list_remove_first(list_node *head) +{ + if (list_is_empty(head)) { + return nullptr; + } + list_node *del = head->next; + list_remove_(del); + return del; +} + +// Entferne das letzte Element aus der Liste und gibt es zurück +[[maybe_unused]] static inline list_node *list_remove_last(list_node *head) +{ + if (list_is_empty(head)) { + return nullptr; + } + list_node *del = head->prev; + list_remove_(del); + return del; +} + +// Entfernt das spezielle Element aus der Liste +[[maybe_unused]] static inline list_node *list_remove(list_node *head, list_node *rem) +{ + list_node *curr; + for (curr = head->next; curr != head; curr = curr->next) { + if (curr == rem) { + list_remove_(curr); + return curr; + } + } + return nullptr; +} + +#endif // LIB_LIST_H_ diff --git a/include/lib/ringbuffer.h b/include/lib/ringbuffer.h new file mode 100644 index 0000000..79ea6a9 --- /dev/null +++ b/include/lib/ringbuffer.h @@ -0,0 +1,89 @@ +#ifndef RINGBUFFER_H_ +#define RINGBUFFER_H_ + +/** + * \brief Ringbuffer Datenstruktur + * \file ringbuffer.h + * + * Diese Datei ist eine Hilfestellung für Aufgabe 2. + * Sie erspart euch den Aufwand, selber einen Ringbuffer zu implementieren. + */ + +struct ring_buff { + unsigned int head; + unsigned int tail; + unsigned int size; + unsigned int mask; + char *buffer; +}; + +/** + * \brief Überprüft ob eine positive Zahl eine Zweierpotenz ist + * + * Beispiel: (0b100 & 0b011) == 0b000. + */ +#define is_power_of_two(val) (((val) & ((val) - 1)) == 0) + +/** + * \brief Macro um einen Ringbuffer zu erstellen + * \param name Bezeichnung des Buffers + * \param size Größe des Buffers + * + */ +#define create_ringbuffer(name, size) \ + static_assert((size) >= 1, "Size of Ringbuffer has to be at least 1"); \ + static_assert(is_power_of_two(size), "Size of Ringbuffer has to be a power of 2"); \ + static char _b_##name[size]; \ + static struct ring_buff _##name = { 0, 0, (size), (size) - 1, _b_##name }; \ + static volatile struct ring_buff *name = &_##name + +[[nodiscard]] static bool buff_is_empty(volatile struct ring_buff *b) +{ + return b->head == b->tail; +} + +[[nodiscard]] static bool buff_is_full(volatile struct ring_buff *b) +{ + return ((b->tail & b->mask) == (b->head & b->mask)) && !buff_is_empty(b); +} + +static bool buff_putc(volatile struct ring_buff *b, char c) +{ + if (buff_is_full(b)) { + return true; + } + + b->buffer[b->head & b->mask] = c; + b->head++; + + return false; +} + +static char buff_getc(volatile struct ring_buff *b) +{ + while (buff_is_empty(b)) { + } + + char c = b->buffer[b->tail & b->mask]; + b->tail++; + + return c; +} + +[[nodiscard, maybe_unused]] static char buff_peekc(volatile struct ring_buff *b) +{ + while (buff_is_empty(b)) { + } + + return b->buffer[b->tail & b->mask]; +} + +[[nodiscard, maybe_unused]] static char buff_peekc_last(volatile struct ring_buff *b) +{ + while (buff_is_empty(b)) { + } + + return b->buffer[(b->head - 1) & b->mask]; +} + +#endif diff --git a/include/user/main.h b/include/user/main.h new file mode 100644 index 0000000..6d2d7f4 --- /dev/null +++ b/include/user/main.h @@ -0,0 +1,5 @@ +#ifndef MAIN_H +#define MAIN_H + +void main(void *args); +#endif diff --git a/kernel.lds b/kernel.lds new file mode 100644 index 0000000..4434fff --- /dev/null +++ b/kernel.lds @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x00008000; + .init : { *(.init) } + .text : { *(.text) } + .rodata : { *(.rodata) } + . = ALIGN(1<<20); + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/kernel/.gitignore b/kernel/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/kernel/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/kernel/start.c b/kernel/start.c new file mode 100644 index 0000000..81ae145 --- /dev/null +++ b/kernel/start.c @@ -0,0 +1,20 @@ +#include + +static volatile unsigned int counter = 0; + +static void increment_counter(void) +{ + counter++; +} + +void start_kernel [[noreturn]] (void); +void start_kernel [[noreturn]] (void) +{ + init_yellow(); + yellow_on(); + + // Endless counter + while (true) { + increment_counter(); + } +} diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/lib/mem.c b/lib/mem.c new file mode 100644 index 0000000..1efcad1 --- /dev/null +++ b/lib/mem.c @@ -0,0 +1,59 @@ +#include + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *str1 = s1; + const unsigned char *str2 = s2; + int res = 0; + while (n > 0 && res == 0) { + res = *str1 - *str2; + str1++; + str2++; + n--; + } + return res; +} + +void *memcpy(void *restrict s1, const void *restrict s2, size_t n) +{ + char *restrict str1 = s1; + const char *restrict str2 = s2; + while (n > 0) { + *str1 = *str2; + str1++; + str2++; + n--; + } + return s1; +} + +void *memmove(void *s1, const void *s2, size_t n) +{ + char *str1 = s1; + const char *str2 = s2; + if (str1 < str2) { + while (n > 0) { + *str1 = *str2; + str1++; + str2++; + n--; + } + } else { + while (n > 0) { + n--; + *(str1 + n) = *(str2 + n); + } + } + return s1; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *str = s; + while (n > 0) { + *str = (unsigned char)c; + str++; + n--; + } + return s; +} diff --git a/lib/ubsan.c b/lib/ubsan.c new file mode 100644 index 0000000..a9ad199 --- /dev/null +++ b/lib/ubsan.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include + +#define halt() \ + do { \ + asm("cpsid if"); \ + while (true) { \ + asm("wfi"); \ + } \ + } while (0) + +void kprintf [[gnu::format(printf, 1, 2)]] (const char *format, ...); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" +void kprintf [[gnu::weak, gnu::format(printf, 1, 2)]] (const char *, ...) +{ + init_yellow(); + while (true) { + for (volatile unsigned int i = 0; i < BUSY_WAIT_COUNTER; i++) { + } + yellow_on(); + for (volatile unsigned int i = 0; i < BUSY_WAIT_COUNTER; i++) { + } + yellow_off(); + } +} +#pragma GCC diagnostic pop + +// structs copyed form the linux kernel +// https://github.com/torvalds/linux/blob/master/lib/ubsan.h + +struct source_location { + const char *file_name; + const uint32_t line; + const uint32_t column; +}; + +struct type_descriptor { + const uint16_t type_kind; + const uint16_t type_info; + const char type_name[1]; + const char unused[3]; +}; + +struct type_mismatch_data_v1 { + const struct source_location location; + const struct type_descriptor *type; + const unsigned char log_alignment; + const unsigned char type_check_kind; + const char unused[2]; +}; + +struct out_of_bounds_data { + struct source_location loc; + struct type_descriptor *array_type; + struct type_descriptor *index_type; +}; + +static_assert(sizeof(unsigned int) == sizeof(long)); + +static void print_source_location(const struct source_location *location) +{ + kprintf("%s:%u:%u", location->file_name, (unsigned int)location->line, + (unsigned int)location->column); +} + +#define DEF_UBSAN_HANDLER(name) \ + void __ubsan_handle_##name [[noreturn]] (const struct source_location *location); \ + void __ubsan_handle_##name [[noreturn]] (const struct source_location *location) \ + { \ + kprintf("Undefined behavior detected: " #name " at "); \ + print_source_location(location); \ + kprintf("\n"); \ + halt(); \ + } + +DEF_UBSAN_HANDLER(add_overflow) +DEF_UBSAN_HANDLER(add_overflow_abort) +DEF_UBSAN_HANDLER(alignment_assumption) +DEF_UBSAN_HANDLER(builtin_unreachable) +DEF_UBSAN_HANDLER(alignment_assumption_abort) +DEF_UBSAN_HANDLER(cfi_check_fail) +DEF_UBSAN_HANDLER(cfi_bad_type) +DEF_UBSAN_HANDLER(divrem_overflow) +DEF_UBSAN_HANDLER(cfi_check_fail_abort) +DEF_UBSAN_HANDLER(dynamic_type_cache_miss) +DEF_UBSAN_HANDLER(divrem_overflow_abort) +DEF_UBSAN_HANDLER(float_cast_overflow) +DEF_UBSAN_HANDLER(dynamic_type_cache_miss_abort) +DEF_UBSAN_HANDLER(function_type_mismatch_v1) +DEF_UBSAN_HANDLER(float_cast_overflow_abort) +DEF_UBSAN_HANDLER(implicit_conversion) +DEF_UBSAN_HANDLER(function_type_mismatch_v1_abort) +DEF_UBSAN_HANDLER(invalid_builtin) +DEF_UBSAN_HANDLER(implicit_conversion_abort) +DEF_UBSAN_HANDLER(invalid_objc_cast) +DEF_UBSAN_HANDLER(invalid_builtin_abort) +DEF_UBSAN_HANDLER(load_invalid_value) +DEF_UBSAN_HANDLER(invalid_objc_cast_abort) +DEF_UBSAN_HANDLER(missing_return) +DEF_UBSAN_HANDLER(load_invalid_value_abort) +DEF_UBSAN_HANDLER(mul_overflow_abort) +DEF_UBSAN_HANDLER(mul_overflow) +DEF_UBSAN_HANDLER(negate_overflow_abort) +DEF_UBSAN_HANDLER(negate_overflow) +DEF_UBSAN_HANDLER(nonnull_arg_abort) +DEF_UBSAN_HANDLER(nonnull_arg) +DEF_UBSAN_HANDLER(nonnull_return_v1_abort) +DEF_UBSAN_HANDLER(nonnull_return_v1) +DEF_UBSAN_HANDLER(nullability_arg_abort) +DEF_UBSAN_HANDLER(nullability_arg) +DEF_UBSAN_HANDLER(nullability_return_v1_abort) +DEF_UBSAN_HANDLER(nullability_return_v1) +DEF_UBSAN_HANDLER(out_of_bounds_abort) +DEF_UBSAN_HANDLER(pointer_overflow_abort) +DEF_UBSAN_HANDLER(pointer_overflow) +DEF_UBSAN_HANDLER(shift_out_of_bounds_abort) +DEF_UBSAN_HANDLER(shift_out_of_bounds) +DEF_UBSAN_HANDLER(sub_overflow_abort) +DEF_UBSAN_HANDLER(sub_overflow) +DEF_UBSAN_HANDLER(type_mismatch_v1_abort) +DEF_UBSAN_HANDLER(vla_bound_not_positive_abort) +DEF_UBSAN_HANDLER(vla_bound_not_positive) + +void __ubsan_handle_out_of_bounds + [[noreturn]] (struct out_of_bounds_data *data, unsigned long index); +void __ubsan_handle_out_of_bounds + [[noreturn]] (struct out_of_bounds_data *data, unsigned long index) +{ + kprintf("Undefined behavior detected: out of bounds access index %u at ", + (unsigned int)index); + print_source_location(&data->loc); + kprintf("\n"); + halt(); +} + +void __ubsan_handle_type_mismatch_v1 [[noreturn]] (struct type_mismatch_data_v1 *data_v1, int ptr); +void __ubsan_handle_type_mismatch_v1 [[noreturn]] (struct type_mismatch_data_v1 *data_v1, int ptr) +{ + if (ptr == (uint32_t)NULL) { + kprintf("Undefined behavior detected: null pointer access at "); + } else if (data_v1->log_alignment && (ptr & (data_v1->log_alignment - 1))) { + kprintf("Undefined behavior detected: unaligned access at "); + } else { + kprintf("Undefined behavior detected: type mismatch at "); + } + + print_source_location(&data_v1->location); + kprintf("\n"); + halt(); +} diff --git a/user/.gitignore b/user/.gitignore new file mode 100644 index 0000000..17db624 --- /dev/null +++ b/user/.gitignore @@ -0,0 +1 @@ +# empty gitignore diff --git a/user/main.c b/user/main.c new file mode 100644 index 0000000..daabfef --- /dev/null +++ b/user/main.c @@ -0,0 +1,6 @@ +#include +#include +// This is just a placeholder +void main(void *) +{ +}